{"_id":"@atomist/sdm","_rev":"2317-71b84b3bc8ec1c872c63bf6ccd16183d","name":"@atomist/sdm","dist-tags":{"latest":"2.1.6","next":"2.1.7-main.7","branch-1.9-fixes":"1.9.2-1.9-fixes.20210210164117","branch-k8s-sync-repo-creds-828":"2.0.0-k8s-sync-repo-creds-828.20200324204549","branch-k8s-sync-repo-creds-worker-828":"2.0.0-k8s-sync-repo-creds-worker-828.20200325010940","branch-main":"2.1.7-main.7","branch-update-params-1.9":"1.9.1-update-params-1.9.20200911133602","1.9-fixes":"1.9.1","branch-atomist-npm-update-main":"2.1.6-branch-atomist-npm-update-main.0","branch-event-signing":"2.2.0-branch-event-signing.0","branch-atomist-npm-audit-main":"2.1.7-branch-atomist-npm-audit-main.2"},"versions":{"0.0.1-no-env-vars.20180508005131":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180508005131","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a07032895c6045fed52cdecda7955f56656ed063","_id":"@atomist/sdm@0.0.1-no-env-vars.20180508005131","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FFQJJ3WJaq9/X4wV67a01FvQFFR4zOUZY8DafKbn6QTn/f5I+SCv6XgyyvebfAlBov31psi/WQJQE1Jies8vcg==","shasum":"06c75843508aa7d794566ac2de8f8197d14d3bcd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180508005131.tgz","fileCount":768,"unpackedSize":1774317,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8PZtCRA9TVsSAnZWagAAUxkP/1L/FcvrSl/KSysmNK+N\nNJQusBoo156iY7CeL6dBuApwEbgCuRcDYrLqkXGQseTRDEgB/jfRPHH7bXW7\nYq+g8LyLejZ6BHTkw8uhm+zUn8iBSDFZrfUCnY6IVGRtm/JaKt/oE/N8+QO+\n1mMsBRvUhyuTnSqZWXWuUs4YYxjdLMoojL3vwB/QQnTP+s+UDspqpv41bo43\nzqcPBKRceFAHbRd0vWEW237M/1mja6aMtxfAzlu18p9VEvfUvD+LD97iFXlu\nH5ueaOjJgW1rnpAs+mc+mltnstAZququidwPL2onlUZ2WM0vgkExt8z3B/gJ\nHEWA1MjqyCEAXhm7mF0OyeIGS/bvEv2aSbOW1ddNs2hmmGPgspeCCPXKeTim\nNRYDMPy8oElmWc2XNSHv/CNEKgVViLymlYP2+YG4DI4XhFc0FH0qjP4LNHZY\nVR3gnCXCVWyPvYekF9K0Six+yQ9ZPzJ9WGZHlhoUSjGIBpusMSCKxe/Pc+Un\nmaWaDt1gzp1M+1rKVATBFdK1vN5zneo+eXHDEYArYt7+cgPsgTHJwp/77abg\nN+9INcg2950eeam7PWqzhRz0uiPI7Q97vkCsA/w4DUEiNDAs/GajbKZDS6pd\nmUUOQqXNj6WBA14x9UGLErqRlzXZR36SMHbPY1z5/XegLdQFUBojogZkj0Mw\n2jhK\r\n=R69y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICCgcxig6f+b6vh9Xr0kLzyBgxr/AVu1F1ZJoFLjbOMMAiAq8XFQXyFHQP5XC+O2czkO/PPnAvw8E2Mf01ZjoMeUDA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180508005131_1525741162812_0.0990869950226283"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180508011302":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180508011302","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"312cad64b92aec7f2a58db7048b4ce54147c0b92","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180508011302","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/VIG/Mnl11CD+hXkl8tJ3AovM6AuK0yGZtk5qgzD7QV1VTMp65uXRYA9GIrZELmNH9EA+/XnrXM5ePbTGXxIng==","shasum":"41a22fe537aab6fffb7104fc22d080e916d067ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180508011302.tgz","fileCount":772,"unpackedSize":1782009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8PswCRA9TVsSAnZWagAArmAP/3uaTDaaZlc17T2UamC4\nNXbm7Oz5aMviuGAjVd50+l+TBli64DRBfJ66aE7Ih5KHGRHUF+D0cPQiApZ7\naIZg7URwZ/lO7C71LHUdK7RHt7A2jT+2JiMqskGyfWVGS53FTWyviu3c14GT\nFhGCamNVtcMOTUTfG8BXjTkeEYtvLLO16AXEtCU4oiH0v8Dcq+6jVfLgCuxn\n3RyScXgaihwUkS+z1W3kBYqk+giZswG7hu7DJrH4d7M4Hb9mdz1Ehy5g+6kR\nJZfNwXEzUgepOhzQVtdfkGOdUcz+X3Uaz6SUpPIyHlD1Em1t4risKcpn8To5\nXC8rOSG+mzWo2unqY2uhfpvOOXToN4+COFLdlHJwvnPN8T+dz/kIebRQE3y8\n+G1TeMt1BdOk99FMYqWZYPjWlIqk7SF7rIj3b6UzAJYwEMEDuwBRwFeyuCpI\nObK7/yfgJWjdoEOne/ZT43h5/+xvFogCB6JoNvjWKJbBqBfwg7h5tX2qurTJ\nkd0wimyqLzmfFKPrS732UL8c4WXLTS3styBOmXeBCbcyGRH+VZxfthopMKbR\nh8TXMW32cj10OklnZDSJNGCOwbb2BLd/x/+e+X/1HcYSIpS5Iy0Rc9upK6bI\nGJpr65RLfVd01cgql6yYBN25QJTwi4vv8DVBicfykq5Dc5Dc2gtmRU03NqSf\nFyeJ\r\n=Newh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHUY4D1yDKazVQFziccVUfPvt2wWCjyyb5AtqM1xj8BWAiAHLCM1u5XCWisRY44AIe9/lZjjwuFI8341jMwLqV4vHw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180508011302_1525742382941_0.1641377655587024"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180508041617":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180508041617","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c5e5a9f5dc3d01c6ab1510587bcb818a13e1c046","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180508041617","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-iyKhzdkFaXcWUgiUrY46ulI2EVuMQiwjOS8A9ZJIT4tqwc85mJwrzRRNMl3OZB3AB3m2S1x1py2YLEBwsjxP9g==","shasum":"de2018ef6efc085b7bc7f67e2303eaf612a1c62e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180508041617.tgz","fileCount":772,"unpackedSize":1777458,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8SY/CRA9TVsSAnZWagAAhgEQAI0aGEcm2aDgB9fJVbFj\n7Pifjnkbae0zG6iP5jKuB0v9vViOAxzot4KRaE1yInsOp8vQg2qCROT79Pcb\nbFBaGBypuSeoq1pyOw2aO608B0eIDog4FpooKQT0QadsAXNViylWpy49qHCv\nwWqNnNNsm7KplQK1Ql7y9olH+aScsC4WVi0erQ70SCLIx7EzpgumYPyK+3DF\nGqAXG+veyaYxCUARZnhsKA+tGDHGNY/FDBGv/Zu986fWkYegtTimMBLIXxvB\nahWQBkbvoSS6Vs0eTAwsY6VijPUpu0B3mBxumicbhSKEmntWf5Y46s8GieRN\nuwzwfDpJIMDtA4/kz4yH0tUL1YVCVCDCD3GxueQkgk8OzMR2ClaAj5bIwTVJ\nRxLmPPc/RzqY77esQLERf66TI8HqppCrGs7g9jiFaHOlnFG7Xy5hVVgEoNJ+\nuXecdBZChqbxOBt9AzG2w0XJO2iqtfcQCOq6PeqEKvjSA1Y9P7xn+fXvQlVs\nnUt5KjcWwvHumT55Kpne7qCMFQAqhg69wS9Zn6YgUo9DEQaDvRwA1A75D+6g\nH1u7Ctuxv9CXviESJ91QjVm4T5/IujwVuvTqAnTzOgEbDZcg7bkL7mOBVrOe\nSDtattMC8rR54sREYhV3tuKYiIo8yooyQlOUpaQD5TdHaah3pIMa1PMOz+oq\nQEyC\r\n=n2CU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH93B73aJYwc2qBMMc/iN88fkQO8/L/FrrHBN7Q/ScHGAiEAiCMne4BrDoyHd+sdkVmhrM3XShcOkPwoVZYIdrDxA4o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180508041617_1525753405589_0.46379198866352356"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180508044906":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180508044906","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6865a3e6405ac3309134c2989b1085c9bc8706ee","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180508044906","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+swWVCOQS6TRcZTcbh2aoohaMzGSoPaLi2M42D/TsctZX/2+bLEtri4W6mPXbX0J8S3a9Cd/5Hqdzxl0lUXk5A==","shasum":"3c08dd9525d5ea1a89bfd2c3543754de1bf8f583","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180508044906.tgz","fileCount":772,"unpackedSize":1778445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8S31CRA9TVsSAnZWagAAlt0P/2oT0MZBrEoiKh2//YRe\nFaUe6GFaQb29xxyaHXXZV95P0H4RgXUpBgaI+WP4CBF6OBK7McpxhYSavpqb\nOlsVEWSB6PjQc/EmW1LWWJbzZ2Ym9SIfDexSA2qjpZ3Rw4rhPv11JZzECQ8N\nOwxtBB5K08/YtI33cZ2zdZo1IiMlMngAUHJuJ8l4ygQs0rEfwTAQabppweaz\n8ZBogzPOd1gfmQHcrBl+a9lbfABp8SzahVGMc0Jy7JlroS+5d+X+Gpbilfge\nD3p666PiZUYFTHD5H4MdT8YSi6tT5dPlMwvutWx0sTbf6/tzhQsHJO5b9GBz\nyHsNTMUXTeEo/gfFHYj5aiX/JY4xA8X8RxM2ZUMBBM0piv2sQnP7l81V177x\nfmNmVBKo25NcLyc63/9saw+th9mGWquqiRwwBKfBlDvMaX00iiW+cdg8C1pE\nBsZYu0HytuRGk1TC7fugLJDh8LRxXV1kZIN14u5dDqdh8j3BXrXUbraFGnet\nCr0Yv8qm+oo6CaJJEeUweFIQ2CMBWV0EWy3Qx2atOO8pMXKu614H2RI1G2g+\nmmK/HxtGFJ15terbnVz03igN9KCwgExdeeQWlwPTbKn7KuyiwwrtPaKVx025\nLlYyAiHAOPd1pxP2AzR4JC0hgTsqb7Zzvxx5lkHtUBor0I73aCYAI3Rw2/Lq\nW6af\r\n=IvEq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4i7jO2tdoGSi2rnHPjBt+qNsiNV+C5DMN668h7m37SwIhAKzaseEla890Vu0t/wB/l8uqRULY4Vqc7Otdd03Xiy91"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180508044906_1525755378927_0.59779256087704"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180508054553":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180508054553","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"87fd6bff24b67bc1626ca2cdb5a19be9f1894153","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180508054553","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cZaL0YsfCg7yKEscgLOIOfqYjA77M4Zp2Pv8Kx21X3DB1kBi+xfnmui/1wI42oFV1SPHsscUzYGj1P3KvyQsKw==","shasum":"82ddad54149a241f2b1586caf867468027f7fbe6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180508054553.tgz","fileCount":772,"unpackedSize":1778795,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8TssCRA9TVsSAnZWagAAO9wP/0QHnMlRwq1pqKFRI8u9\nzXbtYf2dQGhyGvRltI0o21Y6Qhp32Th7AhFjvgHVvuEY2QN19NuBM0RpXCK3\nuzI014SXgjDm3zolI4Pk9mrClxg3ShNUDZLB9OVDD/C+NNxGF28N0MvT5tCt\nbK4KoW4ezsdC1thTk0SbJes+hdlJJJ2WY0R/epBuKSg9QbuHp+HoJgeGuExZ\n+6Jve53dViOLjVuivoNB9RdaHLjzoGgNFF0ifLUEkP8pfKqrvZDGQsU3O9Az\n5eOvjUPD1jrnXbBEolpfjWHFGQbNUTHZTbKe++pW0WTzdzYFhPjZTFXGm7MV\nqxMWASSSY/EvRsOBDh2dJmrcLAh9BBitzn44JlKtqFDkZmEbpLvTSnW4Qe/Q\nBAWgeR2wOAZfeyuxL6SZKOFakOJY5qpD0vw+0/aaJTUEntLJYhxR2xOwaqA7\nSmDhp8TpdjimPuhNgVojfgVKnKHBvzzRqSLhflCXq/14jzj7/NkuQb6ZTVM6\nhWzUD30QZK5xI5hZN8QIedlDtlVHjCdeA/jD4YJnwhdLgnW2CBjGtQOOHmAv\noU/xm1U8JwHrLB1/Ph2rEnF5OK4b8C79gBU6x6QE8oQUcPQ5661KIn/Fl11C\n5K3IqndCq6IZEKslp+/K09W/TkcerFwsoNzADwchxBVfF++k3t+sQ3B7YAs5\nOXVp\r\n=Z0Ir\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG0iyRrmrdgF2dXuGi2BABbfrOZhNHHGoe1Yyam1p2w3AiEAh7CcxkGtLJSybbUSmxfgNDyzT+nLDtJGKJB7g7O3agw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180508054553_1525758762020_0.4835020750277601"},"_hasShrinkwrap":false},"0.0.1-20180508202723":{"name":"@atomist/sdm","version":"0.0.1-20180508202723","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0030545b61e4cbdf2b14e7eb1fc1da962628bef5","_id":"@atomist/sdm@0.0.1-20180508202723","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XNnRuv1DKenl13r6zBlnDE/jl6oQmSOxZZ00mtJHT5G2N4k7ebkYwg3eF10K6aldAYpBYdlY8tiU9newgXoFpQ==","shasum":"e67a9d8577b0db92bea7576ca12849e1aa4115ac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180508202723.tgz","fileCount":768,"unpackedSize":1774358,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8gnhCRA9TVsSAnZWagAA7c4P/RUbhGiAMc7joL/289s6\nB0FtvNm55j8R40OQsH1eLd0KOsYdFVJYa6/rzX0FkUJX6xR9a0ythT+2UWOj\n/g8+nA5m+TgC2S4Eup2QNWq+RPjNwlvG0z1Ur/AKcM1hNbGzL9zxRSxcjg7d\nEwk27WSaZrAjlrJN5rtuCJ6KT/H3SM1G1iAZ6/JnrTojcASYb1cvxo5512Jl\nboFQKYCTAcxX31t83GK0iNNGmchcvS+gl4H1ghYIEQCJxY3KZtyNyHnjZQ2R\nbpInOnyLCSMe/xYjPy+mdhSN2WGWpttSJwEMxLeHJhA6DH5o4VHP4OBCGJ0I\nTI+wG9HDkyUyWNnfu/E99METoZM4aytOSn0AMQEvHX58mKzkIx0y4SpdOyAA\nGBWSDTuW9I2W3dBcpunnDn78QhCtw+oJ22qqLcLoYE/alFa666wMqgx1EVLK\nIpg/v7+ifiXDaMI9hrD+hPFw+NrEwkEIT2WGIu4MMswD+FTfL+tNtv8q8bOz\nPAFUvECXsxdixILxObyKpzKdAzIg08RpaktTumEylWrGI3Vnq4t9+4mFOD7M\nmYLQ7XZXva+C9qGDvoGBXVyYsvRlLS/7xlhciudrYUknp0jSDA8eK72X3WXd\nv5K8/jot6AFl3IbWaJgnZ3iwu+Zw1PQy5R5cNNbWXzUf6+thGmedUM6j2DDu\nVy9F\r\n=37vU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCislwF/T0GIWmzEEb0jn1xhD3y33qqLh1jkwoY6J2P1gIgDW1V5Nb3XzMCmKI6ITLV4e6YJRiOsxvsrG2jC7cj2fA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180508202723_1525811679900_0.427418828660862"},"_hasShrinkwrap":false},"0.0.1-20180508211058":{"name":"@atomist/sdm","version":"0.0.1-20180508211058","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cd4097d873419c44e17b7cb542cf9dfaa8130c25","_id":"@atomist/sdm@0.0.1-20180508211058","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T0+cMEYq0RNKiDfWWvgPz3rCloaQqdgXYtfCoowwL/enk6vBFihWsMuvV9yRkCmxPT1RQNpXVDxIkww6L9Nixg==","shasum":"075fbb25381cc16721372a03dff795cbe6ed2bb8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180508211058.tgz","fileCount":768,"unpackedSize":1774506,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8hQcCRA9TVsSAnZWagAAJVgQAImHRSgCoJoU8glyPhNv\n4h+5osO+GekW/M6LN4fxRzVV7Jo5tb2VV1NnXLLR/GT46D5uWjbBqRzVT9IS\nGAJnrxga+HyRbacTRNV7g36TpuCkh9cviE+A3vlHSWX00K9aCJHQOFBUu9+a\ntXnsEY1sHBNJy6GGBQqf4lJuf/00nizAhyXApIXRWELWeFBTbrtJjv/+jeC7\nEHwOgS1m2+enOwcKvfYZ4pPBGLYT0UqCd0bIiNtXBWzrZP8FWzUkRk8LuXaY\nGfwkFPE9Wq2z/umjL2fD5Oq6hBIOygAt4+1r4m6m0Lh38w9tWZskw2vvBTQr\niXkhyAx0Gl/njVQb5czD45jTL31et6v7dH0XidRF+a3p78UjChAz71uCjnlm\nJfhH3ilY2lwLvkvjXj2kiUmC56EcDWXqPZHcjCu39IS9gILSIrGul1bCCpnR\nl1QddC0vKYcoWHr8/vHLSkr6saIdCEXgzzuKXtTRz4a3/WV81kI6jcN1AMY7\nQfvyyKV7Cs7PJTNTRQ4UlyOydMqLCXA+sXlzRgwC15ujjnAudvJIziymiHRn\nKs3ANZQoSdmlX/YB8n99InygVQUUtJ+5KM2qWDzIbb8p1lJIYIzi7u1DOSDY\nD6eCHAZorrO5CMnClnSqIodxlkJEASri/B9A9fDoT0i5OeRzZDdktZx3V3Xr\ny8/k\r\n=DLub\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDByqduhkIzOaT9f6XrMiPxp65pyqT1d3b725NYEY7eFAiEAogfxb3uTG9PV3qPfpkMHmvSvxJyTg8TVqqJQ29uxC4I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180508211058_1525814297530_0.38895574560821156"},"_hasShrinkwrap":false},"0.0.1-20180508213550":{"name":"@atomist/sdm","version":"0.0.1-20180508213550","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a07d4127087e85c231140ca29cd6357632315719","_id":"@atomist/sdm@0.0.1-20180508213550","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5mmizBl7nBNp1s7j1/eb0C4yyVGrjKPkFyv5AEV5k2Ne6PWPWwjwhYaW207xaooP1LiOe3N6IvuU//S3QVB/Jg==","shasum":"bc8fce0e4d3bbd219f4bc492ebb22e2a1893b1e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180508213550.tgz","fileCount":768,"unpackedSize":1775415,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8hn4CRA9TVsSAnZWagAAnXcP/24Lqu+irdJMV6Z3uq+0\nMteKP2yq9wVWCpU+CrFruuL5H6KuCeIa47da0TQYgUDEeM4JICV8fiINSY4a\n7VXkLWiC9XJ8Ywzin9E9JIayZQ4TvPDE79X0jx0y0HYr1Xrmh4ODV6OoUSz+\nk4Lt3AhyxiHm493ihFxbqI2cQyij58+F8tAovrb2oEqZi50eepo3RD0Pw8iq\nFeZlK/MJorDxrXj/9SqwvmJB2k5+4HqpwwYBzggJf/A4Z6oCueHaNv9G8Kv7\nClNZXgUjjNxRNcR6VHDRB8283aVNssY9D8r7308dSSsWywAXPazi9azSIL+0\nG9JtwlJQbQpZi1EKxuIWaK6xY1AG/LzCt9kk6X9TgwANwTUB8Kc6QuVCwhK9\nuOEO8lU//+2+s+bIYMJV8Sbcp9HjKMDotNQfwXztInp+AJAoQ5Iawp+hS+9M\nTLUbRQyi8zbydWSN8FO6MxKT5iJmTVkXXis9odFapelL9ENQToToGi+lrRdB\nSODhLZr2Dl91qzM6KRf0FiJnoIT3MkW9uWMbIoLFmzMATVSYciaBaUQZVWuz\nm8uP1aVdgs/PHKzC36elQPSOFaRdiWpEi1HKWhWfd8VJES3D6iwoGyunk8HK\nJRLV6CRQD5VrIJ2rUMuRzG76PIXhqberA2e2M0VHhbQNn8KIjRzMXmleF7IK\nt0Ki\r\n=0LB+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkNJds+BvPG28fq+SmfRkOf7hzD1qvmwmO0F3AYjkPuwIhALdzdujqTAxodZNgg0WjrUcmbXcycHoAr0xZQPvnKlAG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180508213550_1525815798115_0.5909530845969064"},"_hasShrinkwrap":false},"0.0.1-20180508231021":{"name":"@atomist/sdm","version":"0.0.1-20180508231021","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"aba21115dd563f50b62ddfcfc903b149bf505705","_id":"@atomist/sdm@0.0.1-20180508231021","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UD/eWyWAyey+P7VuDpQhWg+ylz+2wfqASiLnv5E1Nkhve00YhLzI3X3n7Zx8hZETwPK5PJHAUVWJrRwBLo2muw==","shasum":"cc7d8feef0f720e5beda144b96125c9c834f49bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180508231021.tgz","fileCount":774,"unpackedSize":1782184,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8jAfCRA9TVsSAnZWagAAE0MP/R5pOZrZoxLoH/OTXLgt\nQ2YK3hTd3SiaUnBY+A2Lie3OA+uwDTcBdJ6hXCGon+ZXtZtBJUPbQouibhqw\nCibnsN9Wcu/CGZj36dfEAZUHL+2JCLOwmCUbXZ06e5v5i9a9YmjF94XYJBfC\nHe19hfo5VCbIv1C71o+BuCjVSajowRYiP0l462D/mj+Wjgl7v+7uVfWwTs9H\n2ykYDOHvYl4Iy0rYEzZdppc7z6C2ewivOEd++6XNbKB2K0lCZSdFyeLzY87g\nSj9nHNQ0LUk/XhVImyphy7ucTOZCShz0Ii5Q/FFXqBZwaXK0Ig1OgN9uq5ok\nbm7bB+l1H3iLJGwHuaCl+Q+fy1AiCzOBTxMILaJSX2z6rFAUu9JBlYCkHbrb\nx1D6hBF9Vu2Ahvk8J9iq+FdUYIqY8Y8RK/KpGvkAqg6ObHwtrsV+MDHxngup\nyhDKyHnU2DhPz44voK0Ww5ocMnIkdcVJ35anr29qGZI6eqBvH9TrDWKGv7SU\nBaAxzBEY77LcyvJQphVFnQjjWsZ652ostobumsicK66KkkSVomrZs577vjOR\nl9tATb3XSk24zSPT0Ih6u620K9AyZTmV/MJZMScnhjmRn7u4tp8ak9cPEXBq\nfJ3HwGvqUBBB4ny4QRVKPh7HNsi5ty1PdB7kZ7oIeofvK7V2tV4OtSEOV0FI\nJxOh\r\n=fpzj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDR2KxwNQGmyVHe0buesBXLQEQ42m2vIn8nKZIEguBECwIgaAKp05Cj4ArdQWQzYDGUTgDgEEEtsNkwukxjk0C2G2g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180508231021_1525821469071_0.8725164851817548"},"_hasShrinkwrap":false},"0.0.1-20180509001540":{"name":"@atomist/sdm","version":"0.0.1-20180509001540","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"87a14228adadacff2e4e7e87d32e9edf4f6e6510","_id":"@atomist/sdm@0.0.1-20180509001540","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nUo9EJ1aAv9o00gD/niViyLGguEh4AZF9H6JIqBZniuF5xuuO86rRC+RqlKRFE1oCmvIYi9pCko0/1nYdkGxtA==","shasum":"d99c1ea7b8ce403b7504f4bdf6f3a055e46dc2ca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180509001540.tgz","fileCount":777,"unpackedSize":1784428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8j9oCRA9TVsSAnZWagAA4gEP/imbcTf/n785d9gJbwjm\n+neiMUxV3I/monWq+VocA4vJNCgAhcgTgI+teim4TN2/y1gsHBTPzbg2Wdg0\nfRmCNOi3wmhlvlmGOvvbIYvv1O/oOHJ/jx1q4NNmVmzAKiJJcgsZmmLbOLCs\nS7ghvT/OchNNMbABaaRKAX1Wb54gHLLXq7LwOWoPHKjXx02vXBwYb4KySwsg\n5Bp8BLr9bMIx06fQ7Cg4iBO89yNVnu3lWHfHYlN4qEGBpjTjqlOyeymrufsi\nVL2dPfGfiZeOfAJBGZvhHvUetfcVMx6/S6MPsLNEoKcRZcQFEj6IgVf/szcC\n7t0BC4HgrTO/EosZbxcDFGmUoPMPJSNWDaX+gpesq3r9NOxPwEHfc1QKQhjI\nI+wBioU9wDGsZlllXzrYGpyzlKE8WMYh4ShqGc8+6GNPiWTYa1BORF6F8oZa\npW5Brj9VW2sp5QcbNfUHYELO/677pEkYCWJOBTUEAnKmn9dt8DaNPCKgKQ+q\nmE+5kpgHmI//VH2afeUfHT+t+pS1VRGtGsd6VhRsPu4DPmsUvC6aNVbjOMxE\nFsDywOETPrpb7GblghlI9cHLqXIS3ublsDSrL7VL8bIq6p27VfOKYHujr6yK\nTSKNjSaFTngIdQYSfGjpOnXrMIpwhJzkGjpVlnKX8YVpL6NDIg9Ulb+a1Eja\nd1NJ\r\n=0UZ1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmAhuNlDvPRuskXEYtHVIg9qvgEeH/8Ikpj0wDeFjgCwIhAMdPyrSyfYyIJTWdZ9Oz3q7D1iSlppsh+m85jvlxE6Yo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180509001540_1525825382203_0.3404955359172346"},"_hasShrinkwrap":false},"0.0.1-20180509010107":{"name":"@atomist/sdm","version":"0.0.1-20180509010107","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"718e8ad791035b8822f3c3b68d56c9dc7cfd8c6b","_id":"@atomist/sdm@0.0.1-20180509010107","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0dD8ch9oZISWMm0xxD/jbh9cskUzQKY94/l8Cf3CHsBNgIoNlMkrdCclZSBWKtGV0wd/Ev05HIlyQeyoLjgFSw==","shasum":"7d7b0e64cdfa0d049f697c501513aa0f75dd3e24","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180509010107.tgz","fileCount":777,"unpackedSize":1784994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8ko6CRA9TVsSAnZWagAANKcP/1wveDfrBaaVOBon4/2C\nsQFMnq7q6Qgjc6TByi0G9uMXcEWSbp8ucKIJf22tgU6q+yTQiKVf5BgMpqbX\n1+eYK+mr71BqzcspYsTGO6RiKgkB1OTuBigUEUPLIrKTJ+zHI80SWoUXGUp7\nvNHrX/fkfbWfBxm2Lrzl/YyD1gHccBTVarlGemrCNxmd7qe7YaUbroPHQv0I\n6TM9JRlC8ZAgKPh2C/Q7wpShV0ea7UqSuOQR5AyO81bGhz95Y49Sfm90JIcz\nlc4N8m4tftUJkuByniPqfUdH0SQYXT+mewRoG5KkJjEN7dZVB2mFUeXL2BXT\nC6lnjhxaIDhtgeF4foG6FOsuFnOMD+9zAZLDKGLbdPyT+Ti3nmFJ0QON/x/f\nxLyfRECQddcnhMmDmVr8BZ01iQFFARv3BG9p5w0MAfYf4a4/JOPKtF0tOWW5\nur0KZAWiJgt8RBjdcW2ttv/3NCYLFoklopAAZsCZ+loiEhr+3Rvy0imWV1gt\n52Pw1CDYQAH/1SB9f+S4rlB4R0b7U6TF7QnejMRIarGEBxd5tD9hylK8abbJ\ndu82R/HmoQUex3rsxgwR3lUqmLjK7vgbfoOAnfSrBWnOwQw2xC8YlYEOPoA0\nxrUtfHXNIpUt6RqcYMwlK+TCkQ5/sLqSW3V87TlKa6XZb2hJlPKTN1gkljPI\nbCNg\r\n=94L+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOBzGfkPwDPi83xrc8Vth+Of3UItCXm1Lc5MM74puzRwIgYehCXcrRKej/nVWU9DxJnn1Jrz9Dhy+u/Brutjd4Ug4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180509010107_1525828151430_0.03773716322442344"},"_hasShrinkwrap":false},"0.0.1-20180509012335":{"name":"@atomist/sdm","version":"0.0.1-20180509012335","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"73920ede15b4113a45d7206f57968077c47c39e6","_id":"@atomist/sdm@0.0.1-20180509012335","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MwDR3wiqnSwRU3PXAC+c1nybvz+6YQ0tXnaWWmJw5C2u0PylmFHwirRQx2zXte796y/ObDk78tFCrQETweZv1w==","shasum":"b6060d55457792c7f9e69dc9fd24bd5101a5fdec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180509012335.tgz","fileCount":777,"unpackedSize":1784995,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8k9bCRA9TVsSAnZWagAAZWUP/2Zq8uehbvikD/lly/WO\nDRjlE9I8qzKOu+UtMeiFQ8zNk70N9GQ4v/iXipEZvy9DdpWIFAO2gAn93xtm\nivGQSywFC7ToVZuHx4Zs9iJykK1iODvr2K0r1hmgyHle68u2kjL6y1dGHCHB\n25w0N89/IaCWlf2xDw5HkSOAgggy0vQE1Dix9+AqzmrnwOGfxeR2ufpPkR6o\ndGPIjJ3+qpW0bjRR+pqoA9V+jt1LoZaB5damsFZrsdu9yzgnh+evNBVaixHx\ntcYLx1q1lhsdJPhKbHsgcGyv+StSsyFV+e5Ouri6LLkiv1+YSf+20c6UdbLK\nQNNRO9lw/9ONRn0qa8BVOq2H6JqEkv5DpDOTnyn29INnOyUagEZpFZ7wxINP\n9sPTAkw4wyuOloFSPCqyvafG3GRrCqW3jiSQUilcAEAx5bK8K0MG1pMLczvj\nqLOxx6DrBEFJxfBQ0C2wbE2hhfqaI6N1mRNbsb+9wSEPnBDDnsszurGQ0K3p\n2mNG6h/gAXLDy88fUeRcQZ76qkPps6FU0/dJJLGI/xYGgACHCInrw1LtyOr6\nh6+tFO1NHDspDHSCtg69Ehp6tliGy6DOCXYlbRtr/u55zXtDalEWA5ImfaJl\nieUpbeMMuvortIT9/5mN5RKkWMDpZJWoBs2pJhmLdaG+E1UxNCWF+tMKmzJT\nk2Lj\r\n=h3MI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHNVDihir7uEWTWyMfz0P1ddjP1U40T8SrN8mgZIpgJyAiEAtP1UhA0zMBWY5eztXP2FzAKw6tWzuYGQIo7TK/CTcPw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180509012335_1525829465228_0.7259628853435549"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180509014815":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180509014815","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fec1b173e64c428d9bd7e0832d0bf1c605b21480","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180509014815","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-de0NLs9MpvymOG2x5UXFbILfD8zOW9witipHR7Nb4PZYQTHp0mIAL3Kx3JNdL/QjjWu13B7/vonMhLUX9EfK4Q==","shasum":"135c2d2e67c329fc68ae8e48a9a37cd9fbe88eaf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180509014815.tgz","fileCount":781,"unpackedSize":1789804,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8lUbCRA9TVsSAnZWagAABzwQAJ5x84IVaURRp72x7xDD\n6MfbLblfip200dGrM/c4PawgtuhRmKWW6niaRYE3IV6LQTQ7e9cYy8yDm8PD\ngT+yXQ6bPYEHQmPl31NbHjTuwtza+X8Z4DH85LfWbyw1rvn1Y9TNQ6yKgxjL\nhtaWE5bzu+5mOL+MkPSm0GKRK7odhOaIJ+nmfpdu1oTajNfxrvi3Y+6kea/m\nfMMs5OjCjWYsSly+vNfHR1lKJacYNp/9H2h7MsRFz79+6hMSoGeeOzmDn/eW\nLJ5TO89nCrTIkYNZ6WGefW4UdnxrODwaLv7vtDzW3Fwf3l0Yf3OWFtiyPx9G\nZ8E3PapPtoIBy7hCIFSlrjUP1DWIVXQKZ+a83u4rEiTzjkzbwF/LCD3GK8cz\nhGddIwX2hGNWAB4P2blm1oeySNtr12KdXd+iIZhzu988ZNwBl5+fKCtfRTyJ\nxUM3hjN7VBACk9XQez8/QcQDlgN2wjFgMx/JzUGeRKnhFCJmmUjGGU7f7bgf\n6eVjiV3Y3kptBJf1hMoNymqKVE10kcebtz+Ae+EdYtAJi/DAu0Ya8MTZ+9kP\nB35bfoxNubq5OzAsJEwHcLU/lxkqvU58D0DlfDeY2CDWMZgORfgdXYmZLYfA\nkt33F7YsdMi4m6FUS39Dxbbblk/EGI+lqikeLF9IVuDnDShvECC0S9pMTGn8\nrDJ0\r\n=lKB1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBS5z+RRqjv3b24CsipHsw7eIlz7NT2UPTtFtJW9IqmwAiBqKYQwrx+wx6PxerPhjFsuXQGqgPDkU91xGpHtLEx6EQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180509014815_1525830937707_0.11461826044452139"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509053956":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509053956","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eff479690a4c030d0bebff2315774f567a7cd63f","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509053956","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IIir6j3FizTcy7tgn57qZf1zCYPL47Kck0uEKFOnbaIrFQyZL1q0344YutSM3yTEHk8DPtYIKLAdd8Qe1TvD6Q==","shasum":"de1eae6ee9769e98033175cb4c864ce98f97ef33","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509053956.tgz","fileCount":768,"unpackedSize":1777315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8otyCRA9TVsSAnZWagAAlhwP/RmvWTbkwtsDfk9rS0b3\n9SJROR8ld4ldrPx8rsmTMtuMYhuKU6xdTj6Npko9ZipbQd/d137b1MFGtUOV\nnz5a5HjrdMLCVEEdQSc+RxnvpeFFt4KXeaKroYf9BgKskoeWty6rpnaSkSMP\nZ8VvduYdrAPe1Ozh5Jyc9ePjzmImfvubzig2K2cKjF+uVZnU6SZ1x5wiTJ7t\n55ht3WhkDkgcmj2d5ARe65OqaoCn0NbWXdErssvw7FCoIbdJRylbNrqUdQj1\nXL3wdeE1EdbKcS3isBYBJCHJGkUu70cZ0O0cKD+cpYt7/OLjmZtdkIyvqdyQ\nV6YMbLAfeAo9Hdqn/+R+CRI+hcncRGhliKS/NXmLUg58t8ggKZyZLvXW1kCf\nOtjo8kxAH53Oy+RnTPaQebvnN19gpfiPEJ3pLGnOhsrKfWeAmACkVLiodCuK\nmuq+ck6N4A10VlxehPmBcBH4AvPgA67jDoQ/80bWI3BA527EcGqFu1F8FasI\n9r5OpG47gIZTwpkqzdxRh/90sz32nZ/qodD2PFm+1/f/Gpoo6LsmbhJgvPPK\n/kIYfgeENfjPfzecSzI9vr7MABpcNV9XjI3ZCC8n6qKvxF15X5gVftnLmca9\nfH6yv+KKh3D3IYUYqzYo79XwkdQ1X/owGkb8q9aDsSR3ZgTUFIkqrGLy6BAw\nWJuk\r\n=DTBP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGA3TKMybpuJaGS0o06vbrVgbQ6T/FxG76ZTa5UH/1DjAiAHKwhBCkaHISsrpjyHcyVhB6hzaUU/Ptdkc2iszjuKgg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509053956_1525844848344_0.10045980391468223"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509184313":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509184313","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"99c2abadf62a10f14b55fa333e714291f4a642f4","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509184313","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OR4cb0F+qq86sNZm4ieOLwzPB7sVmBO76vELCP3viBB/0m10GDnuJMWx5fn3DVvXtEm+LxkxpEJXi9257Uje7A==","shasum":"0f0faeee6d22f794ca048ae7518fcb92fcd3e24f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509184313.tgz","fileCount":768,"unpackedSize":1778318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa80LyCRA9TVsSAnZWagAA43sQAIdSH4WB5I3pOrLSOIeS\n6CMo/h8ghilY6xdT4TBPuNB90rOvPmLUhqfVHKlsMaK07+53z0/ngExhs7Li\nPnvE+D2eOu3cwVo0Hi0M3/ykMZJ7zrelOjuTFgSwdbab+VUkbpVYeOFTtM/8\nSsfcqVMILDFphlPoLI7NiXOfyiBNgNR2YF48zbE1XoqrdlO760EhDC5r/0hV\nUffO6hT/a7Z1db4DnmB3fHLm6CNnSlQJzEwjYlGvwzdt5pbT7hhYiQIwpd84\nkMk2jO/2ejd+EgyAkynMnc+tzI8xp2TGRF46ZlVdU0ZvfiG9Wmg3iC6uj6fO\nObdLYDTIgk0QeGBAQ3n62sHh4Rqyq+h7CiHiARcHeTS0rLe/zgvcPz61y6rT\nnNhIKn0PJJkKgk/j6KC2xQbMLWsR2TDWWGyadjGxw4DrzLtjFdFpGJCFKMvP\nt5JTZ7BRfju6OOcPzU0IbO5Uef5ODlRPXpLp9Cbs+jaGop2dTV3DvbrHVJwp\ncRDOEAimhjiIUU2lo9Hsu/tULbgf92QVFTfMo4jKR61bYVY+iOgSB9zPWUGl\n2FxBKha2RfoY2PGwApSumNjI8Rv+jThCsw01FTue9Xd9YDHTzu9FmHcLUCrK\n7FXjZAY2Szwlf9eioGwUhcKcw1ARugdQQSXq5xw4cA+i0i1t+tIF9YdjNZtZ\nYEH4\r\n=fg3g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiu9BrI3YFy4+n/IdhsPcBqxxQFjSRV7lSw2KXdcs+jAIgLmYBD6D75tHyjiq8SARNexcfJSW43tkjjjDsIATuWHQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509184313_1525891824928_0.16891396033612405"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509190459":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509190459","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9d4ef8ab22ea5e8637ef38524103a0420460e405","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509190459","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QG2xq73EkhWdgp7nfLH0sJNESXq01Rq7aH9gPOp/Etfy9d5KiyBJtOb2ZWOnYFpYdh70DORJ6wAb8u6+XkAvrA==","shasum":"607f81e43037fe545b77a0507687615d7700fc2f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509190459.tgz","fileCount":768,"unpackedSize":1778408,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa80gXCRA9TVsSAnZWagAABdIP/2ZtExIhv0LFGdJ8tMlZ\nExdVdvwHPfGiLYSDrSZdUFWCSJtBWWlm09A2txpmOfsqG/PYuvgZWfn82I2f\ndYTJmSDVamYa1bEK0Hz2tTYQpMGTOb7l7X2F5NuwrBOcQneABeV+M9wqkc6E\ndJaSEbh+aISSci6tEgcdBzZuGL1DBYmu9ZiHO8CM9o/Y/IN8xwrJ/Hv6i0vM\n9HEl7SiS02qu6lvxgmZXpN3m92rL0cIy1EOBd1rKmk0KrOp+VC1ju99vTAQ2\n7rZbodhgdFddkZgjYUTM0GWoqZGaA8ACmDw9NMSiQK94kyfJdBP4/Kk+YwgO\nyTBf3Zie5AeZoy67H/DMfe7XvpSTxDBat41ZIxNEBgQhPI4z2dgH95nx16J8\nYZ2nc21JQ7rzfXs4z3LP1shYQsP98UztBSQLTcfmcF542TCMlJWWA2Rq23nt\nOGoDoa+jfjc+GYv42KmA/wioKKcVCbh5DQ60twBh4kroAqWNfj6hIyR2hdh3\nPyX/J12uJD08hyJEQXq1HJPcddsvFkdBbbij1SSJgIBRQZ4bOvy+PqzAiUFH\nhHEZqg8gQocpRoabn7R6qHuvrnnHCpetS1xH+vtvxzItz+pjOEA4yZ/sxm+M\nWJ6w548XKqQBPq/wplLdUwgMQLYvKke5c/Q7SaqpvCuBUljsvgzWngkw71rs\n2Ovt\r\n=oD9U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCUvMVq9rGrZ/Ps19sKBgZzRfzVFo5yZ8DYQBSfTQ7IGwIgL03Np8CcSuOOVA6S2KX4vnWHhMbRSHAr4EUHDl/hswA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509190459_1525893140251_0.8544848719065308"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180509192134":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180509192134","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9938c437860c52a972bdcc9d25d043f3bbbb954a","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180509192134","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JoAoVyPCEnbCuurWRXy+4uaBNEqMzJBlyXlqvZAN1LjBolplyJOfVM1/q+Yo5HBrJAfi90RYjHO+hT41PFm+CQ==","shasum":"3578cf82fa6e2854a200c024b07ce1fecc36904e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180509192134.tgz","fileCount":781,"unpackedSize":1790383,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa80vcCRA9TVsSAnZWagAAF5sP/1/ehZyyr/oaQx1/8idl\nkwrj4i+OG0/0l01EMsqJXjSDl7s9V/wmhKoz+HnSlEYMp8QwGu28KMSGFv2u\nfuuiUewE91bGxlWmzD58bI0fyeXbo1X2F2GWpPVpoBWYKSg4xLTjZOt04yyF\n3IYlkjcnLPsGU3NRdLrk/EDZIhsHbnmMJ3m40wx6DeuuEvWuHaQZ9XW+6zvd\nTJrQ65LHWRcV7acVyf0vOLoU582MKpDJAlJwJGMgAsbNYeddhUgLaWMIGaSw\nXlUnL8xuDtCPE9me9XsGdEeCbIHnWs6zVeJ2CTpm8P3wDQ9doLgVFsqgcjBH\n1JmO1IUwhPSDBYFkrie9ktGOj6e+nsGThW0hv9zKQcqO6xvkeIQwvkY03sZm\n1wnlWmy/hleA6vy4rP9QpW7w1qmvhKDKPSjuXCnEVBgbmKHNpUwvrNjbu9eB\nBkrfEuV2e0j8VwpWjjHnTOIiPi7tyNLKtmrrOmqVbsMIhPwp0fDEMRdjz+vv\n5Ah0CVA2xxrk9BbJANIdU9IsvPBao94O/vDmXPao3HBcRRBCQhLMPNBLTcUd\nwrJOeYltXe63vpuaEnSG19V36H/EFtLIYnmxDFOxquJGbkLoBVUu0D44GOgM\ng76hMyEthxkScRV4ZkDK10zjMQUxwgbvv4eZ9RKz6aCKGJCoa7ZM/D6Yv9K+\nhu0X\r\n=dWRI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD1vo8/P+sHhL2HNNna15Acrp5Rs3J/YQVrh9YE+OjDtwIhAJWYFuCZbxODlUuyEA5vlzHAIAKkFeL4uUyR9QmRgZTT"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180509192134_1525894106773_0.5447232631571"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509192236":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509192236","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"05b715df4f6410cf1dffbf7b7d681eb0e703e6b8","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509192236","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vcfDki6LhDThLBEl6nf6XKfmvUuLrrOC7T2kyFqKlwR78+9Xq5nwS0uo68xk9Y60Gux8yA3DvCAl4YoRQbtzRQ==","shasum":"f15cdc7c11296239ccb609e060357f98a7dc0ccd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509192236.tgz","fileCount":768,"unpackedSize":1778448,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa80yRCRA9TVsSAnZWagAA5dAQAKMpB+XrD4glyMLkbhcc\nQL7o0cF93hwb3tixbH6dzu7UffMeYsMBRDqA2TWVEcez3Bn2PIeYX2mJjTm6\nifNwzbg0xwBcoS4lqgv9rAQD91AX8zWr53lAOeR8XjU2+Iq+oBkj7KkGj9Ns\nD/KTrhTGbkcOcBtsa/m/9PjFI9q8+Vo1W9b8QBYRYS3IFyCsWa2tM1sGTOb+\nuqb0gRe4oE59a27KB+U9fqpb3I5pw4rn4ZXiLeR+mAhdPdDCGX0IpSKytIev\nvYHwAC5azB6BRO0W/4ZQnKnGVPwemUYzpPrwv+usVf/pPcuqIuoxHMM9Vo3/\nnya30Ia0To2dXU4PRPWnB+HHJ/o++Ejiq22CwpM4O2XUBD1GoPXAgUHP/Q1a\nxkp+1wVdCxYiJ6zBeTH+SyOShh9J4FYI0nwbhBQ+sO3bk66TP4XA+94KPHrm\nqwEdIM5JXrKC9SNwbULkYZ4IpvY1xoIZ4NvI+SZR0KhBoDjELOqL7uYW57EH\nzx3xgyxITtMeR0lHS8Crnc7+BdJvrdpPB06bAgiWqwEJ7d7TFJ7eR1w109wu\nt29P34bN2ZNyabMOPit6dp+dAcOYUHKIIL2GyUuIiVV8wt1WH4vQ637obFdl\nYVxqc2C+Fey4e/Zkx24h+T48Fa1UKZDZG04jRdsXl2YsWB6NIL+tnTtsPDMT\ne13I\r\n=aX/U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHMTLiucvli1MW8N1EHppVpwQzUnd0gPeGNu046Aid86AiEAr4ZPSCRlBpEY46cvXr90Puu1xmkICo8IYt4CNFWm6Kw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509192236_1525894288249_0.3331411076868731"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180509192411":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180509192411","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"56b0a5409a4addebd49bb98f107345920c9b7e2a","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180509192411","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eToBbztPgxX/8HiYzTFO3PV2ztA3x2QtpR1pXeIWRAG3q7VoyrkA7lrPxpNXCtLvqk3lFXkYLaTxciKaeqLSQA==","shasum":"e61c760ccb3917002802f942184c39eb1b6b6a22","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180509192411.tgz","fileCount":781,"unpackedSize":1790384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa80ydCRA9TVsSAnZWagAAMYYQAI6+cfDfanv83IB6Ziag\nNGirfuZLMRuoUnodJXYF/17tuAV30PvwXumFq9dJY89xV9+pPAskYdWUD9rO\nrc2qMasqkIBt0QACTcDZw7qyNv6xAPo8502kxCIp2FAHpFzggYwhoBY+DuTG\nm7EurW3kn9rC57zgmfH/IAxUa7exJCklTGg2hhMr9VhlyE+JR+VoVcRnnb25\nZNJmDtZRUw8H/p2XI59Y0IFOMjVFuhMF4GWn5K1G3giuxoksyLz4BKqaAXWK\nIFGX70Vt0h0l9Fu1gY7+DbGbFWxGMQk1qZEXIiqKY6JLWIaVp/ihdzyB0PTa\nBy7+JTo+QSrPEwf02NVUbxy61CI0jREYVTSmwAFY+bR5FbEsk8RfS6ZxebqB\nZckozyYDl9rjuNlosVPFDtssKolqyAIENWH2Ahw1LqmSzO1dwMUb4saGcP6g\nhXHklSdvYg89Wv7RI4BqSHjFS5aMJW+H4wwTvktgDTXmSMBXUSAzrA0d5/zx\nJY6EGUPLfFbpxRwaK0F53uBFDACflG8nNFF0AA7AIRLzssFSVNzLB1/AhSja\nrJ3BnF0X/r8GN4IVn1qQyi4iGszMPutI+9/7kvw7VdKTLpGvSddC7kQITpMD\nixJwRklwHPDR0T7A4LBs9ldujft86G9J7HD0Ar6IU28rQYIFGm26+NnEcSad\nJni2\r\n=Uxbv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDVA/8O2CasT8rF+WaPplSH+RCDDNk76KL8LbgrnE2SWAIhAPEMBIrtZnmND+rFA2v7aEfA3qjVK9W+yHM3rHZT9me2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180509192411_1525894299280_0.053378648174350785"},"_hasShrinkwrap":false},"0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126":{"name":"@atomist/sdm","version":"0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c7736e63cfd735751e67e00e5d44853356b22a41","_id":"@atomist/sdm@0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wcv5v+S0s4MXUv7k1M6dcJB7qBA8OfEB/BNCEWJF+y+KFT25uylY4ZT8GbUIa3XDdSlvA3A/4NY/FD+K7EvrLA==","shasum":"7368b8a39ddde86494ab71b317c45089296a342f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126.tgz","fileCount":781,"unpackedSize":1790156,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa82PuCRA9TVsSAnZWagAAYHoQAKHpzVffnyq95tTVMr7b\nDfml7LM3ozIDHsWwj7/7L9BbUe6njUrhEMmocXeQzetMwbi6XHpxHYZz23Cn\nY85r3fJ2psNRwCZozbQ1KystW3WYqXesxecioKz7F3i6SGWHi+nO6LauiJn/\nSPKN0W2DRS/gGWYJfYrT9WAMmuDTyrMdKMs62p6fwAxPuznmBgbl1FiFiY+n\nX5y/e733Xck/cTqgwC+H2zPGKsqU1trkv7m1FIaZ2UqLVJKa5pVhkwP/XElM\n0QVkiSS7ePi1XGHK9IYwORLgwlRLzdCuy5DatAgSoXXaG42JyfHq8mkEvf/B\neMEF4aHz80agakOnmcQ8oNreen1tDHOG6fLv2EbAPGlZN9G+8lBtLn6UnPmV\nwF/3qCBpjb1rn9WYXTvMSuz2p3dpBpljfb6552qItlFCQU7GOt0mdnbDTgHI\nUZotwirrN3Tpaoz4N7l12Y83E0NzB4nVNx2CYkysiNUY8IvorP/nYNGNzJDF\nSaAnn5qyYGW40S6keq4E2jqA2RMSHo4tvx9EXKUyLqGf4Mwz40zP9N9UGY8l\nkgwIJ+LMF9KoDMtpU9yVUQt8af5dnQsNJdUNzPB0dmN+zjBLySk9tY0YMVDK\nrs+E9FOZDc4KajPCVAzXgLGiGKDBuolzAVaE4wg/GsT2NKsCBTjJVMVYo8PN\n1h0x\r\n=AZ7M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIASE8oyaqyl0mRKD70CAp5XwijZo8jTgXdcIsRdueeTmAiEAgOMFZZIvdnNgKwg1gToebNM135M25c+Piu3j0Nowr20="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126_1525900268638_0.5220050528507973"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509210153":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509210153","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7de26d0283b45bdd47b42d8c1504e2c4dae81428","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509210153","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NZjPSJXJODfrBtKOaqL+y1FONzUFnaQCNKVSLBS5/+OdPFt70W8prz1mBhrg8NNg7rVS16w/I9F/yoIHjq9vMw==","shasum":"bd72f3e70d6ff298cf7f7bb44bc7ecd1bc3eb2b4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509210153.tgz","fileCount":768,"unpackedSize":1778464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa82RfCRA9TVsSAnZWagAAGmkP/3bg4myoC05gQs8yMfjJ\nuUin4UpMay/zeO52o4+9DxGZ+sPeUTWXIJDkRdarowS0SmjLy7rqcQVjt4gj\n1pjxVASycmcQJ1xX9GAR2I4Zi0lN5ZkxhLzqplvW02Scr/hza9Q2tqZDouXP\nWjkA5vKkLIRPvNKL6jNyhsSzYWi7F2JDskacpi/lKeyAAsD2KMqnqiDIZGcH\nhTjzRgTEV2FQn96OD/WIouVzwihDaEMJwWV4eOIdVIvu9D8oBcPDYmdQejp9\nWorQtEtpP8ibeRnWU6Cmx6VPNK/6d7YkF/6dfbeUKf8LhMlynt62gc+yj1+j\n3fV+K8CpuWgND5FlUjDMmBGRvtewlUk50Jecz4K4Q0Ut4Ds0j6KYqkW3oVU4\nR2xC0WJ3laadyQVJpZ5l9Tj0JoVY1p+VRnN7qwpyetR2OdQfFNRyjBZChvZi\ngwup8JvaqMrFfNOJIsN3D51EuOGetV2fahiyOG7tEhg32tiQZzhe9RJl1DMH\n8J+bnPSuXCmavVFidj3HQiGiCDGZujRGV5jFs2g9kooRR3tZxESMgj1AJIDK\nJI8C0Ka19u6OclLwV72iMz5XGKAWCKJ6GcSLv3HLq/5kGHMxr68wEfyelXQt\nJtvBM9tltpx70sBQ4cxYgTdQ6FdHQAw9f3iDl8t7UwW3pBxx+oOd1nal0Uq+\n/Q5P\r\n=D7Bz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH8SFP/u4tk3jGQAXOMUlYLI/4LHervytVC79lFToVCYAiEAtAkVoKuPMsNTOOso6wbodiony1vMK+aliz2FHNRkOSY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509210153_1525900380858_0.008216689940726107"},"_hasShrinkwrap":false},"0.0.1-bb3.20180509211400":{"name":"@atomist/sdm","version":"0.0.1-bb3.20180509211400","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJNUL9qeRM","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5629249f355d902bedc8047b7070b79084bf1262","_id":"@atomist/sdm@0.0.1-bb3.20180509211400","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NITSIocfhf5zNQ6SJCQZq67xc21Bc8hRj1iRio6sZOft+MfiR832z4dpp2DBKsyHOu2D++VdJNJ/howDxT1eJg==","shasum":"406efa93249e0bb1fb0c354676f18d97420bf056","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-bb3.20180509211400.tgz","fileCount":796,"unpackedSize":1823465,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa82Z1CRA9TVsSAnZWagAAlSYQAJDTAN0WJy7Hkoj76419\nIzVsCt0QnIMWDkdIqZvsZUA7poCP4Wt+bsxHZBZoCHJndJpw8qCgzi60xHT+\ntMtzkl+pxGGYgmi6vHpILWPboEJTHKLsJXqq+k1tEdARY9UCNjsbnnHQMVTx\n2AVDTPFO1pNmY8JNvK30AjXW6OOhLJQnMXI4hfcC4saGqm+SAgIBTe1Jco4Z\nUw9fWtbMDdIxKTr9K0nN/4RVtLoAV6YHSqCF7JbKZVWpvpvGq5Vl01kh90ju\nDEBrzKN+utjqcRhikIF8ZWBz59+ZuAvLCMhtrjy/qS7FISszsPjciY7dtqaM\nrHTaZNh40E1Op+qjHbYfAJ/az8UGMNhPoaZWJ+qn0MZ1PQ20FJeWQ5Oe13vW\nD8KzKeQLclZxdbTilsWAGrAj/hkMlfKXXUU/smX+EJ+1drSV90MEKEhdHFgr\n9v7j+HgB/P278LhHoBZGKUZ903S21bnBQ1gWoIk81PbdUPyXVo8rB+lTgGQJ\nF5AXfdlR5KtB/ceyclBN44xrIk579gHtEBlJa8cXRTNun5S0FBDnKZ9cvDtT\nuP+6ylzkOiIwCADOaqVI8N772t+ZVYXWQ/VLU2x3vpAQwPvPuxmHHSKqs39S\nASfNIhKaitDlgAYcKn83gkq1SYLhTrMukdJJVT/0FH5Ew6VCEjbJ+kRQVteg\nRI9q\r\n=p7IB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChN8+gFHkly7dfRrdqNMdcoNQsUwHoqLPMF0T/pdpeQwIgTAP4/Msw0kP2e3TAoXWGAcvsipCc8jDr6jZT0xDx6/I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-bb3.20180509211400_1525900915451_0.9693007410915704"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509220330":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509220330","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"40eea6dbb474186f8219fd33dc148d8173760084","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509220330","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Rk+EoyMfFBoGVjBeiz1gqciUE31ET1QTx13ILgn5vwclp0N5CmDyrI+VTMzSNJ2kiKSQXBJisGRmw6TyfADNrw==","shasum":"27a2e97b588f030925f074a14bd36801627e9927","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509220330.tgz","fileCount":780,"unpackedSize":1793659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa83HxCRA9TVsSAnZWagAAqlMP/0hOTRsbCitIqGtI4j3a\nI1h/fbDzWnAQRJWSdKgj5kx0Lwki9vrR21x4UsMpmIT4ZJR8EIFjl0rdA2qw\nQUiN+sfLWmNwFLeicofl3WRz9X1aW8boDt77N9aSge0ucfXvJWo7LRQgXy9H\n6UkaCYbcZzvZ9yPRXr02o0v+R+oLvTVP/5FCoRXxIw09gZ73ArfYSVEdUnMK\nM+PGpCQKYIQuGtse6YlSdx0ajJRsQEQ0G76Ilz5fPeGkwYm3sjSaPsHv0plY\n55e/PR9JCau0qf4/KDM2bIPGBeqnZMkYlq677WwpImhgedQDcu3IQUs8zobc\nGN/iLpdQD0+zy2u1ErOi7qlV3FPJ70bijwMGM1oXN6H7gnf6N1kx9PgLTLuB\np49qTk5a6KAOB3P+20d40y3NLQK1zl2E96MdWfXtmIyg0snJEULvq1va79uL\nbQw8UQEMdtnjrzLMKfRVxR8vyTrQLAOGxpWDL70FU3zSx+PJ8HH28geTqnUj\nlEnRtmStFZnaAFzP1uUyRkQyGvTpEtPNTxR2IV4uLrA0LN3s48XgwA+FoCLM\nQxe7v4ahQzsAgHZUvWStqngS9XD1Gq4l6P434+HYl9snOKWfJ2n50U+qVqRb\nxnf+437YiSMCm6b2lewS+3unTzfsm0dSaVniBFncRqeLcDMi2g5MFQuNahM0\nc7VU\r\n=Jmeb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDN2Y0zPeZbVaFmt2lnMnyMr5VwBxhSwg4No5fqsMjkcAiEA8enOwbjWGBpEpXgseILJuJAm1A+HT2D+qhqLv/AmE5k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509220330_1525903855700_0.04603718281061475"},"_hasShrinkwrap":false},"0.0.1-20180509223333":{"name":"@atomist/sdm","version":"0.0.1-20180509223333","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.14.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0c5bfe7ca2a5b4aba5fd5e731842989d3965122d","_id":"@atomist/sdm@0.0.1-20180509223333","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TFFsWuuwG6xhK2SDIawf+v24rjktsd0lAixnR4pcR6/svKND7vmHLTCufXV/vbu0EtrYgdmsZqHX95ww0fyeug==","shasum":"20fedb813a1551646bebbcda275e97bf4cd9fc48","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180509223333.tgz","fileCount":781,"unpackedSize":1790073,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa83j3CRA9TVsSAnZWagAAiaQQAJiuPAKJou0oeObOv123\nG56+v49xN/6Z02r+85A4wOjdK/eYeCwByUj8eB0EteErBFM4yw73PRiSoGds\nH+4iLUuK9sv3i37NkvQpJOPGacKJNAmxiJI3gO/looqAu7zUssskK76Xqlcg\nSI2Fxb3gn5jzZ9PeWt3Bu1SLJ0CGXgwoiuMuS9VpfHgoNaoibOaDru2KzHW/\nznFy4F5b8/QTB9FPxsq6kPWpKMGIMIKyDGgUiO4vk4kgFS3cHwhARw4skkl1\nkMoQRKDh0TKf3NFZG6gxypNADPm0RKkFcBULDS2MkrO6O1JXGdTZfePK5Lbj\nyDbg6hqZ22sPL6cGE2OdRPzvbjC3U3wmlCqdI/FrrZMuFhs2HOJSevw6ZeMN\nR300b17QxCxRHjmZRshULQ1FYI9KwIVSa9t0P1IhtdTAt5CfFQ564AM7sV65\n8eqN7QTArSXXVEl1UeK8Li8bAtHfu+JS25c6He9Ycy470EJj6/m6Po+d+8Tm\nXt7Mj6oE2dDpkfsQOAUi7m2AsN4wbERAgNzOz39e2FC9cYkLWNjSknISOgj7\na4FPrX6kPjoIMO1HnNYNg6MpU76MGuZXaiLZZXCMGIZklvuFC7HUtTi4/MyS\n4b2nUWN4+0JbS9Om+7eRvFNDV0cxVxdMpZHNUXMFssX0qT9mnsfmIOvGvYiA\nBHSA\r\n=+ZbF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCu9chFyC23PG/z/+eCEdXPmunxx89DISzvlP59XnSbowIhAN7GFtiM9FruWO1mvr/iHHa55F5ny/wKfqAVk/ckuTC5"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180509223333_1525905654018_0.5483954609206476"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509230218":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509230218","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SkDUQkI0Tz","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"40c1dac69a8f17cb936dacb16f3fdb6655b728fd","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509230218","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-v9TgBXVkVjeoJ8tsggCAxgokZAHHuGh64m9fwM/Z7eogXXmCvv7WnBFnVMG18sNBDYFw+KzS/uYeocawZwUFlQ==","shasum":"de98977652cf805c62141e130196f2e1cab33371","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509230218.tgz","fileCount":783,"unpackedSize":1794180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa83+aCRA9TVsSAnZWagAATUYQAIQ8BaE1i3ba39M33rIq\n2oH/nHYhGwK10NHK3GBLxKM/waT+c0bcFFhTEFYCvjuC41aLW9NWBv5ydZB+\nn2xyL5Rp/ZLhviXC8MA0s/hIuCbpeIikkvpSm8NKWAkevoc5wyY4h4MJL2Ln\n+2t8jPn5JUX1MavwLW6o8G720ZfjQ37j7dn+TRkfWL4rZNX68M0R+2Tyr6ba\nJuB9CJIC/BVcDKPUfWhQzL7Q1C33KQVRz4lIXsURECgWnE0UDIB3UGXsgy5y\nOJmxX5960zvmYP6+d6TzRaPEQMmPGe1kfrr+w10vRkPDZq5QTI5CCEAkCC3b\nH4m5jcp8cOOHNQDdnNH/FrzMGXpUSeJyu7s15Pu+YOK3ro6HrCt9Q3hcArQ6\nxmko2Sxpkg/TmmYonjoQoXRSRtFg7jDkWz88ZUmwZd3Ouy0PDkfvMQDxQhjQ\nSQIWqT7v2UZg1Jr7V4D/dqDpek07kiYooyOeZDrs70u4OHlK/9MXKv3tOG7b\nQJmtdRfE2fHd+AxRB9p7jgPaoKk6ppFl9L72zAtHeC0doRTInRvtAt6T3Uq8\nEZMFXbviQ1W9VqsGUEcmZLuyxw8tO1bgbyKb/ue0BS4EW4XAhTHiCmN65jJD\ng/l3h2zlxBcBevcga3cUmJc43l/2c43WMlkuzNlxSR1ubFj9Omd+R1JOGVgU\nkTmy\r\n=499y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDq/klTutrD/4iUc27f3ZjTgHXcDVxzL/deS4s+3HK7CAiEA2IvHHmTUjyM9ahhve12VHMi4Om131QeyPIUnAoKeYAY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509230218_1525907351982_0.3933578358252483"},"_hasShrinkwrap":false},"0.0.1-no-env-vars.20180509233440":{"name":"@atomist/sdm","version":"0.0.1-no-env-vars.20180509233440","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.15.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7be307d68bd735334ea1cc6d00bbcfd78af6d30a","_id":"@atomist/sdm@0.0.1-no-env-vars.20180509233440","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2F5KHoXAF8uT9pFcHo0ti4Q+gBnEURd+OOlmHh5Ci65HdyXp8dfTX25f+0yEjAZJ6NymriVyVglamI9UZKTvcQ==","shasum":"db6ab1dd6de4e0b7b9e73c0469283dafa41325a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-no-env-vars.20180509233440.tgz","fileCount":787,"unpackedSize":1799114,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa84c/CRA9TVsSAnZWagAA3NkP+QFLtwVzhiIXXX76vLBz\nYy/dfDQB+SEbIJusShxFNuQ2+yG9fPXZHuC1aX+EyXeI4uQo3WgIQZ2sSsH/\nwrie1ARBn4h9GqDzZDTwsqQQbgDuxNdEPryve8g157kwLAOr/nOi8MVqwX+j\nxcdeYi5IN8CZ1tCnI8GE0ASWxkAKpkekfmnRkXMY/2q3yjKvvxAngFjdBcR0\npQ99O2/Q9BBEakGY8IC9aatLQMPxQULSs4Ii9+Rjxvz/J3CSn8eAHG3IKs+A\nXUF9H/iksRvw3sJ+NR/kelboGUEIQAd/V5/HT8rpkN5MrHGedULQXaGFDkoP\njaegNsFuHKhPCr8tAOJF5/ArBV37iTxFYUuwh9uy2wUW7kSjXvN9QiRJiIBw\nTGMzjEmAGQn/T/ij24OvE3koDUKky0fWWrGDLBXQjLhWmhJrroPklvhyGWGf\nrezNqcjLwcGHeLFH5Exa+DRbqtAbtz6W39sYYs26smZ+gDLsXhTAV9p4a3nP\nNU2Fl+rmD8VM8WsAxyV8ZQXnifN8GreHYijlRBGnTVOnjz5mAi4N9/Hg9a+H\nqgBkcopc80/yz/6LhESd0TrYRu9RbFLtVm9s3IUOc2e/sp20ifp9CxpMKLVs\npTvvoCsutEEWLMvt47tIRGBAeXMcekL0m6UKw6Ylbibiba7GAgo8B3Lv5IJt\n2EFo\r\n=Uax3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2m2zg6VFED6vFVA+Id/2qLee5syM1i3TTjkrnjQkqYQIgDXpUqTNG4r8Lqszus6IpbbrkPtR06nvsZ7RDOnyrb3E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-no-env-vars.20180509233440_1525909308823_0.35485107665608706"},"_hasShrinkwrap":false},"0.0.1-20180509234340":{"name":"@atomist/sdm","version":"0.0.1-20180509234340","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.15.1","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6cb3fcdd6b5f4de55147c1a8835d629ac2b3ace5","_id":"@atomist/sdm@0.0.1-20180509234340","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-f/CQq6TFSdc8Gap8e8aF76aQtj1gJ3LerBrFi34qodJe9jjFK2pV/NIash062IubwHknP/KG8uSZrjmZVJhdcA==","shasum":"d17c070bffb63a16a0990ec2c91b826475a71c12","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180509234340.tgz","fileCount":787,"unpackedSize":1799102,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa84lFCRA9TVsSAnZWagAAWHoP/15IwTDqZKoda47uB1Ab\nc/B6qG4ZIO+Zb+RY4UxznRPZAqK5CMd7BOSIS6nuAloxLu4q8rUIEuy2KVGB\nhQtADOKn+FkV+eMqKdBP6xNudrc4zmrXL9nR7sXpG1mYJWJNwhimOqkzscRN\niPhsu+/fGNxS+qQLr7jYP6dA1NdS2p4HmEnKAn74ohvifIpHyOSaAUerXbDl\nGtEo+IVwyeUX9B5bp60xPSl3PJwPcbFN3ipA8G4rf9rpBEFjAQMPyJE4jX8A\nlRRlnK3cVS4l3pmbbo4x+V2tJEL0enHK8wVYo5wdkDMBsjnRjK4MN06uDJNS\nirbWf2LsnV5iBjScNbC2yF0GFLF7taeWTgig66hsx8JJCZxCqUF8AvliYdhN\nTvr2eI3MeRbkYDj4dXfkie0F/IL954wZRXCiFE8HF4YHSypJ422CJmqzOh6x\nVXhIGyZxXMqsdAgKj7v44thFVmojCqLi9m+XV3DwRVFgZN9NjO9UgJVn22HZ\nIjuhcvdAuGvr2cpW7UNxo0+iahDEFby0t5dZC1zajLzQNj+A0xladoHBfDgg\nFu22VQ+evlvPhSo+IGxLueqGjkyRgDymSSSDU3zB5lYPY+AdppZuhrpOCYaL\n66+JCToGc1yLyN2M9/ZTm3o+ISvMj0YX8yDbbTusva03JRu7Un9OYGieRsBe\nAzYV\r\n=iOfa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGQy0W49MqamYxkgbGgvLBfjmJ3Hjl3IYU521rGGQShSAiEAnz5FI4qam2jFBKCujU8BJkPV4rC4jYFUSqH4pCuMQ/k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180509234340_1525909826975_0.8236835414737682"},"_hasShrinkwrap":false},"0.0.1-20180510001024":{"name":"@atomist/sdm","version":"0.0.1-20180510001024","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7085a1ec2661432cb04601e160081f89cc47810e","_id":"@atomist/sdm@0.0.1-20180510001024","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zfEPb6NNGoTQ7P6t2wIokezEiM3RNJhAehbOp2f6K0EjClYnME0gM3AAmkMyDQv7QEOScBlkKVlNlWHIrrQcNg==","shasum":"408f8e588325b5672da290e2d45cfeae872dc866","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510001024.tgz","fileCount":787,"unpackedSize":1799127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa84/rCRA9TVsSAnZWagAAflQP/3Za0Cvpq/bPG7p9WDEG\nWEjy5TmBqHc6ZNWyu3TnpWNzyEvrPZ4tmaHalOuw/bfnQ6Wa4XyFXVjFitq+\n8FhupQY6fDf7a5fEJ/jxJjigPgoceATsaJxT1Hp7sO1m5t3Uyur/QRCQUwU1\n5/IlnvnmW/ted+ABiEo6tWAPyRdJxLUZhFW1lBvp2irhCSRYfwSbfhC5OBYA\n0nZ2iNdRC5i1Sfz9W+MTGWDMLZ8TZa5bjP37Mcyi7F47+0JkB/XVVgasICbq\nUn58SthKMZ0hN0HGycJypCri4YplTtwIQIeH72459pLBG742k1GeiyQaRI9z\niBRZecPLnUln2W6cAwuKCvIm7YTQP93d/N4lCDX+qy4CIJohLja+hyCiA5oQ\nWQWQuIBYAQJwF9UVKb4CHj+rPLdAFGLBX16X2nFTh5ql8uV6Qo+21p0mm+Gt\nsv/O8LIVN69PymZdhigSD7xbP3pKephWSEQrRHAnQviioU31wg78BLuRomGt\nvq/0MjPfhS/DnubsIxYFCDHO+hb/P36B/DxGeorxOg3NSdbjKlA/WdGx0QXM\n+DZzPPE6DiZuFpD8LIz7ms4coAEDGjwrVgDHlM5EvqmpI/9frBvRFi9kBzo7\nTyrU9/3ctrVHkrh7i01KAMbsmsB/WbwS1XAZbibwNToJv2brPUhpbwfp1Fq5\n8iZc\r\n=L9yx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGAi70ktB2MN8C7MCRvuKxc7TSZlgu+F1NxV7LZ/DvyBAiANcqeTeU2ZgUeg8HhD+/kHhNCj6RkCUCqRXSYzgMelPw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510001024_1525911530164_0.011688902458794725"},"_hasShrinkwrap":false},"0.0.1-20180510011339":{"name":"@atomist/sdm","version":"0.0.1-20180510011339","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4a525f18b16cdd08cef0b7ac83eb9a9bca51648d","_id":"@atomist/sdm@0.0.1-20180510011339","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C2k8ugt+7LxOfF7zkbcRGQj+X0/2bOSBhTqSw3RmTyf4WghEkrVweyqs83fHdwIP0JUxeeBxoi6wGd/KkeBjMw==","shasum":"c934ed251c33204902ce4666f13bf228a3798b13","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510011339.tgz","fileCount":806,"unpackedSize":1837764,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa855oCRA9TVsSAnZWagAAInQP/jIHY1cN8xstZd4OwO/t\npUwiwN1ZMnH5TIFBDNX2Xie0qPJu3BlWkngAM2Nf70mvWqS/6vnBKmkkXtSQ\nwpuTlYqWy5hytH7t/oiM7yRCYlPbyhJYdl7AtN/PKPR3zMXpD39lWxHVshcK\nVsXmHZmVt2l1S0SnJAtm++tKZi7L3gZhmzMC2Te8tGTtO3OCWFRWNiMBpNrI\ny88fxzcVJtDIoUpeuV64xLXjYxHiN91ZdThUZfY120ZIviXzytjMwFk7gTFh\n7RPZJnXqbnfEiL7DShOSUGaktSnxLd/+OqJ6BOm+lE4asisndunxKKzcuVYj\n8ezrGtk81JcRtBZwsw3Jz1LNuUh3shlLcIO8tbvbOOP27+mw8Mws8htF2iXg\naGKy/QTlaSpn8LEQicgaKil5JmJdTw96wJSYRpjGFECZlKel+9ktTNRJhbbD\n7QuGB/Ha7Ktl8NgmvJrodSPSSui18W0cadO4izIY1vvCqSeDn35lhlHMOTgP\naDGasWXKKPHZWdLDPTV3kZvC5zw/CWKz+DSs1pJgdOR60o0ccTg+sARtUdeu\nv9SsKROA9AORzJKdKqEvS9KDy6VgKa2uCOLeTrPRdiUCOQNuKNU/bSHAFNXh\nwq/PI8zM8rKRPg4T3AMZLzgnbLJxg6O/VmemAxSUeL/GVC07ofXRy4zvsjkb\njB78\r\n=wgXD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvWc/2spcexTv+5ZOuKmgGr1K5CCC9mSay9axTsV9P2wIgbwRepbdznnZxXYMnVQ0RT9NVvQxmdWzKxgpeOWB+LfE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510011339_1525915237245_0.6054502976419214"},"_hasShrinkwrap":false},"0.0.1-seed-id.20180510035101":{"name":"@atomist/sdm","version":"0.0.1-seed-id.20180510035101","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"585f5bcdacb3bbb3b9e1119cf55ec9ef1ed99f29","_id":"@atomist/sdm@0.0.1-seed-id.20180510035101","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5FxLULT3iM6UGAuNH5nArBaRN+iN2uvkE/wD32Hznw8eUB67PEe9/FmxzadmoeNc2zmwn/+qf1m5KSzkZRn2rg==","shasum":"f2e4b56e5fb1b1205678015ec2a22ec16e855c50","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-seed-id.20180510035101.tgz","fileCount":806,"unpackedSize":1837874,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa88M6CRA9TVsSAnZWagAAGDEQAJD2fRAe15XAumhXM9IL\nfzbdg/iQHX3K0ktfP7WX9de3qIYW+4aRrRVLpDNsL+uenOT8MmhoH8qrR8Ex\nzuW0rcfo1W3REiXR+dJiSJIiaY/kOdkTQyuhy60wamXApHwqYK7SqGlKtdDw\nCj8bObmtAzS2V1m4C+fDh0oZBsIpZ5RbrwcKkpdTo2jZsGc3nIsPi8A9++Rw\nFbxWm2xBbTZmTC593UTBD3XxZqNUh4A3PiBffTPU4YFP1azCrVWflpsTHZQc\ndzWTDabX+wMS1XBcmg1/b0DrUPtZK9oQmjGX0yukuHKxxVHc5eDJ7iaIsLxo\n438t6Tlbag4t3XFCyezsRDkKHKutnSue1h4qPPPV6+x4ChlIVkHfqfFgPYWe\n0yVfoyDaYWtX9k8Tnd6Qb1v2koNrNlknjCuznSgnnVCbLnnwKdBXvbBWx5jO\nfJmzDGMpSYztzXREocmCHd/UlNSg+YyCOqUcZ08vU3F+fPJGRYuMJ9xPdA5D\nyc8y1kNp7k5VJCxFzR/Ip5ilmKQ9RKWq5/3WoBM5lXd1I1EsJ/8web/eYWBE\nO73OjRDu1RhyZ9u//588L0yVoS3/KqULwTIQMMlypTlH7yFXWfwNbKMNQp7/\neYuall0VTdxUTRgD7MrJVe1X5jhR4dJ9smfPAEh71C3qPGcruRArCSzmvkwm\ndiDJ\r\n=LWg5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFIs3zlSL7Q6Gfq2lN/6LGtiWGCF6IbIhU2upxoaVXxjAiBs9VNjj1DdpmX4kYFBrO0zI6kwCqUyqn2RZBg+zqiRTg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-seed-id.20180510035101_1525924664490_0.9622523330399169"},"_hasShrinkwrap":false},"0.0.1-20180510040313":{"name":"@atomist/sdm","version":"0.0.1-20180510040313","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"465ee699a4e3edcaa25530b7745c673abd12c713","_id":"@atomist/sdm@0.0.1-20180510040313","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HEYuA7BOVBpZ2JXag8fn9gY/SL3PW+p+D+X7wPs/a2CPjmInjUCyN6l2KnKZ/UMa2PTgch+niSGc5rXoKv8BXw==","shasum":"343066bac190f1d1893ff8f77c23220288645608","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510040313.tgz","fileCount":806,"unpackedSize":1837866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa88aOCRA9TVsSAnZWagAAXGkP/3I3MksHxz7x0AAhhQF/\nDprDy2tT9Q5g4ooMpnCJYy/imDjBrGQnjZI8y7K7X9OoMPPqYtyZA77aEUB6\n84xpev5uPSyoOj0zlLmBQC8LURoBBuv8hliZqhs60D7FV1+nTZse/IRL+yqg\ngsDbHiieJULGjZ2wGN7v+S6DQdhbjHPf7yeDc0ZSVjb+rjkwAkdhWRuuhCpn\nCO2WY5Dzs4+SePC/nzmdi8N2IgFne3ECmjF7qzPqINtZNg3sQPN78bwClUF/\ngBVX2fR0Gr/mq1FytIqfv9XYJdQlbfzMHHmyZHkSyKX6mtYEml+h4ALcMXNC\n7WnyjNBoFblJM3x4roFz/HDVhStfUU54JFToIHZxCSXM/Tr4/XfkuObsSRF4\n9RKlb1M3ZEDcNJZ7x2sQh4xxJaSoXz+1LOR46MOA0wRYeXo/QDNC/c6gKYtm\nU3rfIUTtsbOBumr+wskcPE4achBpOeHr3wdBRMx3rNg2DepHcrqxXejBvSHh\nQAaX2ZBG5sKEbQ7PMhY9Q6JsmqklPnIoZIsIgusQeDLHnm4EnRDmVB/eduId\nMvAEnA4Y2HUOLR+OE0gRD43u5MiCwICcNFh+7Icfba/pl+K13IRjbd5ffUio\nLNXyFbkkH5JGZDBVHHNj6wuUhGEH7uq4KmhI1/4ejbE2j70zzax4Q+mOprsv\nwK/C\r\n=oLaU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID7Lu0E5vkwJ0D0iWI+v1fIQvjlsEx8U7QNnF2D1nOHxAiBz7pzeeXeT83yWzAWCGJ/FkRR8cFvvICoQxp35i224Tw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510040313_1525925516893_0.20848562138561766"},"_hasShrinkwrap":false},"0.0.1-nortissej.logginess.20180510053540":{"name":"@atomist/sdm","version":"0.0.1-nortissej.logginess.20180510053540","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5df82cbefb74d00dde9fcdb3b34ad4e78a494cf4","_id":"@atomist/sdm@0.0.1-nortissej.logginess.20180510053540","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6r0kYm3j8yC/hOgz+OCK3l+3Cax/vzltXM9E+p1qrIVkMaG1iD3kYCAB1fQQY5zfblA/wUNlUzrrbtd2QhKm4Q==","shasum":"f5c1424259d8af47cdc75b3450fc00b438a943db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.logginess.20180510053540.tgz","fileCount":806,"unpackedSize":1838530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa89uvCRA9TVsSAnZWagAAv0YP/2+VUSVl8KcmwUc82Bc9\nxDEZjC7JkAJQYFFAu7vsnMwmnDM72Mn7YMfSuxgM656wrcuOEUCoOJx6E6Tp\nvxGfM81Z9Gb9E1/sys/umCW0tKdAruMu5AdJStQeyb460e/XVGofenyU/JQ1\n+9ybq8XyhrrDH1yFPpAnX9ZGTlP8zw4ICY10a9nSNeYiQvqC3fBtw+9FS5JQ\n1MowxXvAi6XIa/0rmAA9yAPjkn57tyiUDBEsHgJjArYVKw8xNb+mzbW+RJ/9\n7jvyL6IT7gLe9dxhhfg2xykSYAD2QmLnEOGWSji+pn2WF0VCl5AWOHsJciNi\n/SXa/zam9ZSQfqxKrpynpzkyrnR7GYrN76rO3+mYcVa5sr3SO3BXTUE2tlc7\noJoc4AGAGbYzdkAqoiaODZ5d8Hlh+U86sV5QJrRfDG2PYCgQuKW1YVMs7qDm\nljtOnf4OQgewvffnibtC8mXzbkTWJWPAZgCoI/j9fl9JPDVkseU8DG61EiR1\nFJJxyH0YSKqJPS4Z7C3lqTvCA3/zce9nmHmzmEf+diIBZgf5t0y81aZqboW9\n722HLGoPi9mxHC6d1kbEKSAQA9NDn5zSW7Q5Xi3HpdnH4fz+t7FZWe/gKFNY\n6pPyITJiUJeGQGmZPWXNnHDbWRLnjUMd18o4c0Z2etAf79A3qQL/YGc4B2Y7\nprcF\r\n=WFfI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQqt9L4A9YMUQckr5WpYlNTWRi6gw+2MIS9n9ZUfNXxQIhANAmuH04bjR54TD9Yq43S+OGIgofe0bCcGTwVoRw+3Sj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.logginess.20180510053540_1525930927008_0.9175569585755661"},"_hasShrinkwrap":false},"0.0.1-20180510054448":{"name":"@atomist/sdm","version":"0.0.1-20180510054448","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"47b4e92f13f7df324c96cefb99d888347e2e75cd","_id":"@atomist/sdm@0.0.1-20180510054448","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kkwRvm9e94o88vmIb+/vrf1+wvM9HaQ6eww7QQFNlItVlFF4WJk0kI7R5gITe2FKc3118//raZBuMvznbOft3w==","shasum":"c68cec692b14f57a55ff054cdbb44c45afcac9f9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510054448.tgz","fileCount":807,"unpackedSize":1837189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa893SCRA9TVsSAnZWagAAvoYP/0su3aSk10yuBh3w1F5z\nCvoQLUz4mvRNaxO9GZvwX2RZvDhaMIjzZf/rI+lJDFfuyBOBDdFXnXDkgkOl\nA0B0OEyjK/s8kG7xu3RVZho+E5fz0SqZhHniMejyjzgZSFVrFO45qdCvoihy\nmqm7xr5pLu64/kdNEacWf03XYh3QEHCk9npnCuv7sCC7TC/Scre41FQi/CPO\nqAm6Z1QHgHaHPtSuYaMTS3v7nK4SX/Zzkfn01ackZigOLbxD2FGKyXl+h73y\noCxTnUDFJBJPqNeDS9hAzVS5Xm+6/KvHzfCO2xDioIyuyxrnWwjbZcpii3As\nDMpKVGrnqIIxdfkPeOCbGWNKdwWXpFbegYgaBlumVV93u6YlYYLXwcZ5pbSZ\nqO220EgLlxrP2lIGR2dTjAVButEFm1UhCoYXvgFGTcYYjBJl3B3EIpq2el9d\nSufdSXxqTNTsbItwJy3gf2p4FezsqBI3q9Fk0l6KS60+QFPRYTuLHQ2114EM\nlc7UR5voskusu4GWXOH9thSXYSDLP/ehzCHMDIrBRAKasB2RNPqzMMfvQ7DS\nCn/L41yIPPtYL9mdR5t+pquMsn5bbXIGMjkSOcsOuKZ9p3jvBKWKB4JBJ5BC\nF3Tx6XPrOmuV2RjoiTPdionLAW7Te5p5EdRDKfdXz54e6f94YXiDbBwWZfiT\n32wT\r\n=Dk6O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF5Lgl0gOP24Z1wrDZbxE8BoIxHw1cch6/tmAwimOQE9AiAW7cON+tbO76Qoix/P8YfPZ1iXibueps8cIt8o6eR7EQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510054448_1525931472902_0.5968352228651443"},"_hasShrinkwrap":false},"0.0.1-20180510053118":{"name":"@atomist/sdm","version":"0.0.1-20180510053118","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e354a53b55ddc58afc0470df0e761e181d3ecf40","_id":"@atomist/sdm@0.0.1-20180510053118","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7GSDfBGwZzDqePPvZdKKgPajpWamzRqHfkVtxD5dswHP+L4+FHahokRr5ai58PoREwtKey0ue8IX6jdL2iRDbA==","shasum":"a51976a230899845a57f331c2d6b71b4a3846e6b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510053118.tgz","fileCount":807,"unpackedSize":1837189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8+E6CRA9TVsSAnZWagAAvWEP/2KX12UIjPEoBVrdf2YU\n9IkoAoqizfR0gGXa9smIqxq3r1SZT/5l+mENFe9qmWX54J5r47IbI9/GkKSN\nPnx0grMOgMTOKis8ZgEyCAOXZCf/LNWNcSD0+oSNiDX+Oui2RrZtIAIT0LTW\nMAQUHcL5s2Yz6PexWTyI8CvDW/jTy/x7okaxVaw0iS38qggdmyI5+eg5TuBD\nXOehZ/0HZ+d/H/vU+1vD7W9w5X9Dj/ZteC+dK/5kimRTzRPL4hgYYH9okGgd\nMa3gl0Qhjw+m0yXh9eLZmjPgwUZPWljDF2UjjJX4Eqw8s4bZnHJcT5fMjnOi\njwhanoeQ0tNkk1Tj5vPvMMlnruh6YPqSwtdKfA6jb0d72yt1vWRKCcfZf122\nzDS7SYtc5yUQf4Bq2crEpEYqAx+m02oGpwvSq/8EKM5W96Vlo0/rWWz5vaYQ\ns00q+ER0xuUjldNcSADtsl+OeZpvUgEId25a8Q4XGiQgjFdHQDPdlsmIBhbO\n+xKgtVTr9MLBXq0W90WGxaW+I2X8/MSQCj9NImGdG9sjS0tVV0hT6T2WRFiu\nCC8Y7sA3kYSiNUy0VQ1eqkvfcoe89bLfDl2dNeklvTjHryCa8OEqUhJAogNq\nbVRHJPjWD4y/dn46fkrjhNnw8B8q63tDlFXEbp3X2pXnfWkd7ubWDOLA7XQr\nIWMt\r\n=gGiy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDtJ5th3rHug0hp1JWTW6CxBZEmcyDeRY7ydfmMAkQrMQIhAP5J4TTqPWAhQiHbj3Mwb8BWKRfGTPKhqCLbcHITBfYX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510053118_1525932337568_0.5236918989381267"},"_hasShrinkwrap":false},"0.0.1-20180510061729":{"name":"@atomist/sdm","version":"0.0.1-20180510061729","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"488d0443077c29906b89d5abefeadc2f6eefbad4","_id":"@atomist/sdm@0.0.1-20180510061729","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-He6Cw2JHtLLdRS7KeN9tAFJwZhFvAqyrCgLAaHnIlbBWOjWhzD8sRpbDDlpF9mh/lSl2ED4ufh5Rr0E2fX0dQw==","shasum":"2e115fa85503456873c36eb94eee5d0c28a63704","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510061729.tgz","fileCount":807,"unpackedSize":1836309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8+WqCRA9TVsSAnZWagAA8BUQAJkcriyXq/WYkk/57gMt\nKCFp5LpP38YMBJizB11v1aEN6a9YB7alveuiCBlruic/Nh+8bA1m8LrP9Uj+\nXV8GnREm+OSk5b2rFN0xZwl3IWm7ibROdwH54td7qX3tyL9fCrnN/c0tdjqZ\nckeFPPMh9md39krggDwBq4D/3XNx5bcCERk8CSmqc0fowLkjmthC/05cpDtk\narQQbsz6VufDI4USNdCsGyJm055aNtGnQJ2Bcw3S3lXQJvZtEmR83EGZ0hCV\n81SWez/4H15QuPyboxJALCbrVZzx3KQgto0Szp6eDMVHkSqtapf6TIIBYkVV\n1xuuNZ1yqKypSOfjPeno6c2YqhQSBJOi6pIl5zezNdEn50UsGxg9M0S9rebw\n/x0GOw8zlYbmOXqN+nic/NLX3bbRep8uIOslZ9IAiBriWL+2Oq+AKmKXUYGz\nIymXS542M2TIMbok7HzIKuAo5lkaaIUV1v2oFAFMMdfHG+Bz8REbxL8Y3Hvz\ncEyVuQMYl+58EaR5R1XUXoSJqs8HC85CeaTuF9D9KbQ+6SfxbgKHmQaU5p/u\nDzl0/g6N5p1Yp4eCoSDbsDdwQxpEXPKYn2SuRyqYysFqgrVl8Ylz1GBisXqi\nAj681OIJ6UTERvwgKOktivfcoWSC3yf5Ti/pfX2VyYWhY6JM0cdR4Vgkkcoo\npiLS\r\n=CpAs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBNjdaD3j0N8dQ3euXo3rtJ5c7XQX5PWe/C1IqG0yVRIAiBm1koUOjvNncFNeMA9Qw4ygQmfng0i/0ulO0o9uJNK6A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510061729_1525933480781_0.9911845680298481"},"_hasShrinkwrap":false},"0.0.1-20180510065314":{"name":"@atomist/sdm","version":"0.0.1-20180510065314","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cd454e6145bffdb972cb476ab39e4ec135ae1631","_id":"@atomist/sdm@0.0.1-20180510065314","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-omoMAP+g2Ls7dpuwegZaeFi4w2fUZ24LGPo+/QOeATpy8cuNgYY1E+4SNptTRaAgcrdq6Svf2e5PBIpuCNw7Rw==","shasum":"bd3cf205fd904fb4aef9962815606d49c681f9e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510065314.tgz","fileCount":807,"unpackedSize":1836164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8+3hCRA9TVsSAnZWagAAjMMP/jFe6XmE+hpZSrjzm2p9\n2YdpkX71Z2j3ZamSNZp8lHfZrmm5RNttVCWO1rny5qwIxDjmVkqgEd7UOtU0\nTg8tX6KHYzdo2cZC47M2lElHl5Je/lSD/QYEgDDebcVWykKsd00Zqp7Gtla0\ntQGESkvBKeU+CrYsLxcbAjR7b8BmUXRbkLirPdrA6sG/wiFbx5dwkp+Dhpwl\n1XVZl9Edp+7G0YMQqAjicJ2APcPN+YlcT5oYporUIlcbjYitEfSBQGVO+U2A\n2LkPl/cODHlbkycDdv3whH1UPxxpihQqLDQlHv+OYG1hp0kPDRw+2oLHDeXq\nUBckZQ7NM81sTvgIGnuE8JYom6AZ302KYCg+gFw92uvV1u07OLzVs1l3g5cv\nJO96wYtFEa2NCRyPsVIHisymizKPT/n73dqN2Ke9K53G2bXJP7w5Cnr3W2G+\nGarrz2qNqk1wgTAdvQbuhOMxBqwx43JcjvE5NqSz/NZcRQ1PBmqeDqJqOurY\nNWd7KTzt5ZXrHMTEIeAedhEdlTmXaOqvGUo57wS1MRhKXaW0KwGsytz1R0dq\n33O/KDIf95Cl1sY/yPbTr879XSLzetIvma3UNipNzxNxwHgbvDT9ud9qQHso\nA3GC8uM5LeR2F7tZO8MaGFDDNL7NpPkWBIF7RJmsySmMssa7Jtcj4xC5Kr+U\nEFnz\r\n=AGSZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIERLhMHJJb5s4uzmjX8RI9h3lkqJ3C+vBgmhLxRr0xf9AiAWjom4NVwS45Z0YcXE4Ho9TFBg6PQX8vaoDI7lsOmGvA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510065314_1525935583067_0.5566334781685534"},"_hasShrinkwrap":false},"0.0.1-20180510053020":{"name":"@atomist/sdm","version":"0.0.1-20180510053020","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"021a1a6b1707a3c0db46e063531a3fba191670ad","_id":"@atomist/sdm@0.0.1-20180510053020","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-A1m5QXnBir0X7NK47gyYGz1YNaH3+/qMC/FOajZ6+SaMU9KnDsFCZLMgO800r3FS5r80Ds66J2VONnEhgxrmjw==","shasum":"84cb8e3e7aa272faef123a9c155bc5a6cb41e198","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510053020.tgz","fileCount":807,"unpackedSize":1837451,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8/RfCRA9TVsSAnZWagAAWyYP/RXH24Yr5WKUmiNWEJVd\nuFMmiurizkZI+CToEKbKly341a1GGPoAIlhxMyoK67/5Zg7X410AeVl3JGDy\n2LbU20C4Gf7DDfD9114ggwbuiVA1RxY8GVNLIR8GdTeYa/fn1kyWRts4qyn1\nQBtMzN8AVcWzvdUYoktIoxBHSWiT7biehCzsdbEQJjarnGl67rW0jacpD0rt\n4hRJFFUD4LdtdDBj0ztcWD1QwSjByhPP7OKRCqPqtg1VfhAsd58a5ymTTylR\nVcgyJdaDyMOEDWEt2Y4p/CoQbppj0xhnE1g4CkzKKCrFD+5pdF/S9xeIDyHE\nK72Ep+VkUaZp0NTlDonag9+wsZFVupcI5Qw+9o3/Fuajl0EIkwf4bG9yOmov\n8W2X1nFPGcf87ooiQU67zixJpjyyBART5/7bJvqn6QH6wFB8qVXgrtScCqXI\nIzCbqhWlms7cJXlMt/bDEreuMfdf5Q1FMV92bE1HsAGxpZUIqUJYh3OWfks+\nKHSmIs+2rNCLaDJSW9I4a4xVubYREWNgu7xjwV81x4iWHmyT81SwQxB5Zg0b\nmXW6O/4cK/v2iFgaKWT0c1QH3GYCPlV6KJ+n7uwoEnu5DdFm2kHIpmxi35Xv\n+mZACx+zm8Zb5LNj9mH+xy9ZvUt8YM37CNmkdDwhe0PBnLm+O3jrVP+JHTy5\nAwLw\r\n=4d6V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9sBwypavK+8jDoxGH0uJf+UZIK8bvoYAVr2rCmAhWPAiAEDpCRPQM7E/NFizJl67ooDwN9QfsUnKXM8xUss62fzg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510053020_1525937246087_0.5117111774215288"},"_hasShrinkwrap":false},"0.0.1-nortissej.skippy.20180510052648":{"name":"@atomist/sdm","version":"0.0.1-nortissej.skippy.20180510052648","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"43615a04e183a529058b2a83afc215fee4e8592e","_id":"@atomist/sdm@0.0.1-nortissej.skippy.20180510052648","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T/YygdE1EioTVwHCRiwKnjHefPgq0CKBqxho2WXTvJ1Lf7PBUPLjp6q332eHmfEozCUhXQxjiNNp2AVJx/ihkg==","shasum":"e3c4306888713ad8c73ada7bb27b6a606c3f3e47","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-nortissej.skippy.20180510052648.tgz","fileCount":807,"unpackedSize":1836824,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8/p/CRA9TVsSAnZWagAA7yAP/169FgRH50PgjK7NYRzC\nh3HX+SBpJz0NOMV1C/OHx8M5TIiYV2j9Mvv5jNNxMbYtB79f/ZGi5ARybnQq\nz8hdkpPt9JjtTOrFAlB3w18Lx9xU9c8aXNCyZ2pSsNaxZHzYn+8LN7tIk9Wi\neLRmZgJZiw3JR5JPmX8uXnoYdghmnc7CGIqADaN8+WCw/Z+Z+WsYbATmTbh1\nAqWgiQZrQLf0FQ/rw8EzP70eRtrWFNO1zg67bRjKlfZaxsmrBwW2FKqeIKnx\naI9c7mIQP2+2i7RlfEWblfQugJNDNzOFOjQgw3NwJMbljzGWt1qs5EneviMq\nebTNNDOpnmpacXjP8NReHi2TSfFO7eOA9lIcbf/hDa6a0x+H1hMwApGa4+oz\nap5KTcC6x+kx712HlZEhdSTlo/A7tOzmeSFBFOfdD49IFNEPPcUPeh5CWAuC\ny/lcWRlJztWfuinPWZ4dOZkKiJZl9IlC7p6+raUJFHA3rU69HJs8ioUZruh+\ndSu3W0BaUaOeCOGr8/bwTrlgEuvA4FoLE9V38RzNcl6qmwNy1LA5Ai7G5+Gi\nZ8j1kb1FMDYs5iAxYpxzTnDCJ47BMVrPJtDzsKGtmYFGkfOET34Ip0USUtUb\n8GRCCW8GnQlEeFYSfrRwti7PUWgYQQSh/WemaC3x4jbmczc847gmZLnG99GB\npn1a\r\n=fM/H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBxrPAwuFVgwnVrAdiUnxIFoyKAED4sRq+vHGa/eyklcAiAGVS9duBiztprB4KCUAOGNpTqquYXhWRsaTLDTLQGH9g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-nortissej.skippy.20180510052648_1525938813759_0.3277051781151661"},"_hasShrinkwrap":false},"0.0.1-20180510053848":{"name":"@atomist/sdm","version":"0.0.1-20180510053848","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d2cb8767aaeb191dd06097c188ba6681adc182f8","_id":"@atomist/sdm@0.0.1-20180510053848","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-i8aJ/36NGto38xWVMFNZ1xqewxdV9bj1EHWipnPM+xjjR8CjM6uj/WeHSsI7Unb+eAVqjiK+xrgjuc7+LpbO3w==","shasum":"269f4d87b4829ec095a16a9e3cd764b6c2acba02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510053848.tgz","fileCount":807,"unpackedSize":1837189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa8/rFCRA9TVsSAnZWagAA1GYQAJBRgjO6/D2jEshv8Ok/\nMcZ6jeyzblfAguUifx671UvlWGcihAqmddqpcDUnPqrocWG0oVcIoBomX9Vt\n0lp7Cfxf4BvBpHdcREEh0yWbJetY2s6CWFGXAu52+EoJkxzM/kxKNX5KSS9t\nCWeMT9tPm6lQ2k0SAaN6UVwzjY0tA8T/eyprcLcQ4eDYTTdfvbx0/5a2gLE5\nJ4r6IQu066APmxS+Hqxt3gsKEesksC8FyboTdGTPCmeFL3WPxNQcTwS/otjE\n871KgF17sJj3eRlSjGakZUnJyLVdL6TizJ1UbCbum0u1yQ0pCbQUbbqMSrQt\nqcHfarGUEGVHJuYOSE1bqB9tUnAjqIh5GXX76xjds3v57MErSVCw9BKiUBEr\nCd/M5PYwU3/wKPShQVrFsPOHSZVmzTrWEQARPhdOgvbsBX30M9zue0nCSJK4\ninrDuLcYAiD6E+96quawwvT7ep1BQ0IHi+Ak85N+nFHUFT0TquYaJdi/rRvC\njhENloBqMIEao6cWtQx+Rqk8u81PDJzIKKDPQPFnfLEQ6HaTtYgS1zTOV+YN\nYDM3zE8KtAaQEQvtEgU9XeNSTyTbE4Y0LjHuhQzeypWHVf17tF0sHzZa3rc7\nFq6yMg0dlHBeyW2BKcCC4XLK5ZvBTs08gGJMUjqky6UB7pmncftdUk7xkzz+\n/M2r\r\n=sDh1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAnhLcUzX8gm6XbuxprghgcWycO5F6mywXqnZgPeO/skAiBWg1qwOjGXRG2OMk/+o7Ap8dz/Z2hVDc45x+wWX8U2sA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510053848_1525938882751_0.9175074890179875"},"_hasShrinkwrap":false},"0.0.1-20180510163140":{"name":"@atomist/sdm","version":"0.0.1-20180510163140","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"308305d651091de1895a43b4e5748d7b0c544cb6","_id":"@atomist/sdm@0.0.1-20180510163140","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ADNqmYYtgn0mM2l/besgTBmQKoVKVIEGmYCTet2vMSPkD1BdSzaQ5EAHy2xWri1y5hFgqZ/PDHwPxOda9OfmOA==","shasum":"78bf5eb795cd1c4e5f792ae08ca0134968688477","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510163140.tgz","fileCount":810,"unpackedSize":1844713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9HbFCRA9TVsSAnZWagAAbA0P/1QY22b1fBFlZ2cG8ryW\nHNjPXyV8C1iE0+CD5msb+SAi/Ib8iFv2d+K1aYEckjoBF7ujOf+QfMV/3St1\nmn8V0/UQTFrxVzRudg6pWLjC5rshRBQONFquHb1FL+aS6uue3r008oK1YIQl\nUzmnj+MrRF0OKGvg2z72RbmS/r5eyC5E/gaZbZt4qamSprRxL7UWv0V4TlPR\nZSWqLe29GE3M6SQ6ZdQWmNguUVTZjVIYYTkWqaHtGX64Rqr4ymxQLULs7j50\nGOa+Aw0O6rkIFJyycYYcS5XB95GoG7i1ZctjOLu5zeSdKVw4OXg7VdMTPzVo\n9xdic3ZGSXbqo8pfdYH81SyRBjuijzNu+luxBPKqF5X0LjA0vtjSjEPwQYdK\nd+K+onCTMcfAN0vJSzNvajiToEataL5fPfTdURtDLxB80dS6hvUX8AofBw85\n5WwJOyGiplDCjtKakiWqsxEOkSQDo4IFH6LEktVyWcMiq/QPt83hwPqHdBmJ\nGM7/Gm9BbNVDDmvcj39ZJdvjYwVk0RLGg10YidqZb7pCKio/3Iw0zIJuvR6C\n+LyGPDh6rlvVYQsyhGfDAC7bHVO/ntOMZ9p99opdE86zR3P+3nllApwtr6cW\nQzwNvecKfibBOzJ0C2jvk0QGe/0ZzyyyZPrD7AKYuC37ebqPa/uZvJ6t9Bl+\nunmH\r\n=YVH6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHnXAOON8SrWv12gIQgUIEzVDfmU9Nfct/VjWYB4V/IEAiBQVVS9MWlmasg+DjvPRURFkCwAOmG1ycyHpJV/WyW2tw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510163140_1525970628064_0.9272727051500815"},"_hasShrinkwrap":false},"0.0.1-20180510163759":{"name":"@atomist/sdm","version":"0.0.1-20180510163759","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy47cpbZCf","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"661847ce12fd6be63d69c9a75e61fa410e608cf2","_id":"@atomist/sdm@0.0.1-20180510163759","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-N6+05HLpd7utzgrGNnEbKZUP/eIwc1g0Fh95yVKfhByaXg6x+Xw+438iVDb2xuTjNiVo0dir0QfLmbhGzI/SqA==","shasum":"905f063dcb6ba7b89ecc9d4e2ced226fc88d5f29","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510163759.tgz","fileCount":810,"unpackedSize":1844714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9HdcCRA9TVsSAnZWagAA+OMP/3plIsIm8UmtOSMz72w+\nv559vlG5IMIOCGew1O6yDE6uWmCOdhqr34cqiCiDa1IHy86zXjn0oMA+kapo\nDDVaceRP9x2H7/91vhlupa9qx5N3QPaYzSOtEu6LX2RumeDLw/OvelnS6aZR\ne4BdFTzTQHuMP0H54h6f6C2EyPJ8SkDOiAOqr38w35FAxebLoRwjnq+Ts1JX\nOQgAV7rE8NKeUF+bk08ZbFq2GtWlpuj9v6MsydsGn+D62b0N9uNJ+bVN6w7O\nsoVj0OuX5IrjSdBkyrsxcj4CAqJpjg9pu3MjpO4A3zbB3hcSa+AYo3fRDnW7\nsZ1BPVa35wSteVt6i8O9dUqd8ZPoFfS8zmiGweb7WeEyeJu1R6Y7H3PpUcus\nJITfd0OEQYaQOAJdK5iCiEsu4TnJjvv4V3rxFloJxvizWCLx1/plm2pUErUL\nurb08PokPOkRdUhbNAqptuSgJK35oOwLVq2ufdTJ1ny4HD0dIBXohg8ypemx\nx9KAHtAxNrcmNmuQHPfhV/ZbhF34iMb2CTpWnEfc83Es67XzU2rICzwLktx0\niQx++O+pOD01UO4UPpgDEtFuU4Yh39Sc2r1YzbQUJzt5URIgz0N7TTq0PlMG\nMXOeDs9tbEIoBKu+ZpdPALiNuej8DrEBv5gNLxh2+JbwuUe0xCCAMS5BW6Ci\nm6+0\r\n=3HDo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBNiokmlcxLGh/Iv2ZRlshoukxznA14AoTurGk7BGVCaAiEA4YfB22gpAC4bUBt0dMAKSSjV41Sp77vfzYAyItyhy1w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510163759_1525970778930_0.6399713040049808"},"_hasShrinkwrap":false},"0.0.1-gen-intf.20180510173735":{"name":"@atomist/sdm","version":"0.0.1-gen-intf.20180510173735","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8c6d0f275db5ad5ae66d7168d36574a441c92bd4","_id":"@atomist/sdm@0.0.1-gen-intf.20180510173735","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-njEZw9Nfg4mVhac8ik64JKXqFfTuZEbUG+AIMWJsZf6vTbxdvKfO53o0X8Xm50oE0M3m+M/vWU+OPa0DD05x6Q==","shasum":"e4caced96d4ee6cac15999ec094fda9d68967753","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-gen-intf.20180510173735.tgz","fileCount":807,"unpackedSize":1843332,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9ITrCRA9TVsSAnZWagAAh4UP/0Q3VeAqOXRZCnU44k4+\n4rlFmFw6/OXJkW7p5V2rDcLRZn8mtiFZdJbXL4C0k28t4/GBOZ0TwwXm3Nvp\nwETxPGIrk1CL151s1HI1mBg6v28PDN+/zhO1c4TM8tQLH2LJDiUOJ0w8x0jv\n7MgAp2Bi7R9Ovir7LeUBwjVD1JPQcw2kcGPbiHneJeHjG0H++Bh6ru9ICsuW\n7ESolMl7cDROjSIlYfuifynr1E5M8JwZYtK2j8jPWV45ea1sCuHkpSI91Noc\nBhEkv0CasZ9Mzs4Od8Dv7EH7K6zggIfYRcPSIlTpKif6nA+XPiZ9VfLPrgTa\nPNdb5R3sRTN+OjmKRzoe6/H5r5z5wpPIuKo7f0FbCqVh285x5xCUNNqMyjzi\nolO98U0K+1XwpxKMjKTPHZqPJgBaPXE+axyC4ZBXTi6EQRqG39orCYPWIuKo\nFLkWVlEmVmKCEgswiNTFOi448HwaC8mKpgKOxAPFPuefu9HNamh3WHGfBB4h\nK8K1bSyVz0P36tbGPU9A8GORAr8ODhOt/DNkikwC1lTaKZDZ/3l+T7jWVqFE\nSCdvYVnwOR8oUmXqp4eBiyNzyMnLNeX7Q0WScvniGfBkIB4qkCd2YswEpDlM\nIpZ/Nbp+85cphfe/7uv/OMXV8MU2dwrfNY8+Eho60GPF5ExNtzpSe8EN9rJi\ntyKw\r\n=rbLY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEAFR1+qq7TLR06V3KlQjHjEG8oOA+aTQHLFzLmNAndsAiEA8yEnQr54y5Z6Ol6BuNAIZHEFPAOzLneta4iI/AZHqvA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-gen-intf.20180510173735_1525974244396_0.8977360378125832"},"_hasShrinkwrap":false},"0.0.1-20180510191805":{"name":"@atomist/sdm","version":"0.0.1-20180510191805","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b378bba08779b4a4e47e0ba56ff20359472f2bf1","_id":"@atomist/sdm@0.0.1-20180510191805","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-319v+I9rYQKYm9O159vOa1Aix+kjgOV5SY+VrwvAopfms+yqOB7cm8s42R1sXc5dviwWJqVuKgzTKLSVJigejA==","shasum":"cf581722178def0b78263e5e63a3cdba2f716af0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510191805.tgz","fileCount":807,"unpackedSize":1843323,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9JyXCRA9TVsSAnZWagAAmn8P+wd6ed1KlGmNBAdMUGlS\nn+qmGUF+RNaVd2noGMG0huFNeU+NVJN1Ll6B9AginMt+PnEtjdQZu/VOwL+I\nZZUuNg/8P5PWOOSfv2Su3+gKT9G21Y8aHDv6cnOfsF/EwFpBgUviapDOeNZS\nSVuJTpNAbQvP6dlp+kTBV50po+wAm6Iz1Ma8SA8qAsEjkvEkMXyZSQMwBhIS\nb4FM/Ms80PYr3Op+jib+BbfQ1P0SVapMFgkehHThWn237bMsdnRVGyLOPDBw\nmSYNMDJi99HP9CUCaZW97ZIxLlMOKpip5S87OzqERAfbZOr6QyUn6W88je+C\nt6vHQcv501DXb9+1hppkwHhBmFzKDP926daP6zrdQWnztej7vrcotYD4dG0T\nEYQ1hfC8iTlQgtL+BG2yAKsqh4KZsP2TW4L46130wDs3WIW13rKKOnQL70vl\nVNPfOVKIdVDZaLF+ljRer8lp+ag2Pw71qh8brg/Br6Bd4L0pBX2La/nVn4Ak\nDjB0SHrEzx2blidU4jrqOb+cNzITM0apgULMU9IWHggdmyI7hoqHBFbLBxfd\n60u0M2XftO+VYNxyQPEbHVYKe3AoijXxqpdqMNsN6CJ/tW1ozvrx1Im53k7n\nTO+tM9W5mkcVOpP5XEg5t3NNe4alxD9j//16fLF9cv4DOG+eLWBSs5YldbLu\n5JTe\r\n=XIN0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHOBbaLDdDlPI/g1ORFvof+cUjoEOL2Un4UWiFuBK1fJAiEAyaxnyXaYp4T19XM4DNOWjnZHyWncNDwbgste6dNDM4A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510191805_1525980309065_0.35387066418854896"},"_hasShrinkwrap":false},"0.0.1-20180510195559":{"name":"@atomist/sdm","version":"0.0.1-20180510195559","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"67a4cbfe652910bbe14094c1fb0a5c08c94de2d9","_id":"@atomist/sdm@0.0.1-20180510195559","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T+aD6AmLUWmp5baFH08uxoOwnR1j6t2AAfzMbc3r+yNe617p57xYWVINxI70p1YraCGZZxi3lLMl/j0hg67Img==","shasum":"86c7e6d041122d0127d1e26e0a30b898f2762945","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180510195559.tgz","fileCount":810,"unpackedSize":1849855,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9KWHCRA9TVsSAnZWagAAHjsP/RoxzWaSlYny8qbgutXV\nMoWUrfm7LChMB7ZfmZaV1myEZDxDxYnenVZQho04DiKdrYlWokIX83uwrcHG\nWc+aqbqoYQziKpHkiENPju7kpEOusHFg1pnI2KPsafCnq/4xzp/dEzOMhJs1\nSbCxnGDry5sO1hMNaBj417iKH074otgkWtZleNBeqCqaBiBXkhnBj2aGLlee\nLW95jGEnzLodzZsY4W7wyM2sRhPscyTAJrvFLFR2MT4VOunHn4jhqdwRyeT7\nzfN8fWgLfofuFln9yI3JqcDk7/5uVtJwY8hr//Tf/TKR+GisRVp7d4uY7tOl\nP8PQiyCXXzGLc+Sxbxp+xU4Z5i8EZdgxXepFUqsvCI96N/WY3q5cpjp3svlO\n8c4GSzxZkDbfc9HJkzdHiEK/eMttRqYDwP72lJYH2RTmkbl6kBGEk9NjfV2/\nf9f20oJjXUgMF4gAqewWSpxn/pakfKEng5AfOxe5Z6sUX0BDJo+etagFcmDP\nHcJFSkp+XZ4/YrrjbQNFLmyIn9CaPX0XDqDJj9VWoybY6H+jLZsszUUR2wiS\nSx5fgx27d8TzWQFJXKxjJCUREP9BA3M7opxSP2AQsjbbppZkFmpCSXjm3fqc\na8Px24dVcjD68JBPlqw0h1xM8gdgZFwmGbYTRdfDCpGCeiR6nokb44ViJq5V\nF1u1\r\n=Nb6J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDIVth45fHdmkvblglbABLBaeGFL+Tov/DYBBglvDuTtAiEAqPcC0oFa29fvC1k5QoBZfdmV4pA1DpINb6S9e3xBLQI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180510195559_1525982597161_0.79143278984469"},"_hasShrinkwrap":false},"0.0.1-semver-regexp.20180511032212":{"name":"@atomist/sdm","version":"0.0.1-semver-regexp.20180511032212","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d2684996bfbbadbfa19a6e12e83205114eb510da","_id":"@atomist/sdm@0.0.1-semver-regexp.20180511032212","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nfsB9xSP7Qg4LwtqcK1ztMjasv5WDUaWsEJbLvl6/sdO99VV6gicQ6TtwZySBaYntp0+1Wbi/f32AlUZ3lkG5A==","shasum":"9bd7f136abde957b38378b4d71df2eb16fc7df97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-semver-regexp.20180511032212.tgz","fileCount":810,"unpackedSize":1850284,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9Q4MCRA9TVsSAnZWagAAdNwP/jum4Bte36Zl2NmyjpyN\nQrdabu/b2J3a6rXialhwQDjCkDPtfObRkJfmziLdyNQ7P8M7z5ayTYA0kPLb\nFbRD818icZ1j1ciZjm6T3Xl93WpHGRNOIuSxEyadX6NZ8/+wdPvQiWk264Pn\nUeOf3HpCbfXOSMRXaEwrVGeAERuNv2Gmyyipz8t4m4ClvwMAUrfsHjEG5Gqm\neGR2nBVeduju43pDg2ZoFkYfrHrryupwdW8TIQ5e63+3blHwIbxQbNbxvArX\nU5U9sS9e2ADWlm3x33xVuE1o9ikm2Kefk6wOtO/s2AZLSPuC1Fx/v2jexBb8\npNe76Yw0rWt92B9SuZNnmGpYeW+VvAgst1ran36eOStojDQwuNNx1QUbJjqp\nCezbLpWoEBMFLkFcynJdhqsxVdRyI2xr7c/9nJ1Qq5mkV07wPkYrepqkjvtE\nZZHQyvNO+FWyqw66z8MXru0UG1gW6wT8a2KgxgeuW+u0BJ9G+2U0N/ZFszcw\njDdvtuvJLM+Zi2odJ1CNW+44VZ5XEHOfgq3fbB+7xhneWEQB1lzXdZqJXmUj\nY3GB6mzBwvdiqCZzmG9dCkrPoZ4glslSM0yuDKoW0lsh3jDqGlTIy6lvOXvz\nOKcs4dFbR63jH6CLB/ZdAJvg5mxieAgdIJ/d35lub5h5JOS0S95J/4o57ihc\n7Zps\r\n=k8M/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBiwtHKWGYM/AiBj8qfg+5Ns1bkvl7kRcB7ZKcbKT0PGAiA9zDChbsX6IrgTwWaXAlmU+fUOW0uWF5YVIuXGzUPGEw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-semver-regexp.20180511032212_1526009354143_0.4079288574536293"},"_hasShrinkwrap":false},"0.0.1-20180511034642":{"name":"@atomist/sdm","version":"0.0.1-20180511034642","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d915314e9285adc1840a9bede448b821dafacfd1","_id":"@atomist/sdm@0.0.1-20180511034642","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FXFzn6MABGy5LPNjoi429nvT9lmQgdmyDTmX7dEzi5Jg+9bGUW9F0hOycQ/DrBj9LULkZ+xcOVASHTEUtnM1Eg==","shasum":"33c9be8d9e27a6c86424dd540c0c2d35d23b27ac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180511034642.tgz","fileCount":810,"unpackedSize":1850270,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa9RO3CRA9TVsSAnZWagAAxVcP/3+qSp3upiIVmw7xTKGh\nBIYMB3am/ifMk0e7BSKHbznzTSdBdbMa1TPT/RRs9ngX94j0sZ93B1Db2YZh\ndX85P3dzgyKl9S6hMSNQ7wxGNylax2zT079/IHXZ3wQoMu5KHXP5sB21XYsz\nhpxQeU0PJGmTzU68A6Dm30z3hIdUYjIrOaSRinJIJ5peIEWUFdDQJZLkjp8Z\np4mGQR21nZD8Ehw4C+FDx8YvHHV5U0saF7cwfEvjXBghHrt7MlICzAcOF1rC\nD4bxpqJxiCB/SW8sOsEA1j/0ukDiDBLktKYhP8hKm0dSpEstO/2oVmlPXvne\nHBy4PGP9OscTtrDQVG9egaz8DnLZtaZxwLWE8AD74WVWIa4HOs7GQz4RVYOY\nKOzhVXrq1raJejc3eaqK/YXO6w5lGxfw3J5181A/2SFtoo4N76HlhcgN/XjW\nz3z1AFHAnIF1n6Uv05R/uopixUzhcfSWuEXwC/ke78dqPsCNsu5NUuuD+zlv\nrH9+21Ia0HK4UlKmNq4bIA2w/TODULrlwLbnh/YV0MuIRpYASRA3v+nW7vMx\n1pPenertfeFaGcYnksVNF1oYCknPXkEu0PQMyziIUm9tmTa0+JxOOD6eITSE\nmLRcaHiMvpqVluK5BBoRKv3qFq9/uwHVJMwML9/uz4goc2JD3B2337plaIqQ\ng6yn\r\n=8ZFP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD3dc2zk496Z0coI4iHF/G1581qkeDOpHpw+T5uVtfEfQIgQ1el3dlmGtEEcmF4mLmyt0mP99QP0h6nzPBzzNwc2iU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180511034642_1526010804267_0.6457595292123868"},"_hasShrinkwrap":false},"0.0.1-20180514212955":{"name":"@atomist/sdm","version":"0.0.1-20180514212955","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4ef1cc362f20d13fbf33fc63a098c4abf872c432","_id":"@atomist/sdm@0.0.1-20180514212955","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mP1GuFgg3G0MRL40J5XNUofcRbFCmzZYkItvoyfwsROi/H2iVNkoKXAOlWqmR3PRlJcBCZRPtjRSy7Z1k/16sg==","shasum":"7b05146acfe057887f6af013a0e57bea57134bc5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180514212955.tgz","fileCount":810,"unpackedSize":1850313,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa+gF3CRA9TVsSAnZWagAA4OUP/0FbHkOzFmC91JgNCcJX\n8hRWArYoJH40FFVg5pGqHOXElP/VqPO3LPSRd+BMGBkQzjom2FLOIs2nHlDs\nVxnIJlHh4jOY97eAqbhVCmcWvRZ5xNfEnvHw3yrrTlDcbOgMec9o4gE1hSI4\niK03Kv3Py3kPk6P/PYy6mM9SEY3tYL7irkv9NX0OkCJGE2B4mcJnm5goNNoV\nGMqT+7Pyu4w00B+9dG6J5hhrUdc/PkUZqSC0GwI0bxhl320/o7cmMwj2SEMQ\nkEXQipJ7nFwYNE9NyajQi4uCBqoxZ9ZGy7HGnjPbbC0TyB0fHVQe8m6rqMqI\nZ9c3t02UaV5Gu+DIch/ynTD8G0wYWQdQttdd/GeLYB47OFKXbmEQ+WJvZiZ8\na+bWd1N4aFmHfzzjVroCVPLy2x5loOh469CGTrRIOuKIn8bhwyBLec3Lvkro\nP7vVyaX0XDomDHa/D1Louu/PT3aidixcq9F+PBfyX3CjwVdTR+S53WZTWAWK\npOO6uE2ZAqLyt2iOxA6vtpaXC2eCnpAZzOmp4+Ja4a9KrUYeOzQaxd5k1ZtO\neoTsKFXW+ynFTnFsKk6STF27UegaPQ9cC5lowEx+bHyi8YAJZpcX5dDocPu7\nvi+RFQJy5GIjEONeM0Nbnao6mkT+/m/M1Kpygq5dUpDXgi4FVbEwE23jZQFy\nzPAm\r\n=kkgR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDygink7KfNCmvbjfMC4Nyhf7NJe3ZDFzYjc/ViJcarTgIgAd0qis0tmhn/WZQ0nLF3z5AgdbOYA80PpabvrWPko5k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180514212955_1526333812704_0.9359976591988297"},"_hasShrinkwrap":false},"0.0.1-20180515073807":{"name":"@atomist/sdm","version":"0.0.1-20180515073807","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkrgFeWz0z","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"914c06157f6f49c175b913798b734374339f3086","_id":"@atomist/sdm@0.0.1-20180515073807","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hCVrFelNJTIo5/brFitm2jVBVFBQzwh6C8vAvMpYGDtW8vPoiO0EZN0+PSsTa/N7pGGgwnGefTxt0gmsSq3S9Q==","shasum":"6ea8e15da6de0d32df4630f50242e905bfc43746","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180515073807.tgz","fileCount":810,"unpackedSize":1850737,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa+o/bCRA9TVsSAnZWagAApdwP/0gkGBSD3Ssf2yRuCyM0\nCxMEUSjUHoG/X5dbgVDg40iXQ3vTcI1xkl4NlN6WThrI2IRebgxLu0uS0vlJ\n8NjHsZpWBz070QVRPCOu6ift5PDFpkthYaQyHlyXPG03Poeb/pz9j8sP2AjB\nkj5a0qRYz+NH/97mdvb+G7/2IDILm/LwBhmJQApUBpQI2RBgTiD9srPF3FOk\na5ckNv2AFtr/i2np+/OleWYtZI55n+pnYu4Z0gRQrlnyN9G74La0iOkwYik5\ndMWAIgui46j0+iJutJcELi0P/VDeKfrrmiXJhV/p7PgmoG0TE6P7ThxHWZfE\nBlG7Mmhx4Tx+u7ee684pXB6ZOhgvZ3JNv3JrmHLXoTBpRr/1yIGhxdhaTApj\nZppZTR1pP49kNSfgsnJlmBSGCGHJuCOVaavdcfADpkbplgch3XLZyfd2lGSB\nLl4stE45vbDtXLJ4IxpXbiMhHDD8+QdduzasNNxGrZUFRNocYijrM7AfGNDY\n2UhHiqSVUSro2X0KUR03O+c8ILD69N8qg/ZJgY2fzt69I71gDcgenMiPk9n4\nVTeG4hn2lwGIgIwERHC+JrBZR2o7GGjTPfJhckhxiuZIl6Kj6IMEh58pQddH\nh4ZAPwrK7YzI5nCRGuO8YLOAhnMqemFvaiYDPvXzOSWBRnFl7OeXHNbM1vCN\nmrQJ\r\n=O1IV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFpl/a5CDoxOyLpHZcFaeB1xU41opkWWKrTBCf1ACyEJAiBTtBENCs8L5StPpX5YrXlrS6c4wdrjxrigvY7zLiBIiw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180515073807_1526370264700_0.5174434691494345"},"_hasShrinkwrap":false},"0.0.1-20180515075538":{"name":"@atomist/sdm","version":"0.0.1-20180515075538","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SJVsurXzAM","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7fb4d98f1afc830afd3e05f77dae05109a8e4563","_id":"@atomist/sdm@0.0.1-20180515075538","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GVQ6iuAQQqr5Qr9Eu0KxEQu0FeyqS4a6mSOS+yhYFsXyIhNVZaSzIYQkNk4GlJNCYjZ1Cr7/waibsbxzsaw17g==","shasum":"91b99503f1bfaa3ae0ff7de7323e2e6173fcbb7c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180515075538.tgz","fileCount":810,"unpackedSize":1850737,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa+pQOCRA9TVsSAnZWagAANUQP/0JD6yQp7lhMF6PKBT8U\niPoKsGrOyO+NLKiVTwO+EXH5LqgRch3+ZkvkkRUIrjd7E4kUtBmvVzXUrrKz\n227xAJQZcZFZ5Nljdy9wImGFCQj9B4l6yU8n/RHAkMy9Ysi/OxsdWqaHejfc\nE+L3fe72MXAZDO81wepMX+JLegY945lQtNuewZsNRYafU49p4ma6r6ep0L/4\nANLr/IIAVrzBbRfu+/s8LSYyZzPJGex302O2YeiC9hCQNd4YWjB5AnCh7tlB\nJiJ87mnzsU9tWP7jGxX2Gj0o8gLoLj/fTjFWs3nVf8dYKxB8VZqIhOn9CgLz\nw4g6L7eNK5lNM8m9VJD1y30TPnVbaZO3a1iotgp2FDPbEkFD8/lNCyMcbw/w\nY8i6f7zCL1uKz6PQtIMfkvDXXj1kBGlF19SAGoKWphlqz+Q/DVa7qccr+5dh\nS06U8XHspwokI+aPDB7ngnAmTakHyCv1bDADXIJp6m7hZadb9Zse9KDA1kTO\nDpSNkyFApRaj/mKWDOPrG10ke/SgVB68jiQZ2G7jj44Qu3Y9wCxVIN4bnSxm\nbGkJDXQ9Wyqq3/D9Ht/t/tf/g1Y+3Y0RY/E7H9BFXTmcdVFt8+dSKrMsxlAC\n5ASqhyOkau2Hyc8IKCUxTypdsQGJILap5lsV34I933uOaTRMTOaecB1rG025\nblEQ\r\n=eY3w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD+1qwWPgAuAYebZIh5GC6AxjPJZVIweUpdJHzU52482AIhAL55qLJZds7VYB6wFSWNtxvX8A9r4iB938c2sUy637ou"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180515075538_1526371341073_0.06328832430762676"},"_hasShrinkwrap":false},"0.0.1-20180515213224":{"name":"@atomist/sdm","version":"0.0.1-20180515213224","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SJVsurXzAM","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"97ba3c8170c26dfb44e03a1952affa66144315a4","_id":"@atomist/sdm@0.0.1-20180515213224","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XgK/xvs6bPMhrN7WqDQ+TgQSmb+sowzXtPG44CR3ttqdtcRTX5r6Zai5kwMZ2n289sb6Xp0XoMcyqiXgbZUuFw==","shasum":"d521f5c673d6aa947c33550d13e4f7e12bc5e0ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-20180515213224.tgz","fileCount":814,"unpackedSize":1857895,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa+1PaCRA9TVsSAnZWagAAoY4QAI86hn4M9xSOTZa+Ox8P\nr8eBGn1PIv8jK0DLXGl3nt4hnWFGAw9gLf+rNgep2+YXtWTEO8VpSyhM0oS7\nQZOa2TMry+YTcu5SRLtJVXaBfBx8VXOlKdnKjUViCgBXCWuhS79881iPstlI\nPdMRDxM9gFYpP/qZ/GNr6xk19LJdgPoMeMhO8k7tW26K6bAxUeLrep1AxXsD\nrn6uGV9+YTh4+PDwdhOQRwp7Y7WJCA/Dcih+0IVe8khtJLK31r/xLGgGXbsz\nJ7sk/FvB2/PTbz5Vmi000knOQgG5zv75fHl3PvmgugsnHHvrWUS+almNO0Os\nhsNtEZmqTqjORSBPrTgpEoKQxbcdUYfSlhnjvzMQYIAG5mwi9Gsej04MgulG\njhNR/NOw/SMAhClQf0UC8deOtj19M2Hc0DEk2syDxrEvb9jXr10nHg9E9vKP\n4zLprp1vTUiviMY7N2ahddRD2YWCaChJsSPoH3Zp3G5jmVTOcfUJPgLBZVgq\nyxQYQHQIlFPgNtwfAp3WjL+rOTx8jy8lH/kVR7e6meZtt6r2b9YRhk4DsYb4\n3Q1YMJHxAZRrn6mdL30ZkOYofXTkC2bt8SJWbsiCAx5dvmva3IR1FZBqt1sg\nQyb3uUW+Ep28UPIHb2ThlL73+P3eHZuTNRAYTs896GhC7pOVyfxyRZRG3XD2\n5JfI\r\n=o5rq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDspBIslA2dNhWW4e8KHPVkc3WFSkoxdmmHtUJEJkOtYAiEAgbAgf62IEHylr4UUHLXpXO0OI76eAcwMaQQjch1WScs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-20180515213224_1526420439592_0.5628075884959605"},"_hasShrinkwrap":false},"0.0.1-dashboardLogUrl.20180516141325":{"name":"@atomist/sdm","version":"0.0.1-dashboardLogUrl.20180516141325","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/SJVsurXzAM","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"24154386d166355966157d9fe30347d469ba7cd0","_id":"@atomist/sdm@0.0.1-dashboardLogUrl.20180516141325","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-shW0u0srddZJaSptEpswyWAevHIz/yJFjHc1paQTRipNzpPHh2VNsUAQzctyedDVAVLa2XCN2TRbkLjxhoN5TQ==","shasum":"17237a68cc7fffa2bca0cefb7d125a2a13948e52","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.0.1-dashboardLogUrl.20180516141325.tgz","fileCount":817,"unpackedSize":1861923,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/D4WCRA9TVsSAnZWagAAqK4P/2OcGJTripsGgYtowUBX\nJHeS67eRihSRlh51eb8B8VgY9GetBriobVl8Sob3COMUyzZTcaPYMCzV6nqE\nK3ckY0FAynD0RFonFdA/V6jjwXDRpcN4jaWd1MZn0o70E01BjchBk5DZC1l/\nZNhTZrCgdzeWuQbjo8XpP7PERvyLTcrdjcQEosHDOXZ6a6Qd0qYpidpIRUdu\nGwwO01+3cXcqXNYulQg37cry1u3G4i7SpHpQxhcbSfOH9ijMFmDhMC1rT7G4\nknuo0lRXfRMCxdGGKLHrMVJW9LPF/4qvvCCGA09eSPwS2UskRZP7g7PHA4Tf\nFaTrWDwS+D9VqxlWULLarMN5Otp8xffaEAsSiYQ0nQ+mC2eVdI8QokJMzDzc\n0NUBSL7hvAcqKyDiDbMj8MeZZilCutvrshUUGSunez8I/q0XJm/ceEA5V3QO\nahHYpq+/p9tvw2Aain+hOXbFf+6PLpQz9co26QjNnUCE7l5qMk+Yd9mlT4+a\nfkDupbnxzzH0n6IYrbLOcJypqxdie1ljrr8uXWJOw4ACyXiLnobncp9tyF4e\nK5iq+HkKiX7JpsZxeziK8F9Se/3sd92vu6ZaWJaLhagawoFeF6S25yjRy+7a\nbw8FR+a5fS3mM7ItqjGsQxGhwTtnoRwsQwBIHvV7XsnwH2DzR3ow27PHAvD0\n0ACz\r\n=bXPZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEvroO8L+6eDnJLjytANl1kxBtozaBXFbvtqiuqrxUF5AiApHDuGGA0BRT97d0vKyuhzVjUSOzr8E0hRKkfZEuHE0w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.0.1-dashboardLogUrl.20180516141325_1526480404094_0.7383798832208393"},"_hasShrinkwrap":false},"0.1.0-20180516144425":{"name":"@atomist/sdm","version":"0.1.0-20180516144425","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5de73498e4ca96aa711b40183c9b80c53196a8e6","_id":"@atomist/sdm@0.1.0-20180516144425","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+vWx++Lw1XJ3AG28Rq+bhIUOem2qbPxBnqpChUXYpxVg+KHT+hdyuQ4oV6yfTMbDnhRe+e5iGrQVOOcyq1iR+g==","shasum":"a2a0c8fcf24aa22ab8e27d1346938ae7206a5941","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.0-20180516144425.tgz","fileCount":815,"unpackedSize":1858304,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/EVmCRA9TVsSAnZWagAAOnwP/3pySh5oN9a7Z3/k1ko2\n5+Y3VBOivP39m/CIpUVPkbJYpM1M0pWBgA3ocII22Nq7UkDYXwApWVx7rm8c\nJl++WnPP3d4OQcOfjEG/IHYSDRGd+pYyHc3+86q3zDdly95dDZ9zFDcjsDAL\n4O+jYU2w8QmrA6tk9Qn54QoTkhE0kQ6e0mUsch9dm+Ig9Hjg7YaRZVYa80NA\nJRKq5IDoNbcxUSPMyAIAucWRIzSvngEmyN994izSave/N1k1noLg2rz0hKmg\nxR6s0wsBLrN+oe8pj/0KgVGmxv+yF6THP1irI7U/ApairWGmon4+xPbCeJjB\nUibpFcqxHqa+caPpE43bjhE27wME05C5RoeKeCszysKseP1OTxThDxXzht5K\n3v48etJkW9YVLc0BhMpHO3i/1EdFJ5Git8/MtHa0QapUN3wWxmGC1jLHRbZR\nZvq6M4LxiNxLezKLvBX58/jgkSKx5HSG7tXaqdcECtH4nyXtoX327akBv+FC\nJ36ByuEMuNhkSDIqVivzLzVuDDnWosDsgQ4MYGXwECH9SRhp8NGyqBt7pGiW\nPxD1vpG3BQzSNivj+hzflsqaSS7P/ln3nuE/JrahGpk3AEGTEUChUR68VRbp\nPh/UkwD/Av9ENBRl6lG8qa2bn2tXMzzLF1Uzts74Zd9ectlkmlTkTuWKdR/N\njqSo\r\n=GNeC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrhvHm5sQ7LUbEm4SgCjD+9gh8rAUKY9kkVUiTdm9oyQIgFKNT9sx0pPSHOChvkVKG3eex6s4EJo2GUfG0NvsztHk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.0-20180516144425_1526482276832_0.8048309018607445"},"_hasShrinkwrap":false},"0.1.0":{"name":"@atomist/sdm","version":"0.1.0","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5de73498e4ca96aa711b40183c9b80c53196a8e6","_id":"@atomist/sdm@0.1.0","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RqMd/z8nJcOTPMH0lShSb10XHxuGMrbLF+Jty2k9fuSb4DzczLzT7ILYuk4wQ+SjZJLgObCxrhd8R2QbP3Rb1A==","shasum":"ee362e5c2dd0f8a7a63558aef1ec334d4a625550","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.0.tgz","fileCount":815,"unpackedSize":1858289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/EZtCRA9TVsSAnZWagAAYycP/Rp9++b4rM0DsIRLfGWs\nJgNEfUxftCndpjLQxAXKaKOPzLE8cqtV2oaqdwDjsgu0IZ4MgjCaJ2aub4Px\nSD7OEzfbzBiPhIZKj6bLL5+lRcpBmxlsTjtmVDstrfEbgMexoS0bjRXIxxJb\ndoWdqrHGOfegMXrHHXAHctY4NPDPto23uoTRk88lWC0OaIZq0/DINgRubEXk\nbgnoGUe4E6sFVgFYiemAneqhfNhFMOKoFnUUkXdIGGyFfBDJ9+mn7xTAkpvT\nBhUQSJ8n59TAfpgxG6Z2eKRqeAbEIOH9J9q3BCxN6GSxUgyx0SXmUgtJLHnq\nt6FiXAOT9YBA68LhCp0CNiDfD5o+gAkflNQbGbWYpNqNC4hyVgldZJfb/wv3\nnoGuiypojAviw0j1qFs8+VaAdUHSDzoBebxYGHI5RxpuaDeOnCKosqwtrxc/\nN9oQiGvXFcP2H/NBQOoPXAd2/wv10zpfImUh7qUIVZaGE5Nh4gqOjVTryOoU\ncLuK9UkPGABXu8PhqibcWBaIykJ1F+Qiu2WqerDBnHuCBAbAsRQ8Ae41Fank\nW08yYBcSkCOTlhCEeLLWZcC+6zh0dCPa6N/dlt0NQvICf9dNAXVXEBwZbJ0X\nqGGyLJ3DN5YwX6wVUbeXP9xCbBw1AHN4PKBXutJLPRGGcfsNaCokduGuCzVY\nqMlx\r\n=llUs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCh1HhprJkOJMKCFipMHCVQ8GEr2lEhI2YGq776kFGfLAIgbBiRu8OETzbqgh5ciIqWJ/KGSKh3BJZpam1Yq7uOOBg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.0_1526482539149_0.37158363380145554"},"_hasShrinkwrap":false},"0.1.0-20180516150239":{"name":"@atomist/sdm","version":"0.1.0-20180516150239","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"49735154b0a51e9d38b30ef8b2228b5203974664","_id":"@atomist/sdm@0.1.0-20180516150239","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zUIaeg7fi7l3B4M9K06LAa5mwnm3K6IY09rd2JtB4bP8Q2WoT+15OAhnZ+NPsUUe+fC5p7B59sZ9z3ZNl0+kyQ==","shasum":"f000da789b3fd090ee5980c7d615c4e1bdd77cbb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.0-20180516150239.tgz","fileCount":818,"unpackedSize":1862316,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/Em1CRA9TVsSAnZWagAAqOgQAJKoVTuOSDAtu56Atm6W\ntMc70V9kMh/OCf7PCqJjypz8V1t71nVvh4S6EJP6E6pO07Dq7nGuSkEm6y01\nsXXEGFmCu+GfkD31JpKna2AB4l1EfkRCoiYdUe1Eaggudx5pzwhy4vZNJ5eG\nMCe2mjdsxU3A9daMwhI/2PR76RAuugMyMx3OIbM+u4QjnTRVFRR1PWTHrzB2\n+7/qSd9pPnLQrvdlpXSlRSrArqhxg1ORcwbhRjCqxgkb6GOkAZWu5U6G4kgZ\nTuK2yFiO3OTC9tXi/19uTqc8VvijgpOG48z30BdHP7Y++fY1435E4W3g7bDV\nC9BT5lJs3ryzLlfzx3uN2ZZeroRLj9lzdRxOZEgTJCfNtb1eKp0asNa9nzzB\ntlZ/0AlYLQbt+AL/Jch4NnP1bzGV5WuB2L5l6Fkir6QFBVpb3xh/sEc460Dz\nwGlI9IR1e3preeR4xyGTIPP0M6mdo+aTCuZ0Z84+/cThmtb+Ifwgbqcu+h58\nYWV8ok9zy7CEQXbyvVwZyKuNqwZ1Hd8RJ+zzO0jJd7vlf/WxmmPqfs4fC/nL\nnqsx0+suEVLQIeB1JUsq+memEhm567Ey5dlicSAD4IcgDekqilyqZk2a1Rk6\nhOAZ8Oyr2O2mwHQS4bFUJLmMrWwd/QxbYJxZPU9TPuGIP8PVlyD75Sj9oQX2\ne8Ky\r\n=uIM6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMDJCJlG8C/XvXMre2A/Sql3fiM55MnqaXDii+b03RLwIhALwzi4YXHysVHPHXITFSjQBXG+Xa3x5W1qmzGC357Ahr"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.0-20180516150239_1526483379486_0.39220869223806387"},"_hasShrinkwrap":false},"0.1.1-20180516151058":{"name":"@atomist/sdm","version":"0.1.1-20180516151058","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e32d85ce0a76f522ff6584fc04d45fc142302222","_id":"@atomist/sdm@0.1.1-20180516151058","_npmVersion":"5.10.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZtaKUc4sByvMeykgQMFYWpqm1hD/uc8hoAldE00pkI6y9rnHZ8JRlDadhpcSBIAo9BFtNV3Zg7HcNqRUc4sTUw==","shasum":"d3271ad14343588143d52d9acf2338dc7a305e02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180516151058.tgz","fileCount":818,"unpackedSize":1862316,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/EuGCRA9TVsSAnZWagAAA2AP/jaSth6VvZx/xJix6EKq\nh1gf3rUzvShP0aWNe2WUp0z4mgqZWylX0v8moD1znGWiE47Jvc3EFfOfoPH9\n+Z6OsBqwVsPME0NdiHbmYU2AXoorNSkCHc1Ta3EXf2UdyV9eVMXUwq2NXwTQ\nBwopHiUlF3IMfSid1DiDP5rjXbcnyOCJXj88ZPVYdc8OA0jr+LdoQqPBzXso\n0bSOPzG5PsNgT6SRpkSmgZRlqK48siqgzARL6hKB2EpzsbmSw9W2Zh0wHFdq\no2NyFrugYTNHrOzCH9/9HSayS7tZbeqnd/eakYH4IwGWgWVfosvGEE/kizBH\naWBgE3SQW8z70yF3t4TCC2bhxRVz4YeZ/g0rqUsjR07V+mAn3OtKFA7gIgyg\nGp+Nd/69Q5QW1cDSbQOdx5DUMn6meul0FxmjyhVRqpCKv1I6ZxKB8SPy1ho9\n4XZkM9jY+2DGz/CHgk1JfiE+FAiX/nndEHnFuW4e+Rl+UqyGNGB8LdlFDeyN\nq1wamCDgBE/G9lNZCQBDvDpSCk0VSBrz1LWLgfji4hRtBor7CFpGFZ9XCISd\nUxhcuJED0dQtaXRZChPnVsTKuGSklq0i9PISWQVLABDFumwso5uwkoflJA3a\njqNdJ97ewIUmZF6WTNJm4Qsnc7tFUCFy6C6qIRGws93I5E2p18uJeY6TDFmu\n65aH\r\n=VBPj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA1jI/IgRPgqK6rz/iySg2nKTBgfGCIJH8z3ZJD5T/NQAiBgi5WXVRdexefbNTcGdOTyGEL7/kD+wVeW8VxeRl62qg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180516151058_1526483844715_0.3472995439616213"},"_hasShrinkwrap":false},"0.1.1-20180516161858":{"name":"@atomist/sdm","version":"0.1.1-20180516161858","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cc72e8799fbc6ec53faff8dbe056cbf740ddf4e9","_id":"@atomist/sdm@0.1.1-20180516161858","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-viRCbqab3CuYZG4QDsEAwbS3OqxjQYOP9p7BcSkeR/Yj0kBosbLgkCrE5BBMOrJZeGdUv1Z3DHtyaya3JrconQ==","shasum":"62e1d88c9f6a58707545413a26819450d7a87b3b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180516161858.tgz","fileCount":818,"unpackedSize":1862377,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/Fu9CRA9TVsSAnZWagAA+JkQAICzkqUr2OVhBxpaPf9J\nBA31CqG1yW0b5nJCMnLCMlg0HwTB2RwlvHqZZnou8AryK5dNs0kmIUDbllqF\nMMnGsCvslWnBkOUOPBypLcWyOViEGZjPb2LO6YOKbZXyyTQ5OHej6kRrL1rs\n4Uv+08QwXQ2xCbydn6pmAArBemHghYyQuTAfAaGigKh/FXOmMCHL2l+hboAA\nAWEaWvb7pkuT/w88GZxrN9+rm1fwqB+vybTdhZuqgKnSTc5snHRCn5PQBaGt\nTZrbOxpQkkk2f8t7+YDfO31KSNbLoa7B1BaCsSr2EG6mkVy2oaN7qoZjBItr\nPsjJolMpMm/vljjcXug8wdHnPEsOsRgVmT65iHyny6ESMazqFxnQRQyTPDCq\nRT724d9fykBxbzsKobEQSIPHNRNPU6wkiFVwVku5HEzdDyhMvVC/7TrTfFZF\nPOCg+BwbvfhE1SyTE8nF4BM7xOjEQ/XpRQ+kHA/at+eFFLVjANGpnhLGjiRz\n/XAHaVxq3ukyLnC0sHLOUKxGriNZP5CO/pTEz/lIurnc7Zff6+FEZbvDq4JC\nAfuwfDYU1IV01FyRZ5AuaolWEqe/m8YV72BBPq5Ke8Bshpm6hRSCZD47Tdiv\n1Zcuf0BZdNozIoqv7pg4rc8hFPFdf0OAppC5uPXdkereMbwVdgZW7rG/2KNc\nW81I\r\n=tNyA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpUvo2exd+WYiIFzliDHWH1Hk5dU315HwCwolp6Bi2lQIgOCE7x62AFGSaEslru8BnkKNvEfbKQpH3ObAkRIBW6FY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180516161858_1526487994440_0.4524069717921866"},"_hasShrinkwrap":false},"0.1.1-20180517014316":{"name":"@atomist/sdm","version":"0.1.1-20180517014316","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6e192099c7eb07cb4fa214f05008f6942fc768b8","_id":"@atomist/sdm@0.1.1-20180517014316","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4tHrJ/vaBrXm0mdSOXrLvOpjZUwOqu0/rNx+/EaguPH5GoIZ73wpGTYMAr9NypsMasWsDWrp5jKR8b+PGK9V2Q==","shasum":"bca21e396aa49cf6cd1334e4916cad10f6ce52e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180517014316.tgz","fileCount":818,"unpackedSize":1863046,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/OARCRA9TVsSAnZWagAAeRAQAJg/k+5wO06pIt1Q3CqB\nkjRYttAcjVa26sH6A9gDKMIyjgyMBIXH2KcralEiJPS6/1+edfecPcpba10C\nrjbmS/o/rFeB9+sLXUBqNn9rfFlN3MSwRgOASoLUCrLCJY2rLz7T7KxkUWql\nVzNc9pfxbthJY5vG1XM7hOLXJQ39XtRgMNMseVzsrMNltXFbvRXteJUapOfz\nsAkgR8y473GXNMEIMsw5FiFlhJ6ScjbcreRTpbBlDtnzGSCCBzPoT0XkmXqx\nsupI3O5NvkPx4N7vQ146knBi2CQiMRUHlhukUZYW4XBx8GNVqSO4z+qsvybd\nzkXOt1qMTkEdDArljmpy7pt3Fy+sZgDex/w1lSVjJ/z0A4sMcvB+DbrJwC08\nQyCxk+XuHE9A7EilyC2h7q1vatlsBSTuLy4G6ozBEKGhm7GBNmbLcyUZKauc\nSIAX/8XPWYfg0Cp/Rign2LgS08RjTl3F0LZPjOAbHotRuz4r1GC4EefY4Wmh\nQtxmpH90WgGYHEwZ3O5/Wl48bLNKRxj0Ysz5KAswd2joH0kiYd6g6xf7wKV/\nKhFDezL+E8kHSzX3ao5o2Pmd1V/8LxaWgUAqXZYgxiWFqiTNV0pNhIqKMyN8\nr4c9hQiZapgKfPZkFw9k36aSxRllK3lQGaHAoL69IYkiHCmJ0AtH7NkqQbbJ\nIs37\r\n=z0gK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKNo7UbcjyIpSODEFGIjucpM4AT3sjY6HlewGetZzdPgIgd7t522lRj6QUDbofxHHJ8i7Ds3d1Iy0qKaxTDwHzwCs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180517014316_1526521870722_0.4305433153358562"},"_hasShrinkwrap":false},"0.1.1-20180517023538":{"name":"@atomist/sdm","version":"0.1.1-20180517023538","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f06ebb98ed93b4f7e91eee24066042d970ec7ea0","_id":"@atomist/sdm@0.1.1-20180517023538","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PTopwIN1WqPnTb5uLeQqO7x/IppGLUKdLK0LPzB1RSg9DA6wsxEZObMwgC2uiRXO4tWRAbrDiN1mVexUp6dMyg==","shasum":"054458396cd75e36318885cc41588c67b9a1cae5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180517023538.tgz","fileCount":818,"unpackedSize":1864036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/OxYCRA9TVsSAnZWagAAwvYP/1X82C8SWoOKoqwR0bil\nOra1+z6REauJp7RrTmwm0OhWU+0riwy/ux1pYVjH4ODHIpGUfY4KBmvdhLYR\ncSDsSK4Nj46Xb3I0B3ewYhrfD9B7AHQeu2+Db3H+WhLstq4cnCO4pbDctL6x\ng00OWNaDp/GiOnRc8bBlJmeC/diDV2WLi0+X0tFhbDYIyccLsppYM0wQksXx\nNSne0J93wGU2KqWA2t5v2m04Zwyi0NQKhMQqVRcpfD3RTW/rfxZVstZVpd2x\ntRVj1/irAOuhgkz7yx8o0cpkjSEH9j5eCUxS5wcYFw1mOKjnmTupSNV8kQGi\nvzTFfIG06WbTaTxQatDu/qI6ST6/MEcriYAghjM/T0jy4PAwVkaya5R+53IP\n96ucemNgfgqCbXie8MIh2w3xmC7jEdYPf1/W/dgGjSgkQGnUu16F8toXgcSq\nsBQFLAHB7ZQwc9y1HE5CJ+dv5GceeCkKMbXOPSGvT5Rd1z4anCTBf4z5I7cD\nAIrGGx3dIZUxar+t/boeWKeZYAwUIzEOg33hUZ4gIUCyOxZP9PXvOT8pMExT\n4u0FBdQzapp4vYGHzkxs922mMOZaV+xgdiIZYqDDpR/4+5SiMgu4i3YvMiJC\nQc3Kgob+8vyTYKwsQLv/h6SoHrN6TjbvQd5FvuTLVlAWHGfpVgo5v1vQDxNE\nBB3d\r\n=RWpb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCR4MFfKijETdGSyj9Tg6L4B9YwtkvS1NoBFFuPvpow7wIgGwBIYPUBSW8jNLLx4+xIWPNRs2y3FJ+SnYxBIfdSEz4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180517023538_1526525014688_0.161774111278294"},"_hasShrinkwrap":false},"0.1.1-20180517163727":{"name":"@atomist/sdm","version":"0.1.1-20180517163727","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db4da3fbfef08d50e7f988f310f642630de7235e","_id":"@atomist/sdm@0.1.1-20180517163727","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oiRnv2ncEQTXHseCLDDYibjV5TWBWq5TzG9GsocboR4I6i5MSLuYp+HDW1HbCtXSaVvuxh1XSg8ha5LrvwKJxg==","shasum":"bf2b9e847c3066b3511929541ed05e7bfb6131b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180517163727.tgz","fileCount":818,"unpackedSize":1865009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/bGgCRA9TVsSAnZWagAALqoP/igVm7zlHfLRMPOOY2Jo\nnFXO0aZxRc6DX5cvbSnz3uFvIcRkMkMBulTGB6aH9LZ7/1wgebAzZ5e7PwEp\n/Ez15WEt42kFxgNwUYGsByreb/2pCjziA1xz0Ukr4rPnhLsCTgbjfUxQ74cs\nfVem/bxt6uoCnm2DTdr3D3RHGuFW3llibrw1BRIdoU5ZfbjcIgB5r/Pic40N\ndmjpRgzifdkj15dgVtceu9l1jNEJet0Gu3p0VWbdEY2RNQlFg2ikFTPWuBc5\nh+T1fHfcHd7F72Hww7+s+uu9zW4jFqpSH47PTU4GD4q+Kyx+vYZVDae4bN2s\noQW+TtOeZ4eodRMCL8MyZs9Wq3I3TbqFHbfKGfkSILL5NLbzlvWweLrhPvvt\n7P7pI29vHVKP4lMUj7KeGCTnqmun0KVP6iNpA/DwP3vZxefavY8uExG8KBFr\naHcr1TL28wrRhKlEMkqXYJH275ccK5IKwXNFBXLB6qPGhF0G+jCtUJXMjOm8\n1uNzWkrtGXEWg3CpLG7u3lMRkaUey/2LNnTEfqKGlO/a/JbZvs0++ihya5JE\nr7qbly6Aliw9JgqSVONPB8xbdq3TLloMxAiNchmlmpq7KaNYTMhv3HUtHB7b\ny0cp70oJeLzcswe9YR6lI3JWBOhQqE8eEtFuf4dZ+I6nL/Yx9/lwz8XV5dEh\nW2mM\r\n=wdWW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA1NfKCPkiTM3of9DOytPUgasP4NwWY589vaci/2lOrZAiEAuVd0+13InKcz48GAxt2D2WyZo5iUxb70trzNbnv7PLA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180517163727_1526575517538_0.5024089072016518"},"_hasShrinkwrap":false},"0.1.1-20180518072833":{"name":"@atomist/sdm","version":"0.1.1-20180518072833","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1d6bb7857fe3ce89cefbd9328bebcb604e50fccc","_id":"@atomist/sdm@0.1.1-20180518072833","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xRjgSOAftHXIxtMVastB2m19xduk9S5EOc+vk4Wy0sv7YpxQoO5MTRO5uPSZSJ9PrwzVJqgxWI7cFz9ha5LHtw==","shasum":"a343f63b6d0d02c91a7c3156d10d74a00946ff56","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180518072833.tgz","fileCount":818,"unpackedSize":1865760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/oJtCRA9TVsSAnZWagAA1p0P/3RgZDEXccuYlJ9tjD9F\nnxc2mJOGcZP3fYx0lMrypvieZE2m+fpAazoVam1n0DTxZSmh7Dr/wWNn9dPU\n4A6HCfCcmNaKiD53Qaeiyqr4tfREhgzLfZPev2mwdSS7tANSGaQwC9fNgxpr\njDv7fJiGcKf4WQX4j5Lkq53NSPbek39ekPUoGvn0jUHjGr/JVp28Jq9Tt+pV\nCvbl+qT8TUtut5VH8xnKSY3qw/7aZ9lRtOMbFH5ZiWzFIDtiW3PNGdU+tGDN\npZDNjEGwm4WiWIdzsBnuPbZCNRRJge1E9r+pg3PiAuqg29B0uIxamgoko6zT\nWpuBTlxrq5ddrNjpGMJYfHYfK0Lix7eRWpXAbANMokv5PQRkuEBGNHeCh8sd\nr10NvWtgQ/AqEUwDATyEJsSKk5FwsPbkaikmno60of4FgbT+QykkBEzxHAMZ\nQk52FNo/5DmXb2QePI2GRzKSlTA4TeCL4alZrMfKS0z/kDDSoF7nUBM7oaxg\ns8Sfg5M5auo7NrdVDItS0Wi7KEr8caianykLL61Es2m9UcX9z0QQbe2ygdUO\nO5w85T+9BAi75jlwE/tXD7WmDswRq79OEixd3l/L4krCbBYIkqxCQo8vnHf8\nRXczAyp8HHVtoW22LXXghouazyD23KMFrMoyv7adAy/GMXkfqcLAy5/aipXe\nFmYM\r\n=/ihl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICsiDa9S0zDxGVdtxthXrpw5cqfIHMGloZJ63boVCb12AiB4O6ePsriJhVXcN+OyCV9NhnkhzmP2US/UHCvQRI6ZWg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180518072833_1526628971025_0.3570783052236213"},"_hasShrinkwrap":false},"0.1.1-20180518081018":{"name":"@atomist/sdm","version":"0.1.1-20180518081018","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"98839230b048969c6a25fc02238667e3f9034ed5","_id":"@atomist/sdm@0.1.1-20180518081018","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s0PIzMjGS7/cs2/HEmtqW5L6DrmIYZOdOLT9lkMsSnDO+jw2GYK9CnC/pv/ErqMuu5e0Gwn69esvbPwiFYhAWg==","shasum":"c90228cbcf0726764609f53e500112594d1e9805","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180518081018.tgz","fileCount":818,"unpackedSize":1866960,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/oxFCRA9TVsSAnZWagAArqMQAJeGRWX4F+svqi6kSzG2\n0YpoDhZM/icv6Wn1/sJIzsb388BAS4PGF/5oIsJcOh/R34eGt4UzM2gbj9QD\nLJApVyLqZB3Rk1bn61fVr0P28FZagbooEb5yOPqAyYXAJ/TjvnewBwboIMph\n9tnCQ2xZsfMsTJrLbIfxp3tqezYNlNv0PFhcbsUbXa9PfQb66vfEb7yAWsab\n424R1uU9bj/8m4QAFL/3U/lkVASvsKGyiP13qbKCRkYY4IyMGx7bUP+UxrjH\nE/FE9Hg2DCu8lEI/LF5VvXOo6b4FVtDE12CMYyMtyKMuWHs8DRdPiz/xOpHO\nuAFNhGoyF7KS7OM4jdw65VdARDrLEAZcM8nPfDn7y753XfoNdiR1O4e01b5k\nDbMY56SXyJemAH9FJj6RLDvuMYcREUgP59BJ+PIwu5XaF9D0s+xI6XbnIo9e\nBMTXgRG0pWpKiVg8OexWRfbvnLnmcbEYvt4e2xdNo/6uWJXXtJOEHTxI6Woe\nlc5fpY4RreMROHz0vpmSbqfrO7qy8Yd53Y4lZL5TnyrVeZ+4EGxctGafH2zH\nGLUe3fatTDDsrsk/9JG9AtHQSagXCsMEEnBtilx65Mb43LzQNUsxDbCHaRfm\nk2JTkxfbtZHrzKvI5QiRL05yOuQPyZca6xddTjXUJ8p3JWn6GFQNVZZXq4zX\n/LWI\r\n=xDF4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5CN8gwgDgultGXQwrA1oNyAKphkg02tocaYBwHV6b+wIhAKvv3aUOPdR7tU+MqunUtQZgILcoki2ienD+5M++D/Q/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180518081018_1526631491238_0.06774752238134574"},"_hasShrinkwrap":false},"0.1.1":{"name":"@atomist/sdm","version":"0.1.1","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.16.0","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db4da3fbfef08d50e7f988f310f642630de7235e","_id":"@atomist/sdm@0.1.1","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-379dcQCGIjYc6rdSOIbhpjPVVa1VDnGgyPBb/D5sXwwz169KS5QWxfUlPzqJxkWkBx4QE0niS6SMAQO7K2mgaA==","shasum":"c64e3123714f4a523d2e962253d7e7cc5edb2c26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1.tgz","fileCount":818,"unpackedSize":1864994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/puQCRA9TVsSAnZWagAAiCQP/0YE0eTe7jLUQgZe7B5Y\nF8lN7dk5TMJsRcCjl9Q5fIZ1b8WMtBCDs3ZFAvB64U585oTr3/rSEzABlp/y\nTH8Tfjh3mLwnCW/1nMMRZWajEYxPu4e77Xp0ALqvGPln3TH5tQdyf0dh3/9F\nLVrD0TEImcCuy0hD0m3Olfe175Bi37iOIRJlDcPhJDfc4qvMDk5nOTxjZ3Vp\ne6TLWVWjqS1sAk9v+8oSAoUFKCwX2TZ6WYPMJ7/v1Dq7I3sV34FT0wHgUuLn\nqTRcihBIVMPa30c/S8E0On8BHRWbpw3tyye2VW4cxtAU7r2ycFqRnCn/Hj+L\nrPtHw1GY44MCew5xHcafREIxUKol7AEmx86d+wB5RWpkXhTEnuwNABS3IMIu\niI81T7STB0ZApTP+QM6oWkL3aGLuwq4DpCRv/xjmW9LJo0Mob0t4Qex6KkDa\n3iz7/7Iz5O2dc/xIjW8WmIxJKo1dr0DNHDSF+NtpEd2yUrCgv6QUG+7XNSdE\nz2+QA//v206+nWHHuuOMIZ4RUSLejiAACcktgan2EybP/durSvENufcXYAjB\nUuhEbwe40cMuZOd+NGUYWz19WRm9EUG63div5PDgiOsUaaiEsEQOj3FVQ1s4\nnLEDqZL+mCRT/0cWfA3Vpoo8Ac+kcHsh8k2ViqFT4qcAJJdngPPcXHfP533S\nhppi\r\n=LQGh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEwXt0ZkZTftu99kYiG8ibhhTFA81bvH0Fswnf83AYLdAiA23JdbcIQskoQ6+nYpFAzUHufp51CV88GMrSQLXGsZvw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1_1526635405008_0.23348179274399383"},"_hasShrinkwrap":false},"0.1.1-20180518225035":{"name":"@atomist/sdm","version":"0.1.1-20180518225035","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"02dd12f5e1e7e1aced63cbddd89fba4d2dac17b3","_id":"@atomist/sdm@0.1.1-20180518225035","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uo37pSeBXCd7twa6fCL7m27q6ml/OKH7FtkrOlleLRLaJUaRayEaFOw74n9xdQJMtaum3uoieVIEEGHe2vuVMw==","shasum":"ab0e4cc36c9a4da8187e99a92477fc7f56d88ee4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180518225035.tgz","fileCount":818,"unpackedSize":1866986,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJa/1p9CRA9TVsSAnZWagAAaT0QAIIKV3tu/6aWfqizx4c9\nveAREjTN4+3R6y40NZNDKmvFq04eCgLGT0jLLana6UaqzalQ+oARkcVw9XMm\n7Y+zdn/NwG0zg7VrMKAvZz4tiLxxc5oNRIRGpD2h1QYCV9secqRtJTcUjJUf\nbfODmYVOeiRtb7s5dkp5ozPu1WH0jDzZkzpA4jq5E5VZoabxWQiuR1PwID2z\nPOopwLN7yBLz5gIbNhFoxYOGjDV5QCJxUrrhXvKIILSZnKdxCHUjYrmrjPzz\nMXQV6Z2kNq59RpqsM3w4TZZ++IheWkdUyhCrocOg/WDVSZEKb+cpHPTy68UF\nT2Nucd0rDixskFK2mKdZgbOebP3d5WwsJ0dI/DY0NB5y4CitXmDacvhMXL1K\nSg21uSD1lH2Y6nh3HrZcvVIiWvGToOk6VlxdOKFe/sJ4GAON3w6gPaC/l9sR\nQCIzjtzDkXB5j0Q7PJkn7gxEIXMv3rwxht3sFITctYtKSSL3O4sUS1u6andb\n6T/moIKtcL9QYGKFSaO6hJ+/sPd60RvHvACIqCNkPuMlv8fTszCVpY3zek03\nPXGiIJ8RdsmmO1Y0pwySG+7mw39F71rFWNdOfynYEzklCf4thTnZP+NPzlS9\ndBxu8aGqtXqM/7kp0tihbhgupB6AFsNoXZNQbUtup1tFzqR1QnMPvxKveaJA\nEa0D\r\n=pJ5b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOheggiqn/qKv9C0NYRulY6KQQpYjgxyq52MoKMN3lHAIgIL8oZr2G6Z5gHJ/ZDxhC6D/JRB0wLZgL7ni7uF8MZKM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180518225035_1526684283922_0.232523985705809"},"_hasShrinkwrap":false},"0.1.1-bb4.20180521073510":{"name":"@atomist/sdm","version":"0.1.1-bb4.20180521073510","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3642dfc9e1d66590a15d27e88cab5a8f108b2770","_id":"@atomist/sdm@0.1.1-bb4.20180521073510","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-U+c+ZHupuT3GIvqw1o8Kkn5M7Bi2QDMvqw28eYcMgRuAWi/1p9CIcC+Tnp2phvEAvWfbo3Lts8DAgftSrrZGeA==","shasum":"4a032185725c58c0269435defbd6aae3029cd2c5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-bb4.20180521073510.tgz","fileCount":818,"unpackedSize":1867941,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbAniHCRA9TVsSAnZWagAA4LIP/iUfm9bM142F63nulgbC\nRwx3QKg0257ipOLAwrWJff5AzQI4Xx5tU6ITLOilZNox7QXEhP7GXUtjEJoO\nsLeQIjFIirX2ehMdCE6aHMGBctVXQls3eqoWdK3HLfEGQAns1kgPTKxwhN88\nY23/h5l8fb0Fzm9QUMWPh/ak5FWN5B47o8S0Qo6YH4KQuySiyOg38A12dTi7\nhlVg5vkhU4bwIYrM/xUYoNB4OonCUUM1g2n9dvOi7DnoLoQDKLL9tm6hQLEc\n53a3CdFlvj7mUW8bfWusBEOTy9moyZumsABN2p7ReWvykCLbFNYioTfVKb42\nfGM7DR4ilDOWOIkIgseJRyu1oSu7kPkR5WezufY5pE4QmZa21VMgq/rGS7a+\nrHsGmhzTyj+HVEkIi1H+TOozd0+Sd1eKxZrWob4PtRVJ520Gj9tJduKYSa+z\nu5qYtwzGFazOZoRWmrGX8yzYqyxXkJWEUJQ3NeyqDHPIRP+5JjmKyWNbTjTC\nLurJ5Sp1ieliCkpYMvMuehsFAAoePlOj9fDdYhBvEEeTETEgClH8MZlBfqxt\nqanxkUMl5qD7OGtBTaNmdkcbYdLPG3hZdpUdZ8qEXDhFvhS2zZ2MIlICfQCE\nyHuRaoRTYwrxkft27EE1cx4j6m1zzhMMPLVdBzf4sBJRai3LGEGveuQ2I+7G\n1MFQ\r\n=D841\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICheWY3Jpw7Qk6yBsiuHvfCkFktWYpsyq9FPkC6wAjSIAiAsvVO7byhe5FHrg21twyeyFuw3azM8iQ4nD1UhVpMK5g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-bb4.20180521073510_1526888581221_0.6065438635201954"},"_hasShrinkwrap":false},"0.1.1-20180521075352":{"name":"@atomist/sdm","version":"0.1.1-20180521075352","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","@atomist/spring-automation":"https://r.atomist.com/Syrsy8R_9f","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b890818c624086beb6135da8bc2acab8698eb9ce","_id":"@atomist/sdm@0.1.1-20180521075352","_npmVersion":"6.0.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MlyClJVbDd2x3MnH8RgqxC4rUr22CZPgRwbo5lPPp2P9Bg4IAuKi/5hDr8MnN0Sx+yQif4aZmDDCi5THIQvTYg==","shasum":"1fea2e223caa186f679e804d55e7deb9d382a7ac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180521075352.tgz","fileCount":818,"unpackedSize":1867937,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbAnzZCRA9TVsSAnZWagAAq8IP/1LXe4LGZ7pA2uRr+dIu\nmWfqyibBvswFZNjyMAOcgFsLiP7vM6AQWiZ8B9WN782HX2l1ZBx5OjjVh+pm\nCM8mlXkj4631ypwJ+ovmwwK9y5no0kVOQrfANh4fVJE3sp+7wXXgvx+ytJP4\ngzl63RoOJHP9zvVovzCSg2UgvWiWNqMXKV+2myzRhoGGrcwJepv52v6Z1Ldi\nasgqPTugokJ1f6tvfx+bBGPQGtaBBZTQAkEqIs33PJVNsoey/kZjjEY5fq2i\ng5ijNx3XSrOSjJuuAZgLgjcYdiRZB5uXyydQRI/mPYRlLNq2/7DuMeo/JU62\n+py4wF2LYTkgTfIIO64IhTicsLbCC+Cebf5+En4XbVHJ2hpMAlYGANJtOCO8\nujbsX6lmb/Mb6mT5ZGW5tw0+evmyZ2DpdCwmtmmCaVDM2ro6iQRKX/zNSoyv\n/OcmEHmJUz/tFQL+oaJpYhmJ4O8rSa5lHUFNez/i+Dble1znzYasFJJkDaYg\n9Ln1JM2DcfrQpTjIftE+/RWh5191hxFCsfxrytZmMNyJYiD2C91WWPtobisd\nIJRMyGGIoOM4ovtVCqgH+pg+VQFAEThk1adnVRqkFDyQyxsLOcZyY7BYAosI\nPExoRFfRUkPVBK6WblKzSlsg7M8icYdNf/efKPJTyI+w5KgrmNQitUtrCMKh\n/S91\r\n=a5zY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRNu0hSgWwfbq23IsxdKhlt6XTdInQ3ZVahgN89OTKQQIhANxUH7tx2ulMr1L1cJr2cZ1aReeIrfBnKXp8gCjhe/4B"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180521075352_1526889686467_0.959940676320028"},"_hasShrinkwrap":false},"0.1.1-20180522055644":{"name":"@atomist/sdm","version":"0.1.1-20180522055644","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7ba04838a72cc8a23ad26cedd4ee6fb439dc85eb","_id":"@atomist/sdm@0.1.1-20180522055644","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EbQF++g9Q4CREm624Tv1AB+Rw4IqycRCSr1V30ixD5MFzoSVZJqjKE+xDL+LuRlitiAQfeiTZVFn7a3RlRoRgQ==","shasum":"5f31306fa0e087ec3f2ddc1c770195b9eaa08ea8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180522055644.tgz","fileCount":821,"unpackedSize":1871210,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbA7LGCRA9TVsSAnZWagAApuwQAI/S37i2/kKQXz0O3RMq\nRAxezftML5wZq3MbInHgIEgJAf+UZBvtDITj6NFBoM/AlUO9KPWNdvE8IJVn\nS9+ZmYawCE2XGMcOHcnUv5whxI9j/EUz5mJow97l0nhCCdiq1D8RYUuYxR1e\n8tgM8xi7R5z+bsT8DJvcKmP182dXA4rbcYTTvxyXa4faDAQZo2ZZVSPEQXSd\nM3eN/+ayeoxqXdgx0/4ewaJCNEpy0HgFfcN/dX5TXFytsMB1vIU+a3wB0V8t\nUDwUS8FCTIoU3Yg9lt7BSxkuweTAN5jHHFyyPdD2+XHRsgfagwu5n3jpAPOi\n0v8n5Nh1JCiIYetd5DSgZgnsYmc5MnqbjQt1FBaDOPKnlgwoo866umznsdew\nVJVURY0RnyYAeBMLkZT1zTrKF6CK/XoCSA4NXjq/OeYpJvoL1kJHyr/S2qSY\nDbavV3LLzKHQAtwkGkVVn7fOHvhDaMz0V3p5dM3J1AvmHaHUIUbLnzNLbs8n\nbZD288xsi96d7oGsqoho3j8KB8zVPvUo+A8CEKqXen/m3tEg2PjzBRl1di97\nCfBqVi22btY0d9exg4Hb+pnusQx/JxSi6BT7iWMQzLhTHRlRcxGxhke4qmlt\npizqC9/Lqkw7llUNqVoYuIlDnIBYD5labj5PHD4+A0hmbpKK7MppFUvzhmsh\nuCgY\r\n=+LuP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICtN9oZaAayKjP4Bj8sEXDshoZtupMtiMxj10i3TqaOyAiBlL6h6m+NGIBpRCVQAZDfUR/8NGqcv5UHY1p5u5lsy5Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180522055644_1526969029334_0.8793498489254579"},"_hasShrinkwrap":false},"0.1.1-20180522085739":{"name":"@atomist/sdm","version":"0.1.1-20180522085739","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8a6d26f3c2c0dd2472ba7ec2065103681aaad0af","_id":"@atomist/sdm@0.1.1-20180522085739","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sgpS/+JZN5bnx+KIoGgT4kDfE6V449K2Lupahe/BHfQ/P2PgtFKdRkwUDWh4p6BYyzHAaA/GmmyVFqKR9VpCVg==","shasum":"0fbd33248c4209d41fb33a59190009ba677ed597","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180522085739.tgz","fileCount":821,"unpackedSize":1871467,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbA97WCRA9TVsSAnZWagAA5Z4P/1i/6B5VPBmuuXVJyWt7\nopOD1mwIxeblsyxs3/hcznUpU4b9oOIRmn0XICcPgdgV7kJ+v9UFNxNPJkPx\nnp8TuyzrU66GoYtyhM2UOG8yo6cPMNvHJNFiZeD3fzOchmn1gsSFyaI/AbgR\nRVBMHO6+ZDx0Zlade50fIiNGQASC052TNfJazuFetszCFHggxOAfCCieEglv\n8+QA3smJS7mtDKSm4fx9JUrVTlVBrPeBdl/di+S1hPFDpZjawjE7WQkj8cj1\nkuKXmnLlgEerYtHt01WQb/MshUZ/FcwE++0Ia+e9CNPOiXcPjGiJt3+INHUX\nIwG4JYCNuRuuAEqZsZOTufoGhIZ4YdX9/40+tgaKtdacIRpZKQRMQ/jbJPwL\nCoKJz8m4WXVrc40PUBx957qUsmfpynaZeg+AKIlY6cCyXqLX0nRCfWM2NQ4m\nH1X+ENWwTXMzTwI7EgCQLHHtVHj9b5FAWqeRAiQwe2x4k7EkbKZzXkwYWxfG\nKxtlJbX8VlQUaO3YDZ9DGKdKYTeNiQvOUp4UQAe6SYoyXI49ODoQHvH5Ruhr\nwN5CA6Dv94I6qITYu3UQoYFZY6REFD8Ld1Ua9CweYvD1BwZEWchBrmiIOMjR\npDPk/HvA55DwjmlMqzgr8ddnJfsklP8E2fzs8hXL7fWnYiw1Dk5iu1ISy4fs\ndcWK\r\n=EnH3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH8x0N8aWSrIWm+LQkxKQEtCZlUoT4RIhX5nYvjD/U24AiEAyUVUi/CibIquX2SbU4mppVfu8L2Z2ScJZnkvIepa+IE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180522085739_1526980308069_0.4735844006766279"},"_hasShrinkwrap":false},"0.1.1-generalize-goal.20180524082820":{"name":"@atomist/sdm","version":"0.1.1-generalize-goal.20180524082820","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8f7a028e953e23a1e14e87bb9fd6a6904e226075","_id":"@atomist/sdm@0.1.1-generalize-goal.20180524082820","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MpWbi9jgBoVvLMQpBs2BzZXnjdkEupLHdico4jStAOsORIO2h3bH40j0NG0b6wGox8zp5BrSvDFJZjuENXzkzg==","shasum":"1f3948a16b2ea8300dc93fcbebc1428120bde7dd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-generalize-goal.20180524082820.tgz","fileCount":830,"unpackedSize":1877666,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBnk4CRA9TVsSAnZWagAAXRQP/1cagVy24nRypmpnCcXV\nLXXxCOzwoFYAzLwazZ6xpQ+vqcdJKDtwTqXUlBMs24yr2B6QuGH+vMMrwZnB\n9YM+bpFqMP+f33xG73F4aNsxV8Ai0ead2P2l2ITViJtLmCr/NU8BnlIAlYUt\nYFJ2w1AiM61cnlBVBtIGUgqbRXFWFiW/NByWXhXX7lQnQpiVhJM6DsdB9P7b\n8U3YnKk7uclTGWi7I1WQJrbUfMTn3IX57THw1SZ/zZHCmTLDYZ9fJ2s/0pQJ\nt1HumQKHN3tNh+0zYkQnQQgtzA3fByDGmXKLTkiMwcHnKtKHHLyxxwgVJM5L\nt0po24Sq2YOCHXFUl12R6TTUedswWU6IBnSlc8CX+1vXh4kQDTMPFkqJUmPZ\nfTis1pZ+y8+O9/mE2Z1Ua9cbihvPd9fFjrG++MwxGCH6tUnv4r2nBwcIgVTn\nw1NtVzBQdvYjTL97/HoOyLJeXmb80+pceGfisfpgKcwsaEuIE7japAsxzaxs\nS0ZgoJ8h6ncVawW4VpAQvzZSNm1mSdpPwpjSPnPGAw6G4DV4+9YHSZDB+IQs\nALzCH9B0veQGn0vlN3zskLEROH2yMq3C+bH81prj2dPjKP52QzWSrAJYZsyX\nj+kldgqFgMyYwERCSTwo7JB1pPK3EuMdZ83Ep3aNszd6InLCcBTAA9PvHgAz\nQE9j\r\n=UC91\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDtKewa0oURKzJfe4ByjLpTsK6FN2pHDR5MRn87LXUF2AiBsjFmSoKpMhK41EjgkGPuw7Yg9HrulH7b1ql1zjoHl5Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-generalize-goal.20180524082820_1527150903262_0.5776403703216209"},"_hasShrinkwrap":false},"0.1.1-20180524085115":{"name":"@atomist/sdm","version":"0.1.1-20180524085115","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"966e2a37489f9136d611048148ff15cc1533bfd6","_id":"@atomist/sdm@0.1.1-20180524085115","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-J8mdpuXkSmIFpAyBwyuxIx2lvSZ/PLpLqO5+Z3QNc2aWEx3KmMsu7IQGUmSPZYmbgjVtfpBIW6P/KetPAEkAJg==","shasum":"2dbf4a85ab830de6715b1db8b4097963e186e69f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180524085115.tgz","fileCount":830,"unpackedSize":1877650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBn7OCRA9TVsSAnZWagAAzpYP/iBYlKb38WoXoPkFGTaN\nR1d6Kbk9cmKD63l/yvYWHvDq0L9qWvRkn9Os0BREfq9X3DTyMcT0m1w5rpzl\nVgOFrqZvrkXECRQrGl8tq6gVZYBQg+H4LS/kH47DLJ4MZcOTPOIxC1mxswLc\nFCW+kSCSaEY9ungXTpOxUenJZOiz3LwrOlBvcBfe1i6af5OB9+1UeSGr4TDa\nERktZooXVU0pWBiSAXgqOOQse9wX88SKW8zhcWO1kK1UQkg6g7+gP/5f0GG/\n44YBqpCKFJ8tmrZ8E7iQEatPkt5YMxExQmDLhm0zx2xwV6Py2UptJkTkhB7Y\n8gH5i4lS1AzzrO8+5e58hjgU0kT1uTaAjBrAhGORJ7Cr3WnLK9h5BIcRSA4o\n+OJqNbsBoWrXQw5oZoAVHB4aqXNJtOAoUQfpP1SUyhCImCsBkjPJLs9nlH7C\nVBHHIt2uzqxAulSSOPvpswNpWXudKuSRHHfuGEfojOnSdB9TUU2Cf6I3W9JU\npxGqoeHYo15nmnj4uyabUGTtwAGJP/lr6jiZPs/w/Srfqcb5bSqCgWdmw/er\nF06jnK2xY888vJ8QsEDY731Fc3QyfUNyGfF8VELDIBDwUAGrR/2NTZ3tsKOX\nma5v0RQj6uTEibZhziUsFXnn+X/5XuMr9/46vOqLadzZfazSvWUCtf+iBCsK\nQlU3\r\n=+jhB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9FupnUkvqeJjH9h6g4sNS5xdu0tSDfLflbEizRdB5FwIgdZOYxIYhUHC21wOf9UqSnZchfVxN2rPAfDCMwCe20+Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180524085115_1527152332275_0.3775251339823851"},"_hasShrinkwrap":false},"0.1.1-20180524100532":{"name":"@atomist/sdm","version":"0.1.1-20180524100532","description":"Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9a8c7cb8319ec71f285d826652821164b44e8b1a","_id":"@atomist/sdm@0.1.1-20180524100532","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7Gshzg+fr+cZ9r1rrVDVL6/6IVaw2CWAgB71GnuXrini8aHRYJfrakfdGyOKRRmTiESwu44qv9sTgLfB2ecMhg==","shasum":"ed0b52e4e41c32060a190918671741cb47e62693","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180524100532.tgz","fileCount":833,"unpackedSize":1880287,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbBpAECRA9TVsSAnZWagAAWdYQAJjoIyJvFN29fYoCOmpU\nhH4ij0RpqMwnsj5cMgVOm6TLoyEa49CIYQcHmLjA+tIFmEZLQx0NVdkqutMS\nzhykhL1JTt9Kv+Mt9pMxG46AO7Gr6Je0mxObGifKzVCiE2SaH4UuMxqbMIrQ\nMffSeJPGUQD9NYTYNjgWk9zPpPEpMQ6+96rULNvTpSVm9xYQuhcAxPTDim9i\nAuSfCzpwtVSfzTV1E20dU5Jd1NFoUhtE4KmaEARgdNo/AsoyhRdyfoodA2qd\nsIzOqm/JkbQZMzej1vMg9OYb/LTcqviopNGt0wuMG02wc151bXXMU0fgTln9\nghyTmlaIWZSr7t0jnloZjk4gP/0sLbHcz/vNGXn1GkrE3FWjE4oy4GxS4ABO\nuYxRXyMgy3DdAqCzGHiaR2IV43hnMM5NU/rGaznPzhCmKFinG01Npxi7tBlz\nLz6P4sLXggWngw1QpXHhNjA1GNAKc3ZUkUbRWSMDLVf9ioh/3xiMQISstLcQ\nC7n4isfSNBVsLRA0xEkix9ts/Pf/EdXk+s9DdRHLNkIujsoJ5sWaxY2Z/H7X\nincrsUaYJloOXvW7Ij3I6r1VQuTBjNNvbcLsnaEylkTa0O+tbA12fgtACHEE\n6x8AZgrXyGbI+CSa75iJpjpYJreo+04f23rv93eJVAnrHMQRniAkK+5JKZs/\nxHcm\r\n=khds\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAs/7Oqr0wCYPcwxJ5XKc1lzvhvIQVnW1ibyBoWcy7KWAiAQpnwfbd4yOQD23cfVpqnWhIg1Thq335L/j3Q39T57gQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180524100532_1527156739208_0.15735384977825628"},"_hasShrinkwrap":false},"0.1.1-20180525014606":{"name":"@atomist/sdm","version":"0.1.1-20180525014606","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3444153c8826d3480929ecfb3bfc00d135eb8c8a","_id":"@atomist/sdm@0.1.1-20180525014606","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Exun1gFd52KqB59f+bl86wJ7OXbFN2oAfuoaoQdtNDxKSn+zT+lXUKHhgRrDzkXq2smewyLyPDn9JXtyuDdYZg==","shasum":"6aa10c2f04afb2689bae65101fa4ffdcbce4fe3f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525014606.tgz","fileCount":833,"unpackedSize":1880254,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB2xuCRA9TVsSAnZWagAAqDUP/15tgbEmDsPamZt27GuE\nK+VlpO2Lppfe2Xj1X2RUASSgMvSDjpOrz1iC5fZaZ8MKpD+WrbhYQifYIpAS\ntMJwKV2qfo6RuT+JL4KPfoq5p5jfsaw6fcb4gjKIaGtIRDXZWg5jNu4KZ0dF\nHqlV2SxK+bl0hApDOZQXU0HJinD+E2Vvm88j4jQYDY0evZ+0IajuvuL8nGUp\nFDCnFTrTLI8++/awpUhGpdnj2PnJ9ohWCiZWuLIqcoCBRVnapatqzn7Emvwi\nuERTfLelNQ/MAezZ5VmGDeXfdwigEljJD3KP2l4GRyCMScFUirkPZsa6LdNR\n9NuZ8kvLefUE6PLE+Kut3/XulG/W3lMVeSpiiy5vgCims+K82JmklCxgPR/f\nKr2WL8QFN5a3yrK7aIszRXLCNND66TwoUq33ZD/hLxM6wH/6aZry2v2QO10O\nfCvZPSM5LHeoe2WZMbdB9PdGC0wT0T+7+stIRu0JT2ys3Fxxz4xLgtBHpM2q\nLhSIYVBx+ew7U4vM21oCJBaH9jdGCnc7psbwxoLPfmCVHoGTCEFYT2UWnqAp\nKrK0GA8ApTRJYhH7n7ePEKaHZtKorf/fuorv9evi19NL6wxjvIW3gWCd0rZf\nGy3E08DC3z5jNxVpiYK/mg4317o/dp1IsErYxjd8UK2orFJSBsbIKheQs2Sw\nEt0y\r\n=K1Jk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkMcSE1oyF4ToDO6E5TWnQb5Kb5RB2/+J9TEiW63vABwIhAMIYDHRmQwNm/YaKiTN7KDetc8cdyZtSN/x5PAodh+fl"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525014606_1527213165230_0.6230746203083668"},"_hasShrinkwrap":false},"0.1.1-20180525032831":{"name":"@atomist/sdm","version":"0.1.1-20180525032831","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"06ae7da5c816eb354c960bb48f142a86617614d9","_id":"@atomist/sdm@0.1.1-20180525032831","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KvLkaGnA19fv6KSFpHfQvAFyuYfPHa3CCW/WpqmmouGtZiIMq5xuq6LjEo3cL/iYvWJMRdkpFqd5yXFLSSKGIw==","shasum":"fee893ceee99bdf5e472e56db898785f52c847cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525032831.tgz","fileCount":833,"unpackedSize":1881097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB4RoCRA9TVsSAnZWagAAJIwQAJqMx5K8Pp+PjvqOckT8\nx4Sx+cP7ZLM0MX7e8QlMXIMM8WSeW2FPLQjf0jHrcc3KzMjtCtgrwpJMuo/t\n3E2f+VAytb1EBRbuLgTBhP0s6Z47B0iJiBdpAZE2K0d4mkfFV7AXyFWRQvL8\nOCtOPuGNmXGWUVutU+rx66K35fhjX/u7+ItbzcqSKGhf4N6GAkrd2zG81gRr\n6St07+HEl6f1AszB0NyGyouHzp0GJukREERtfAImGWdO6SjYLs4HULyrGaX8\nHLezJ5BXMmTh6HBVL3z2xmHCL/4hOmI+v8wZSXSe8AU+o4mNsO/Ok4ZB1IVX\nfoRSeTrVn0NC/CdqfypBPWdkp1zjW23laxZLen7XIKbQF+y7YhtRbgdBZ2Zj\nW5+kPB8jpVHvzCDFwsD93s4sNcfl0bqsITqe83xiZpkf/PmaqA/CMay/0lfH\nCRF9qj9622H1iJQP33qoIzt1VwWlI9abVpJUcS7BGvEFsoZCT3cCmjW5JIKF\n8isf69HSnv35bf7vLE6X48y7NlGNLSM+R9cFXPomKyKaWAvtivVl9h7ENw16\na5cng1KQArRwbupMlDPNwDsxDVvzAc9OyMG9kRg4mHPxJaNAjkFVGf2l1FzF\n8lsZIMSTy4k5OceDiEJXu64++8kefNb3VoAWNMT6olwg1YLw19z67ToHk9GI\nxccX\r\n=2tsF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaXRwNpV3pIDSYtFkSPPsys2g5bpzfDH1x3LjLz+K9bgIhANSgbnsSQP2c1Nx7fsjw51XJxGExa7pRvXlfmj/4caT0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525032831_1527219302077_0.2451806357934907"},"_hasShrinkwrap":false},"0.1.1-20180525053833":{"name":"@atomist/sdm","version":"0.1.1-20180525053833","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8ccb793bbef16cdd782efadf10a001a311d7b5f6","_id":"@atomist/sdm@0.1.1-20180525053833","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-A1cCjhORbcn0hKHGDUjHtDsBI+ZFlLHk6HdbAzwed5MqnAuLXiKisEcH75kBG87zj1/v4pLB7yC6XfIb7C5Wrw==","shasum":"efb667469b51bb7c0f9fecd5449d4eaf87c66021","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525053833.tgz","fileCount":833,"unpackedSize":1881144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB6LnCRA9TVsSAnZWagAAnvAP/RV71tw1UUYA1khaCmZs\nAfm13SayJFM5ZRnsOZdH8K8gn7OSBqEXeG1whIg7dNPf2GF7E4FfAI2HO6lb\nImfzSlakGEF4PqorSjz/obU4cNbmcSzUkn7ekgpFNrKryuKVs9T9yMMH6/vm\n7tOIIWknV8s5qb8ktQ+2Qqlc5Lfh15736pHRQt9MB3L3ch7TbxTb1g+IXJeD\nt3Xxyg6z1Ea9WPjj6R9W3b6qMdIBQk2f2AangxLOSwmZtcJPTGJt3YCOgEo+\nkg6cWDz3cBgaumPti+PcJ62AmfwQIbmTasOnVsozkW8/w8Vg0JF/CazninyP\na6YqXiq4glKm9gFfyjOALJDymlpEPbSoSQC0HMZHViPq3ZXtB43nuraA6ZVQ\nTOwiJnd/Io/jOLmOMN34x2u8yBMzakvUZ2l5YNwDVjOi4rd4n699vinIjtlw\ncLVkpzQ/kWGqpFk5yFO13NnDXwERS/bvmfmROHc2whmMBP6hZuNIAK4AWav9\neXWskdtr/VNuKlZ5m6373cytqAMeofl+nR3B7JkMnF1UNy5hmjaxn34TMA7R\nex8T5yKulvBAIxEZEa1YqLdwGdFl7pDKkray+AGUnlXZXgN2v9+2iLbFsIBA\ni9jt9fyu+Uc9xmUa3d/F5Je8MkEbFH47cKKlvwqY+Hp9lV2pEsIDHq0HGGXG\nBkXn\r\n=AK4W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDQoF88PKXjFkIE5GUfigrYVBL6CWfDa5EnWjyLFJ5EwAiBWYC6V3ZF5b2RbLDHYwGm7ERJbcZdCog6fdjNSXCwlyg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525053833_1527227109765_0.7329695605656334"},"_hasShrinkwrap":false},"0.1.1-polishing.20180525073839":{"name":"@atomist/sdm","version":"0.1.1-polishing.20180525073839","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e4db3063c436e8727497537d42722f290c5b6fe6","_id":"@atomist/sdm@0.1.1-polishing.20180525073839","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0EjrjZe6m+hS641203HpwDG0lYvrncWjeTFxXv7Va1hRiuEfzQ0Dg99AxGue0FavCTgNVDO1TZXSY3ZvbQHT1w==","shasum":"7d0d6e1f3e0243ad181403438d35625b6739768d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-polishing.20180525073839.tgz","fileCount":836,"unpackedSize":1885106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB78SCRA9TVsSAnZWagAAD6EP+QD7QzwbApqA6giwggiK\nIkpCNwwCN3FX2wN7ZN86ZKKJMY5pDusLFcQ9KI2GA+Sw9Tr5uSh35WEk2XRP\nutWmNMmZhWaHIsigQRX5ASVKdD/1IkSsSmcmQ8tjK8zedABislCITiPPurpv\nVAZmtyJwCrCTph5Np8f2R45BXiWuqemSzooClN0zCJswZF6peoE2FHaZ8CuU\ntPeTpALtUPSnZ7yzpGfnqk6NzKUVFghH5QbeyJBtaTsF88ocxqRE2rnOqjKE\nINv5legzBSMSteM3g2Gu4n4MF/rvkZsDagF/wZRQ+3BJxGXArnlTjTPxhS9z\n9e+FxgsOTfKSFdyQZ5KEDUF6jfRnNaYNza5xvlyvv9UfXUVs/n3j0z3Llmd9\n86fP3nWIuVENU8XT5i0xUOcy1AtnHI+uEqwk+yENAmWTvlEiW5rgixEroj86\nwwVzXo8dVHCnzmzh9anjgOMG9D/M8wJEVJ5tbimtXu2iahfPhjGajIdOSIzY\nfym5OxEzr2T0AX8ziVyXY8Qsjzyjp1OO6iNPea5OjiK+UtrgyeDWB0cZ+ed4\n9S7s4Q/wSIOL6ycRWd4YTIbJt/844DtRML5kIFkFv99i0lO0dyXCCVgDzuvr\nGpb4VR08ZExElN3e0hvbaRZGyjk7nsHKRctS5M4/ctZ1LHTPG93cjjmOoJoJ\nyFzq\r\n=Sv7A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE965cyhPKajxJOVPtnpHcW8ol6478AIm1nRoRsEsF4VAiEAkZldU2SnBfclGT5d8wNp1JF9fI9l6f1IGOMEt9vLvOo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-polishing.20180525073839_1527234320427_0.15557195948096592"},"_hasShrinkwrap":false},"0.1.1-20180525075547":{"name":"@atomist/sdm","version":"0.1.1-20180525075547","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e3841cf5a0719b1e34afd256b402980188d608d6","_id":"@atomist/sdm@0.1.1-20180525075547","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AWrhM/JSsEK5H6qrZ/oSFgGZE43PhzRtuYEtFkXVojjagb8Hl7t6K6cC+CrqALbUptADT0MWg3/ncortu9MPVg==","shasum":"af35660806a419f358d323ded1e6b16359741f9e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525075547.tgz","fileCount":836,"unpackedSize":1885096,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB8MVCRA9TVsSAnZWagAAstUQAIn57RrzPvF7WEq7Y/W1\nEpvAA6qb15Q9WrF93MLAD+YcEnAEJOjfeMFY4j84HAKsr4DrUs9vZgrwGw2E\nMfvsB1RHX2RyiiI6SZl3vplY2nT2fpFLqW0C5+pJyFBjQVNW9iIh7YFosFoK\n4C8pmpv8ua0xgbfJweq4O33eLY+eqflAYE5LPvIgOibzQ/Q6xDQjAwVVUyak\nvtLQMDWI4569pLbnG6ZMBBcpRlwjeSCIyr+e7xxbgL8In3hz4TyzGKuij4Lc\njNKacQbiYdRVi1w8QKIPk3oiYwQ6gX+T0AfppG4Tj3R9yE5GrLL4wetiE7ra\n4SoNXEU4FvNYIe/qNxsnR3dtd/uYLpNlBumpS08NCJFdrAVkawNoIYlOiLOT\n3gg7R96sdp6Tqpp6VJpj4U1cJPHDSW2wWoaP1b5JvKgUoJbzFidiKM6zNVI1\nH7wDQOk+QsfriM8/83gX6HnU2ZL8otcxhLexWUulHByKkSYP+ZGbGePtOtah\n+7IM1FQwhDpS338q9JpIfM9ufRF7M8lR72oLZ7v/FDxK9Set4mql5qufkLUz\n94ZiIcVVssDfX00ziPH9zGNY5lyUoseC5nXgJEMzym0e7R/xI3oXhGW38BqU\nXXBQXVyl4O7QKMJrVo4ovbQ6STQ8ZHA6uW2C8AxZq/jh8uCl0h08jXRFHUuo\nfHXv\r\n=5z8K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCFK2M3VVvs4kpqJ5MNyUh8k651Grj5ydNjkOFeCvUDVwIgAgF+gAufUsmMeziVwFUAM2WqfY50YMg+7h1BjW6/dpQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525075547_1527235348420_0.2022025038754116"},"_hasShrinkwrap":false},"0.1.1-20180525080431":{"name":"@atomist/sdm","version":"0.1.1-20180525080431","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"baacf962ab3b1548be07c775a1638106d973f932","_id":"@atomist/sdm@0.1.1-20180525080431","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qzMXUP3Bm9dXQwA+Yb3NL/ybDC4fMqwJ/IfaOG6n+eHnL//YWa7cuVMoFQ8t4UM/bdnjKxlBuzLXnIRlwfWX9w==","shasum":"0f28aaf7a005e0c9f42e197301e590febe4d6627","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525080431.tgz","fileCount":836,"unpackedSize":1884998,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB8UaCRA9TVsSAnZWagAAoiYP/RAyPJYJWaqeF7tJoOwO\nyHEh7gB7GTNPDq7wUpCZKDyjgf3uinKXgyy9xb0Q9ihU8jeoJE4hRuU26R/J\n+oqIimkbvVB9uRvlVSVift0hdKaACYT+WTaZ4Eq1+LLpP2kAFnOPZbk5UhMm\nESjcaKsSrxeTdiogNkI3orKmmWFRGbkxmRuJC6mhWyxHWAOiwHdxiVKzjjhp\nrG9MjGMruAiA8kE8+vGY17dLVYDelSSv5n/imq/+3fGdA3ZB7+f42iiGr7VP\na+ixNXIZgj5IwZ6Iu+Lz7TUnTrNlcqe3BEYENoTt3CRIy+OaGjoqW7KHp2My\n4wY2xsmWWA42ajjHrJjB9r3vZaDrBDQ4Dkdd6F7C+IxfMSoyfCUebxVr1j1V\nju3plRi3VW7h4+/botMS61QXESLuXJJS209bAFWRSgLeX43rKsrQpI8aoJ2a\nzglZcGcRP41Uo9HtHmTCR+RWRVZs4OJBSnyqW+KpAHhCrutzyFd8/9xeOn7c\nV9pmzMFoD788a5UR042g/xOROFo8SSxp0iCNPNgv9vJGzGUHCYWzTKsTLlZJ\nQgAu+bbsDjb2nEgemu1WsklZIauhW53DZOOfp9AvjnayMx1GvhdvMtQf/aH2\nTheyw2RLp/ctidao5VL1YVUhuCMyNPBnull1ccAa3SM0zM0dxqG+ZXTOxIjv\nRlw2\r\n=qNLH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFydRaI4nT2RZAVgztXEKREtMrBpwx/VAIDgkcJRxzypAiEAyzoRzHVKn88741Q/JkfKrMWk+jLOJYLiByQuuZ/UCcE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525080431_1527235866258_0.9592921275335202"},"_hasShrinkwrap":false},"0.1.1-20180525092937":{"name":"@atomist/sdm","version":"0.1.1-20180525092937","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"df861f84c8ab55bdea0ca9810369b0f5fdffd32f","_id":"@atomist/sdm@0.1.1-20180525092937","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PiQof/cT/bDncXrSmfqoBnGqQu3nBWxWH+spZLyBQ3M/2m/1/FSLMtRqYaNsNWLV3FkFUm4UtT25EajBNDG8Ew==","shasum":"ce8ef789a3756f0826941963374bc0cf7caf53d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525092937.tgz","fileCount":836,"unpackedSize":1885281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbB9kfCRA9TVsSAnZWagAAzqcP/Rcy1vUI8xDYzzDgo+zC\nyosKFQz/1Si+GtNDfkPBbjw/Fq3jPB6CfTFdsiwZngDzvqbqiKb9y/e171lO\n707t4BkIDGn5l61xHIqJJInUkZ6qhyCHShUNv280LNjenWt9oDb1dIabksp0\nB5IeBhSmhl+HJQpDb0iFM2VhKKzrW8k97Z1UptZG6zlP/MURGcZI6yF3MVLG\nBgomNcMOEglGWeOiZLOW1wP4fTpq4huuiFh8V4qD/zbuMjaAfVUrt4UIH8QA\n0BU94UDp7mbnH6HVYR6/2DjSCCOZGVtQy4xBKf9OAkbFLxCyRuouDEshD/Xj\nXQ2/MFh1lojj2PjfPknVtSUM+d8F9KrpevIBSegkTqxwN1C1lYpvbrvXHsPi\n3vaMEVABVFCjZkQ8GbwC2s9Xpgxq2wXSSB1YQRA/FzhRzB/plYIF3VSsGJ3f\nEeXW+yrkG+DzQx6PJWfB1l7C7/LnB8KnSK0yynN8T3lLeCp2QDha5GRZ1cov\ntStUWvQwYGJYnG7Dvn9qLQh93ID5Tf7Zx+JrKV37Ggnjs4kXXu01tzkjoCMD\nL2tYk71yd8VVDc7+qOJK82i8yQdAiotbE/MWIpI/WlcdIbWDtpLqg/XPBnNv\nM+KcGBgG//XjsT4bX8792ZSMbX8NrsYVrunApZZZm/FIkPXPxweO2yMs2lCA\nePRV\r\n=VToD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDStOdAYQkKCvLow1/NULMhT3wvK1rQfkFnX4BN/9B0IQIhANXRSlFdyEmU8hYomQDF0+2hbQC3dP/MxI8c+ktbk+lX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525092937_1527240988596_0.850981090323536"},"_hasShrinkwrap":false},"0.1.1-20180525123950":{"name":"@atomist/sdm","version":"0.1.1-20180525123950","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0bc7b5eaad8725813a720a924ae476cd6ae6e847","_id":"@atomist/sdm@0.1.1-20180525123950","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ebVKuOX1NE7K7Ew1vg5Nv5D0H/n34iamPpvHh31fKOpkmumY5OCJNMVw3YAsZ01JmScEaB/6zVFTecs3WraAUQ==","shasum":"a97f26a5ffbb134765485d80e89bbf0404a37080","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525123950.tgz","fileCount":839,"unpackedSize":1889174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCAWxCRA9TVsSAnZWagAA44wQAKB3QtjGZQqKTwuOpjTN\nL0RggorGiWRSkPTzQAodgQgYpctkcCH7wmfL+QZYvGJYQhRgDD+ft62zwkmz\nKx5IsuYOqTV2xqGtz5zlOhXOrJB/uSBimbO5F4Puw9YpoqlVi0uwLB+gmvyG\nppkAtEhqBm9SV6so7spgIWAz3KCylP09PFv6/FmW1WDlJoKr0ZUXDZb+r+MQ\n+C1nZk3ALklNYTYIDLuO9cpZDN2ZPIsYvBVMHREDk1ERQlPIhCXdcOYwq7WF\nujJBID/6gjpAwUrywkSrk4NTz+afRHT9CZ2OKufDieUaLPOi29jXdScQxg/P\nchcOGOJQPco0GUXMMN47vlBTgHWSrJAgkadsdfQhIgGeZ1+fqmq9QjeG7Q4u\n9gm/mSHbd/KCjDpeMQb0rEwf3uMVWxeefzk5qmERhbL+2JdyPDfINtUPcDPS\n+U4W4C6o+jyFaJmk1pC9nPcsZFfEG2c02MnDNnVH5kW4Qqz+Rb7go+dju7Zh\nE6bddn5mSq1aUuAz/QkIjbEwBQ2+lUqyO+T59n/Vwx43HAE0S4z06AObdZ6q\nKe7LW6sapTxzTP7yYlHe/s9UQ8lyBq0+SN0UTc4xRCwMZXoVVYjP5XIM3UqX\ngMRO/RzpXifjLOx2Nr3UKWoefMhKy2mzPkUHEoqPGHTQpnVhK3RD4yIY5r2J\nHLT7\r\n=23Kz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCW+7Fw76eFoY3xieVBoKdLXUVQZgqFGIa0uJTtzibIAQIgbUGr6a/5VIuRomKtfCwwl9V3gtbdaDLQfAMerqnjSWY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525123950_1527252400012_0.6950043303624176"},"_hasShrinkwrap":false},"0.1.1-20180525230434":{"name":"@atomist/sdm","version":"0.1.1-20180525230434","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"27e5a88e33c4350a53c517059c4e2d653b9af4c9","_id":"@atomist/sdm@0.1.1-20180525230434","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m65PMnYjaEIHLS0tZR2HcYH1nHe855T/VCIeLv1y7EKBLRYvcpZyLg2NuiRF+eK8iUBJKKDR80JaCSlRYnQKng==","shasum":"b8fed258fc18b2d17bf04e76f310388fd2a24a64","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180525230434.tgz","fileCount":839,"unpackedSize":1889174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCJg1CRA9TVsSAnZWagAARFkP/0Igan3pJ8rn5xpuewdp\nsdBx7G2bxjJpnxBKd/J7+FBsNPPmwMuRW/gqKeoxIdZC9xnvamTwziU0LsIP\ncUUlgrrrVPdbztk6i3CFGJwxM7q4vGBlOEpfukllp4kWpNlSaa8OlVhX66ia\nQ044tr24QVg4NKyOz5H7ilReeBnpR3Vord5ZDAa46yI4c9LXyb8pWxfjlpkI\nR/vSPVxpurtKmHfLLUsIrMGncFnrW5E5WlpdgVpE10yme0iaXqyhVEyv2Wd+\nWW5uGEa6AEEte0GhwrNndXxIXIbV3gWaYxhkSpNyI37QfjbD6VrhoehkV8PL\nRYhRlos2DFI8R92dhr1lYO0UlB3I1cLDHEfwv34r4n3FeZi5uhr2YBKdVOJg\nNTtGFISaNdAXDAQ75iDWoalkHjj5KAnJeXOJOiJl1PjB4M6w26RE5EoxiLkI\nAwNcDLdQ44t9UIe6PK4u8linaI+eNUnwYHrE5IeQcXfTrUm06J/fQP8+W33r\nJT69KuM7OfwsECEck1t0LnB/tQWux82v+0U1V82Snmuu53AP6N3CsPnd6sLE\n3kr05jSG+Jf9CtTLxPNnGz0g4pznaqSZZJ1p/AcQafLGTmz786+oYdV91mgM\nH5i02otWWiqxZb1PD+1sKdmfjY1ed9LbsHJoF/SUAMOTLVy6xYcur8WBFOqn\nRex6\r\n=jgBw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZEdelbdO0IM9/WhbYlz58afUKODnCbhZknSxqixKC6AIhAMjdmIXOPWfdWgEjMmXD4Uq7SdV3D/t+tyL5mJDTFhcq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180525230434_1527289906579_0.6857843780210982"},"_hasShrinkwrap":false},"0.1.1-make-non-fulfillment-natural.20180526003358":{"name":"@atomist/sdm","version":"0.1.1-make-non-fulfillment-natural.20180526003358","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkV1v3l8Jm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.12.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"085871eab254470d992d61a50c16b294f53e68f9","_id":"@atomist/sdm@0.1.1-make-non-fulfillment-natural.20180526003358","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LqVmKRr0J2dMK0kPY1wLshOB8ILPmF6kw1VpFuu7HywAhD75+XWIwNfzh+zZrq7Mw6zCBlIlpL+SII4Gj4oZfQ==","shasum":"defeb729eee5176cd7064f80bb6ea2d2b6848490","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-make-non-fulfillment-natural.20180526003358.tgz","fileCount":839,"unpackedSize":1892734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCK1XCRA9TVsSAnZWagAA/+AQAIB+kOYiJ+0k1qhgun/D\nJtad/06IRB+7zvuc452J7eI4Vsz9o4s/JUqITzlWoXCA3pNM+o9VSF3B/hVX\naWSh3KCntfZTfSo0yRU19Q+9AGEvyayqpuux5/u17jk6Zc0O9Oq+79gVMwX1\nt7j4L3T1Hzv911tMnDlvq3q4GMykEw3eBVDBmYT4gOzpta7HBoonnzNg906z\nmGAuT4TtogSfiN/E62uxzi43BooE2AsNo9XcfLzJ2on5g7gTVWXALjWHvp92\nLcVzii+QpVtRxQEFSM12+nBV84AGDk7pTOW9pkeTe86Y/A5V3tPw4/rnA3FX\nvmmCCivANCEfXjTB8M2C5mlNQ/YahAaX74a4zjfdli1G7r85aUWID4z8gUMd\nq0ALfxZmN8LPKTs4vcG0s5FYEbedAGHCsELxBzomhtfSf1eXnspBgArhqdm3\nPgEMqs8L7i7npOy6OX+vS+cmoFtaD5J6omY552Iw8s/3hhwJAubi30TUAeL+\nFSx5oXIXPPetsF4WbhKbaa+nK9J4KxewKoosRNjJZxmjHn+Pswttx28d7O6Z\ngWyC55HFuGOXMIs1bjbCD0VK0aCy/lJSlMeJ6KSt1zJic71VeyXitR24QKCD\nmJxUaPz36MZZ2CZy8zBibJ3IZ2nz8lTTP7AYTMLcbo+rEN00WNBUFlVnu7eR\ndhIK\r\n=hkuW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCc5Oh0hNIMpmrYpCQ4VbFpLVNEeCzZS3otYVGvy/thdAIgLT05747CpOXelLBSaOxGiiKQViL4eqHi67zD74QCn3g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-make-non-fulfillment-natural.20180526003358_1527295317938_0.982168095179641"},"_hasShrinkwrap":false},"0.1.1-20180526054313":{"name":"@atomist/sdm","version":"0.1.1-20180526054313","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b01698549995155a0fd724c36d25552cf3b1c0e9","_id":"@atomist/sdm@0.1.1-20180526054313","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-24srqG1YTiGHsBY6JX+TYBPFBXSTXivx3Oj6a3dUB28q1sdK+M/EXQ7QWAfGObwuTanM5Lgvndyj3d+++cACHA==","shasum":"486300ab62a4c526af6323e10dd947192d305e25","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526054313.tgz","fileCount":842,"unpackedSize":1893581,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCPWQCRA9TVsSAnZWagAAUJkP/2IV/+oiZnZZGD+GsjgO\nxHMYQwqqlYBatIs8lXkTGL5DsQ6xHB2gW/l1ulm2CF2HtlJf3a0VYTp2yjtQ\nzud/dPk9bVJDbRThfhz2gCBa5a6qw5SXorgKCULctuGx+IEkvlIY9npXRiHL\nam8F2iwoGVwWXvzDJa2MYOB7YO9duC0+uR8XkY8WwYHHUJIu5brwVf04Sa27\nDoTsOv1YElLRQjf5L1hBb6n795ZXbYAmWklJa/XkclEwhVedconY6zn2himZ\nENQWef5H+I3q/Xcko3d5exrgny0GUhalGvhRxJc+L4TMgUX6AZeL8lY1d9Wz\nqwSnCcJGbXtV5tmKdSiWZlLHTXof7QNl1J7QN6sjQt2J+dFTeiyKDlh9qiiC\nM60vhGSBtUmLl9Ip0lcTPDTtLjMneCuQ7cNC4xesZzw+yBTimbyPlDrJXPhd\nktfPaFhqxyCEu03WSRrkZpNE3SbzoRrIo3P+IlRkxwBwzVRykq8i1Ooav7Dc\nUcdg0lmAiOtGrbqxpaEuzECwln9LK6eeubLqCfDvsQCG+/dXQZIMrLZ+BjCH\nqLy8cxsIhnHsQSl4yagfPkzbQVst0VVfxMyxVNe8NIH2I0F1f5tjBe++6SpS\nd2lDHwrHGB3hDhBv6UT4J1HUqcfnsrbvmGrxseeUx9kg2fSJwGRnIyXWn4MQ\nKqXp\r\n=sVQ6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB6yM36ZCty/ck40aPbH6Xo6Rkr06OS0qxYkrUJJY6l8AiEAoLUu5wxtfji+/5jvKzpvXQGjViR8Wl1EvOd7D4UkLv4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526054313_1527313807066_0.6457803632965518"},"_hasShrinkwrap":false},"0.1.1-20180526083456":{"name":"@atomist/sdm","version":"0.1.1-20180526083456","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"814a00ffcdc3f44efddc01e4a5d6a87d82f919bd","_id":"@atomist/sdm@0.1.1-20180526083456","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XcdZpRi5kLsevEI4h6PnXBfsCv+t89EuOy6C3zu+Nnw4A7mDE/cmRVpq9Ax2ju8GDh9QP9yP7eKeP30wQeXu3w==","shasum":"b70fc7423db161ce90a11330fc12771f847e97c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526083456.tgz","fileCount":839,"unpackedSize":1893574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCR2WCRA9TVsSAnZWagAAKkwP/iaRug2PaEKfmlMFl2lD\n0TtsbaceR6n78dY74mokK9amxIMh05KtyCUF7V5eKPqZw0m0FLEqBL/bfooy\npH/QZUvixwcYB8tG/hRsCDmmEkFEf/ys4XyA/0+1wytZXuwmIThNPSL5AGAf\nYbhxDBTGZ1+tMNhvS8hq4PndJHxaXKr7E/hscHBCFieBfNo/ZQ5weiqqVNn+\nihhUotJh4f3moC+VoiICbZ2fOloGx8Q0iFIYW7kFdUyNM+Z4chZqE5+kQjrX\ny6J239l3mSXrUhT047ZuXj61uw96j3s5FL4knIA2v4QGNffLkKcOmlVYljT2\nGv9UC9qQoIvGd4vi4vcXkwAHdlywCFo9I0YJ2Q1crQBE4kfxxlcpI7av0By4\nWmGJugbyJrAnALgsjT26ilSFO8vaVzfQhS+x4+5pjRcjwbxRsF/wtISGCiSC\nUyN/8pg0mFJNMJoIUrgdJ/sceNbvNuLqiYwKmcatw/lYVfWndN4pqRM4Sfzk\nQSMxPGwXHIjOdcznfRaS4+6PgaGwOsJ/EyRhSdYrTvAJi4/F2Rr+BShV3hpE\n8MeT84/DXbP3Q+KmaOsHTNu1R27QcT6Y+0djrPJ52RhQu+ZkzBJQ/HL7PYfo\nFaB8OlZPs8nnSrWWLbk0JEX+HW0vtjpMdy6C1aPzf+lfTLqpY+JV+NV0hg2a\nbRTX\r\n=UO+7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHjF+HFmKJseO8va1n1f8f7KE1g/jm+Qdnd5URDgBQj3AiAq/gWRrgQMDgbsIVZLVeiSXG33IMYWgt1NxKQgffaUJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526083456_1527324052274_0.708832452845293"},"_hasShrinkwrap":false},"0.1.1-20180526090148":{"name":"@atomist/sdm","version":"0.1.1-20180526090148","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"035da17be972dd71c8a453081b08a8aa4649a999","_id":"@atomist/sdm@0.1.1-20180526090148","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SlaBCcF1brLSjxa2AZpTA7Et9UtOZJ+MPkwL1Gd5Crcpaid8UfHFNL2lRxat1jdzp3au8EB4weFMLAj0LYYqyg==","shasum":"7a74f29c58232c16009eb4b210eaacf9e6472460","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526090148.tgz","fileCount":839,"unpackedSize":1893574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCSQ8CRA9TVsSAnZWagAAF/AP/1NSHU9Ydu0UlQ0zhf/W\nLPgjQqp13RxJdsGlGNZRfIKnnOXQB5qXAwdSXOBULf8kBjLzP9ye9pGSUOD7\n3QjoCh8xQfDg4QkjwfQbTDfHY5t20okyodj9Q9K/X12yfKw8d9NAafDhMNLp\nlFe/aDRd8eDeKjZ/ix9nuDUagdeocC4UaCLSNq60kwDNRe/GnltfDKmkElLs\nLeCYpRgx09U9Wr6vduUwZH1aNBfiSSH5PhsURXOtGw+W2fwL/VzAVsK2fVYi\nS4xBXbsZPNV88iPjYkCHcd0sdULgAyQw7SoKi46xab5/EsjSlzphWss51JQZ\npP+tSFmkF9pACn87/8mZ+twvcwVOVipAU0JVSSA0b3/JMNkSXOmVZ78Pn7iC\nHQTU9dv1lWO3gJR9bsl/FEryKREudDDJt7plysjpVif7zJvgqf9LePE2XY3x\nQdf9uwdPa83padGgpiQDyOnHvrUbEJ0HI54oQT/s3mNmLx9d3IyA4NkExERX\nsK0HhpxP3SJcwN3j3+QzyFCA3EJU4Y+R5uiT5lcQX07VZGY9Bp3wgPpsZYYg\nKqIB5Kytjhjv+ffjfHlwukGkjPjCcvHYGtm1gTIFZe0RlepcjvIenyubUfsw\n/aZccBPEHZ9+ZdhIZrg8jkiscxXPehPjWVrz6mGI57ssmuKdFzmg0IGSiSue\nyeRI\r\n=7eYw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDTCgiG0E/KQHzLADfAqyIFjVt+QReQ5SI91MUlxzAxxAiAr4Ux+W1REKaweKVN4Da1eLXXgqO6cifO1rsbtA1prDQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526090148_1527325754308_0.06820683175448017"},"_hasShrinkwrap":false},"0.1.1-20180526103827":{"name":"@atomist/sdm","version":"0.1.1-20180526103827","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bdc6418136a00d1182c716ece888f9f7c4958a2c","_id":"@atomist/sdm@0.1.1-20180526103827","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-d7bsUkIGv+rHkJ7zY33awI8BktefVWmCWrfaRWBhfyi2dj+bohithuBtafSFBoIAwZXV3xW2ZHaKsvKjGkxt7A==","shasum":"26fee098cf84f942ebc5ec2c251c25d50d50f5de","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526103827.tgz","fileCount":839,"unpackedSize":1894849,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCTq5CRA9TVsSAnZWagAATQoP/ikCV9dj90O+7Rg8Hg58\n6eGZjOZS6BhAaiSqhdA0Dlc75yq4cIOZGhnYEWDSMVbTBVPx/MLysYQj+33x\n5qMxKAQl1tu9Kz5O5hcRtoi+odkMdzG4/Ybh0HFCeK19uioxYfChM+wqZbYc\nnmK7pVg9qIaoAOKqMZgSGq9GJMGKl2AFzDgOpJlYiwCKwRwHFY8qcvlkyH+8\ne+ICE5syzKmR453flYoXRkqsw0wRyKtBDmizh25YV9EK62T9xJGA8bRlejfC\nidyTjjLjejXm+VcUfJqi8AJsOXOPp3EmaPH+Oe1guGhFK8ydyu7d9/1qzSwH\nFL2B83AywXqncEocRJOBX5IPglh82BvPvGP5xnmMFS3B+ZRj9prYsBKCdOlp\n/P6kjRlqox2+PHMQMsVKduTogUHieoC9DEg4MS9o7N2/aaf0L3D8zKTu01t9\n3DOPAlTfSAsq95g4m4N26Eom59XYLIs9Oi28zLV7oZ+UO+ghWVZ4ILdQx+VJ\nqS74zg5GaDRsmaUoYVf4aX1hecw9pgAQOG9U2MZ7fOqG8Eq6FAAleMtCszD1\nzOD4iP1pmMudN19HhEYN2U4Ecko+4Tx5SMD7Jx1eKLTlpCCgPBnpeI77AZwP\nBBN3LfWdYR8+yMjWJadtb6M5TGvIV60cLm9+ew7C5M1qOV3AJgY2Xdwap3fw\nLKna\r\n=2618\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2ce8tZEjTq+ed4R4chzjnY2kcEmYJGg2cFs+63t1a4gIgb0NOozt996QomNmp9Ryxo8nXZp3dVrFQ8wmxlKgD3do="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526103827_1527331511371_0.4013829433926386"},"_hasShrinkwrap":false},"0.1.1-20180526110303":{"name":"@atomist/sdm","version":"0.1.1-20180526110303","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"61aae58a827ace2ec32a1f446692fc6f9df41c7f","_id":"@atomist/sdm@0.1.1-20180526110303","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-omypB1q4n5QBSuMGEcs7QKtDe1TinpuALEv1v4+UcUqgo1xizm1mKhzX4OMtEfHD8wvBP9q+UIX+3Y+t+tKMfA==","shasum":"f6f59f29c5c5675c971fb678378dd1dd8ee51104","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526110303.tgz","fileCount":839,"unpackedSize":1895844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCUB1CRA9TVsSAnZWagAAp/cP/iuITYNySiffOQ6Cc2pY\nQXHZbbWc0w2wxzOU9FzYvZIX7Tc8ECFpajXp3IxW6TDbrcIur/mRnC1gPmzg\nBtIpCmbIa2lADT0UT8N5fx/jj05l6HDVNw5BAYIcMzQRlBhefWY77CriYSFd\nF2KvCfuMbhoEJElMdpXEyVgjnqTBlNe0F3WVwgxs3FtJMVbPzapOIX4XYSg8\nVY8Oy3g/L3PC3IFiH3nsHQ5YfLzSQ0JEtH0lujsP3/iPcSc7gIGRiNVsjQ3h\n9/IwEXRLpwKeTdExajAKPUmeLmLTT7nGSwWmr1/HA6bvxeQ2/XxicyF6fMDy\nu+f9qY/8i6T6fEeiKrXXJaJaBuF/6nxxE43PYr77IVXmPB5rYy0/bZQa7RyI\nVDHodm3BbuC1Q0JRlODj/yDenTqTy/clEcyVIGa6Fz/+PzRohkXg26Gc8Qjg\ndkqLxI8makkCpIf/2p6QmyGBrLwwn9vxaFAgNpxwbgC6XynzhdH5gdPA0Rjh\nVJCI1xkJZO7uw3B/8asb2DVhb7+KuLy0H9EsClM8F0X0ikqvegihLvxpSeXB\nV5g8J6fvD7iyGqOBBa3/u5aFvppeeVEF8W5NcI+6K485IBMj+Ih/SxYlqJGe\nKsufjDCd7CprZiTBj8CI/udEPCl7Usya/I00M+jYAfs1Mu+6d42RvbW/TW9m\nTcoY\r\n=mSKr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFsxaKNeruHCAygP7J4hSGV9nwXnOgoCXh7BburGfQH6AiAlYtvpCwzNWdcrjLbno+IxqHFq4qkqy4ZKnNt1UhGKsg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526110303_1527332978957_0.7402867673492068"},"_hasShrinkwrap":false},"0.1.1-20180526115224":{"name":"@atomist/sdm","version":"0.1.1-20180526115224","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"19bccd2b36de091da7a5683453ac4075cea49a00","_id":"@atomist/sdm@0.1.1-20180526115224","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RawoFOm589IG3xii4li52i2F3uazj1AjW8scaKR4bQN5ce/51L4nwmLtN/aIOyzzDJZ7W5yj/MjYrFERbKWYMQ==","shasum":"96b393237c7954b697633be4c5dbaab5bc3609ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180526115224.tgz","fileCount":842,"unpackedSize":1898462,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCUwkCRA9TVsSAnZWagAAJYIP/2bV1Zbjt7GA74MnvKFJ\nE/0FVsR3qVRJeX1p/hS8o0u5xyPklzQAzVQQLnnX0b2Kz0zJtvslqJurOrda\nTgArNOm4U2cYFE5mpRMYeYPtbAHy/Lj63L1f96JwJ4eBSz8xxFiQ8HzMhqb4\nY5tJG4KgZg86tWwdplnXNeuWLfyWKENomDbaaYqHkWSAMwiovakg44XQz8jo\nXMjk17sMQ1on0k2fR5XtZmSZVglbPIMozrCQ6qDWH0BxdTiHerjRhZ0E4ysu\nWIHXxmfx2tS+YUjmLSsicWWfW4rfbB4TjWK3SwN7xu5NBSZYODhYlVzPAQaR\nRboLIPyuAeqEBME4kknHkrxig5dMr73oAsPC69zdEB240p9RrKEruAwORwdZ\nUPNe0HN8eG16q1rtJ9NI72y6w5z0C6+NwYkzxhfMffsUiTzV+zIQmzRmqA8y\nhupizukf5etJiBUh3yWDOycuhQ8DzT1V43XIwv4ksJWt3tj8zBKFG/upwsOb\nBubfw4WthsHXjT1CtCbizx1PhlgUZ+PvJNGVns7FJ6hZRCOrvGX7xqclmXFo\n5Kc+4izwfLARaaxKzyyuwzl25GyF2Ry2xlNlCxdSBDeAfUNmyMRBJMCiGia1\nCopiR185yNzh9fBfJ4vF3mWv2fG8OO4bMaY9JvT4EPo45LBCEy/pyaq+gPmm\n308j\r\n=XNKa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHQnEf+ez10ksvrce+owx4V5Hn2+NbGlze0D5OW6f534AiEApG2nQL5wE5QYb5607iu83Um8zhSmU9cWDk8vnnMgShQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180526115224_1527335971011_0.5505784675337495"},"_hasShrinkwrap":false},"0.1.1-make-non-fulfillment-natural.20180527002305":{"name":"@atomist/sdm","version":"0.1.1-make-non-fulfillment-natural.20180527002305","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BkV1v3l8Jm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.12.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"20d2101d3b9621bae5f42d7aa207d263ecac6f05","_id":"@atomist/sdm@0.1.1-make-non-fulfillment-natural.20180527002305","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-q9+vzVeyZRRfWUn/EKblJz3TrDfvCpnTxHT43WOkjQ3hQCk8moYVMaDbRZY41H0cHI2h+T9F4TiyodCTvjhkBQ==","shasum":"793ffcc50be6d36de7959b714990b2be224551c9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-make-non-fulfillment-natural.20180527002305.tgz","fileCount":842,"unpackedSize":1902022,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCfwnCRA9TVsSAnZWagAAZvMQAKBq/g3VmltaIgGZ+qIO\n4qLmxqsXNvdtF25GSTnvuHIxyIA4SuECelLwtVyCMJjSC3DAEzPkIRAdwlBH\nDNgFg2y6aJSzm5kB407abxRZML6gxAh+LpXQ+VIbPlk0AuPAYvT55xKfWGau\nE1uf31xKGigxSKN+rDrNaA4j6prQ12flb9hDpNSEzDXgXcgYxwoSRfjynOP4\n57SKS1cXD9rehlW57UY2qRa3EaSQehirkLrVV1MZ9Ku8zXUhNUVaBjtYAb7j\nWXc3ovzmq4NLUUZk3EqGB/7PppKbCoH7CCI9NDh0v63BxMLL9wNyB/C03mlg\nl6Z6I5sD/3Smg+1iHs3IvaaMxyT7qLvvCFhlwyQsOVeUm0EUm2eq1nNv/7N4\n23Ia0iWWSa4KB6oJItUrCBx/WnuuX9fGzEPm/JisP5G61c7IR75NvneR5EZK\nFqqLRg2FZ/JO6hT0eGfRhvfnNpI+orfCg1DSzdEzdokhehoRZIPvSlRHFL3g\nMf62vnsfK5jK/85gwdVDpu4aY/KF11x6s/09m1GcLPUVxRZreQRS+m/WzIW3\n2m26+fAuy9fXTsTeqEqzCiPx7GO7RDZpkSz3pBlwy6PyD88O58B6jShJkqdf\nZrXq+QrVFSI7VXPS5KsnTCv0k2zpxWI6sM/pdJifiGzMZt2LdE3CZ10mvTUx\npT1C\r\n=lTBF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD2MF9T/1iWO+Q+OKGwCfd04YT/QG92rLjoelt8RUsLGQIgGNqLbsLXadyHyPFH5ExRI3yn5dbpoWpF4WzYi6QJASo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-make-non-fulfillment-natural.20180527002305_1527381030039_0.5937065054766091"},"_hasShrinkwrap":false},"0.1.1-well-known-goal.20180527023300":{"name":"@atomist/sdm","version":"0.1.1-well-known-goal.20180527023300","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"44208cd0a7bd3d090d6de921850f37519163dc30","_id":"@atomist/sdm@0.1.1-well-known-goal.20180527023300","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uKekQd6sIKRbeMDw4uJhJrPvgKrS6adR5KTuGWHhssakeg28tEzVNHm5T09beSkJ/KK4heTjeW/4unk90T38hA==","shasum":"0b221404cae0c71fe6fbfb56e7b4b5da990c9d68","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-well-known-goal.20180527023300.tgz","fileCount":845,"unpackedSize":1901402,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbChpaCRA9TVsSAnZWagAAcZQP/0WaFL8IYp6IG97buCqo\nIYOw18ILjAMpUYQREBMmrACeyFJiYrq+WPIY5/Rc5QHPKSw9SV6C17sAO82T\nJHSkHMmVgS63dTgAdsop0EXkz2+R9Is3Ft6xA3pcxyijNjr6jHbG6zbpKTIM\n8IkP2gC/ARtd/CCHk1wvV3EAxdVnaKHdE5Kmxf0UYxi+83XATJavc5SIvQsV\nn52HKFcqmxu6S3H7EUr6GzJDUJB5V/99rdOKjXUNhSEAox8VhfWpkd0qCoQ8\naUKwCDR7ZrvT9tGrc5naHZj/V45fWLS6K2xH7h06AdOynpOT4OMXUxVk8eKw\nLIhu2thYiHzx3E75QvbYCTBLZpy12UKfi5dBIIrWKrWtyHCV5cHt+TF3ddmY\nhzGhX0dfeieR7I5kXzb47FTosMB7Qht72L8KwBWqk+5xPqFpPe54DUKTrX+q\nEMxXPJLUGvcfT378kmI9F2GPOSAPyMaePpcXLOIPUpiEVPs+nPfNnQu8xATQ\nrj9oTjqXsoG2fkrfeQFlUV89bi4B8IzpGiYNkGlj4QvLmSSi2zmQfwC8xhcx\nTo9F0XfIz7Rccr54y/KKsXwgZ5dhf70KXzgd8SKTc5lcCXhjByDS6wcHssz4\nJMtc5fHSxBqeSkBzGkEzaYwGw13hwQpj338PKiPUfMKHwIZTjjjbXvbn1vEE\nztfi\r\n=byJ1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDNuYyepQM8eZorhthS9m8X9dA3t4gDgU+zbgrXcyhncAIgPlqQd1/WQ6/I6jw4JyT+V81PsdJdLq8m64JfGac3/JE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-well-known-goal.20180527023300_1527388760450_0.9564254944114514"},"_hasShrinkwrap":false},"0.1.1-well-known-goal.20180527034843":{"name":"@atomist/sdm","version":"0.1.1-well-known-goal.20180527034843","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"feed5160433d2e524f36213012a06e61e8c1c257","_id":"@atomist/sdm@0.1.1-well-known-goal.20180527034843","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LJ5MzdniiYDq58rNT9I/tYUDtef3grbzkJ16393n6vQJb0fjXcnGjUexTdsjfr/W+LvaqNWUxLVWXZy0R7n4bw==","shasum":"37ba3a95818cb394bf10e19f146749a3bac00a1d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-well-known-goal.20180527034843.tgz","fileCount":845,"unpackedSize":1901530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCivDCRA9TVsSAnZWagAAPkUP+QG/AjYkMb0f0/wKUX7b\nUBYR+mmG9RToMjgAqdIrGHEHW1yxuFVcV9nyR4lObvL3uJCnC4n9H2e3Z5oS\nAoHupdTojDyKDsatbF+X+EkGhsmtBN4LLp4lehS0vCgfp2OLLAEbL0W5kmNC\nFsopUpT1GBNIRlhY1lridP/rZP9u78l+RTYpz9mMev7m4ttOdMUmlurV5t8q\nwA/DjIgU1l9D3OvQ3b8p/B3Co+PeVATF1XaE1rnBfvVBXZVo5CmBZpqCfqEM\nfqfsDfGUL0j0/b+08qiX6E1Jep/3XzETTRBmNuicq7OznFu9w7xhPQNZkGKm\nqE1NaiXdrjLNSVoOiZf8nVueGCw/+ZQvgPGyhDLD09rmKAfyOsWqf9P3erKf\nfKJLQr/SuJi0GCOTNw8R3ZGUHaqVKDhXxVI+xo3Yj3AGbEF6yjXCSc7CwmLZ\nY4fwT2+6X+9kZDo9Uy2DYhlPmNYjTMdb141NV5oXn33A3cvn6RQaUnLvxELY\ntlygfWMnwyU9JnllmYNfr9FVYkltcepXsPchVzMXDNmTDO6abAUdXDdt45rq\n1wyzp0eTuTrVIWMUinZVWXxokjgfLP+FauspaW1zXdw/JDad98zPZSat2Ptk\nIeGeoh6nsdBD5dpJXvDDSQWjjf/lFXMBp5Y6gcYRRr8sCgv4/lBdAiXaY2nz\nrvsw\r\n=VPhm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDhAQQ3vFPwpVOI2kmBGW5M/bEzwvVVF7C2rYJANMAmNQIhAJTibAt1+3WDi8GDBBWXHHjVCBTGRxCwW609k0pdw8t9"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-well-known-goal.20180527034843_1527393217001_0.207739031694959"},"_hasShrinkwrap":false},"0.1.1-20180527043355":{"name":"@atomist/sdm","version":"0.1.1-20180527043355","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"981bce2a63b0aafbb102fa9d170bd961dd30647f","_id":"@atomist/sdm@0.1.1-20180527043355","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-I/ohctj3qq5KbWgCWSuQpB/WoE6VDWY7DZfcPOVYEPFpHX6/USomdFKYnOsikXTuah9Tp0hRqyRr9bs99YIXpQ==","shasum":"f25d53b9abfc68d00e13910d86143e300d50d945","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180527043355.tgz","fileCount":845,"unpackedSize":1901514,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCjZfCRA9TVsSAnZWagAAVSwP+wfNS9h0Zja3VyOr24mt\n1mR26L2gJxIrFEXQrqPY3ywQnlJSfgq9jX269bQh8aFqKq0jsptU9EwWEPAf\nxRothsWPC1eu2y42JI8aG+Fi1Pa6kUqTt3QiUrNvJQOZrcyeit0St3lUSakD\nIc2hAcglCJF4BDWr1dlL7M85EysM2qiKml0azrwnP7RqnVtBCXS/7Hrfe8DR\nBeTT6Wr6NmecXUeQxmN5Y+uAiAxmnCxzxrFlmTjRC6DUJy/PpT2JwgVQXGTb\nkuodiTSVvPyU6ryrAznt49z97HwPJWKi8k9rq0PMexnI/mIqg22PLAPnWs9B\nnSg8i6TYWOEHejeddyj4f8EfzpOtLNy0FKcx4RcfxVCbDZ4tOHEO97dKzqKZ\nCxRAY1Wzbs+1XJ3NA7hbzq4Wai1hLK8vS+x49IhbiYpOTGWoH4XL5vqkbvnX\nbnI3zWpV82xPmmDXSAPv37CACDOqos4ZlTZjSKVGt0lj/toUg7CWgM+cyxfy\nZegwFMx0O/oCwf0xk+hljQwgPTHzs/oMe4564UDbXAL8iI3xAm4g+dCCO7AM\nUwYvRgc3ev1HwsKxrLPeUsy3kgbHPl+xzvldayWtR7NZI90vrOQRap7Dhg+r\nIrdhladcypz8STu+f0D0EJpVzgDte3g3+hLXEM9XrFR+lzmuKtV1Anae7Hft\nXts6\r\n=2fB1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDor7s8NQrbxQkNFL23YrE0/UmQ3rYvYUQO65vsuUQn+AiAPxMCxmo++5YOb9z/5nSFcyW3mWFjUwDjwqd0kkSJ0iw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180527043355_1527395934019_0.713549766196022"},"_hasShrinkwrap":false},"0.1.1-20180527075809":{"name":"@atomist/sdm","version":"0.1.1-20180527075809","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"05f3f556ab92f88c97ab60ea766a58b05ca17bad","_id":"@atomist/sdm@0.1.1-20180527075809","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HpO6XtC8nJM6xgaDbPsAE5RrIOP/k4iBXZ0i9kE3ea3tYvX0HIKbbVGwJ49BUn+UNDrviELXSKAGJmq9h6mP1w==","shasum":"ef2e9cb11ec15226b6f69d4cf42c918fa5c73222","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180527075809.tgz","fileCount":848,"unpackedSize":1906602,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCmY7CRA9TVsSAnZWagAAK7EP/3wU1Iqf1Bq6OURcEJE9\nzpbpjtb4jD1m+8UKD0PJFYgzLsJBY95ioEfoDDhEIv/IXKbPBjRuxN8hQ/LU\nWree0CCol1/TOaPm1AsofQ9RPip1E2ub8HUnhcoti8WEDmzfde1X/BtPc7tm\nv2lVOR5hfv82SHveJgQ/5VxOXJ25ijezTjvgQoBGPjhDAkU9D4/XtgrdktXM\niEBitU0x1Hn1lAz9wFhG3cg0S2R9d1Z6wTcRpRb70P1Un/R9fTefK9Mh510Z\nH9UXJ0kv2RcEAXTB8NZoFmScY1MvaN8urUKvq3W3WqYKikVC+Gd9hwY1sc/Y\niqq5kWoXmDHOyUlSQ1KZCYdmZH4C8EzFbe/M5hE007HocJzv1gTMF8jCd0gF\nYr1B3vLqvOT3apt0I29+FFFtUtTrgy4NseD72zC6TzMCD22Fwa/vWyBldZ1A\nQfIsMJuGIV/kV4JDfviSweQwYJTLPaBQY6R++wa19ai5jfrh0+qN2rasA4l5\nbNIOljcF/iwv41V9y5mltyNIhH2dzESeIZZKGhv7O6HhINsk1XXmBYgWiUYM\nYNT3HvHkTDOWa/B7u9sRnK8Z9ATdVExymcNJuIaWNUlKo81d2qXv2Xz/fUFj\nknOeePl+iOBgZztsanhOIUbVop9x6v51m2NisYt2JISPkVsprNQcIPLBwjx2\nBH6e\r\n=Xlxn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICO3BHH9OC3LM/E7fEc9kgXmv/vS+zwwaLkkdbghrp7WAiEAwsYEaAypCeWnDRTX5nnV0P4JW2gXpQSYSF20QUuEa4g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180527075809_1527408186131_0.9416242301581279"},"_hasShrinkwrap":false},"0.1.1-20180527081903":{"name":"@atomist/sdm","version":"0.1.1-20180527081903","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ca0e5da2198ee3d8c577a5cd207f9630f5acc0f1","_id":"@atomist/sdm@0.1.1-20180527081903","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OkeeOPy8ZklQRdIPrzSpErcCIf7xQ/gz9OCnFTTf5qEATdUd93sI8T4SoSkNAQ4Z4LGEJg9tFVk9pJasIT+MgA==","shasum":"75e3300a383c80f0c835c82a375c7733a023c8aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180527081903.tgz","fileCount":851,"unpackedSize":1907499,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCmscCRA9TVsSAnZWagAAoQEQAKPN/LqgDhXd+2dE4BHw\nrVBBJdFC9X0fn1RdADOvhfcpmCr8oEpFV6qmkJnLBqJ6GrPmBG5+Lt/92ln6\nGqYOrI4TcAfMtkC7t4vuyHYD67PKrvYf8ujWgm+tK9xo2497PWz6OG0n+93K\nzlHeQ40EQmUocH9/RWheHj8fhaIsD3r5t99OquvDkNqt8SH5WTbvj6ImZFJj\nGMOx4Ih3wKW4BBQx+TI7MQSXFOfdyKpwkj1t/JDn/owwy4OKkycZQuMOkM9d\nBwpXZbeYzPSTHZx76CMkghZ6qurAaRws9mj/HG27WJBnoHYYe+0GLUDQM6QW\nWsmU6lJvP9cBY16RLSjyZka48dBS7YQYu+GT31wWeSlqD/Tr1dbm9NXpfuSX\n8z6pmr5SnginzsG59zAGT3vS6Vm5e7RJ3+/Ga2YWxQukPGhHN36gsI3YANiM\n4IYcmrlEaQQaU5+LedT99qC970Bzrgp0voRIKJPWVj+Dmj3gntWHDxZW9ebV\nvOMwF2TJj99xjeIZZ6Qd7+1bcmCb++T3dqx1Xf1BlV6GH5soGaVJi8brZA4H\n2OrHZHZiOyzqN4xGigCzqPbCVmyB35EsxyEokix1hN22Nno0NPS9houm5Iii\nCrKcdYhk8KnlXmjvgUsJKMQXiOEwCaOBgHm7cfltaMVuoGx/+j+ywHehxoZa\nWgMk\r\n=obkS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCAXGtV1Mek/0eOwj9UF+5bXKYSppmUozgj11vpRfvDGwIhAKjTtrFZhKbfTrklBfWerHcMBa93c2FYpqpnjsgmM+oO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180527081903_1527409434603_0.25680805950202523"},"_hasShrinkwrap":false},"0.1.1-20180527084208":{"name":"@atomist/sdm","version":"0.1.1-20180527084208","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3d6bcb53b9bb12e7ccef949a166a9b925936c55f","_id":"@atomist/sdm@0.1.1-20180527084208","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FFOvvjUOLl36pHGy9LeFQsYMiEpet7XlVlkH5KIUZXvrClQaRB9qZLYplLPpfuA5RVsiarQVod6QbmTT8x8U3w==","shasum":"041a6ba38d962da8f19583862fce7d74ad0cbdd8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180527084208.tgz","fileCount":857,"unpackedSize":1914777,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbCnCECRA9TVsSAnZWagAACPcP/2lhjGjqbvC95D3pxb6L\n10YfRqc79R1+NHmHqu6hOvoit0hvhVj5HlyYgvs+U7UdH6zYEGxq5bZtJsO+\nS0s0gnOTVVdzalz7xKNjUyK62Yt17M/cEF/bBGlUjvrO9tLk8BqJGFyHME9s\nGJahbVbx8Sgy9Y+WNMuBKJ6oqXrKtbR3HH4rOTl0e3Ub9J0As/X4O7nCj6dP\n4UHKUWIN1dDBVIhmWDDMtCXid0xWkUkWPu9oD3iiZsedFkTJiMq67WM6vW/V\nbmVhch2pvdREz4UyF0OiOCrTwikTVfQ/CToItrVKiriANSTFC5h0d5MmSVDz\nqq9UVtzfQOOxUOLxsZe7OM9C+Zj9AAxXtYo5y535GnDNk63IeLwZnAx2CAK5\nNNdRL2lKCaUr+RkapajT299EHx2oM7oHc4jlgab7TsJjL21A3Rb+UJBj38qd\nEdmZci6GGBJQs8sTUFRNavvJxJDO81LDSxBmEjNfC43mnBBuZ/8c+WZ6ivUN\nQv+Tod5gVN0t1dW2FZFYl+yzl2txtTMmT4e6udLHORgAZCfc6XE55rbHNycA\nOrlbuk1pT3qj82UXL7m/iR0bsumW+LVjJ2CJUNBlHFQxa1BIKepQEENt2270\nVrGwo1DN/UWq+1a8VUtErzn3yIxy++Y+NV9Q3F4kZGAqIniVPwvOoixP5s0L\nlLS1\r\n=88//\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDRM4YNQPNzVAQRcyieShXJEOIL40ELhvhmNbIDhNZtcgIgMO6v7NzEOfOrcoNJ6hTKraJkADLdd2JsXk23fZKdMZU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180527084208_1527410818079_0.7408419975764884"},"_hasShrinkwrap":false},"0.1.1-20180528004157":{"name":"@atomist/sdm","version":"0.1.1-20180528004157","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c643abb44eb2dbb7ca62c3361ac32e72586e95f6","_id":"@atomist/sdm@0.1.1-20180528004157","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xn/2tLJl9Qcm/sR6cxS1oosYEibcVj2PS02XtXf0KxyqN4AfhlMnSD3Y38hbWdhvuJNEz8Xcsfff8KVp+aFz+w==","shasum":"3eea1de4a1ec3025d1bd39de02c844b3e9ee72c8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180528004157.tgz","fileCount":860,"unpackedSize":1919943,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbC1GCCRA9TVsSAnZWagAA0e0P/imhXks7eXKkCsfSY9m6\n22azBlgOkicBTlulvZ3ZFga46GAjdtSQp3wPFzTLsX40apeQa5U4v/jrXbrt\nu0xB241wzv3QQqYu+bHjPDWjORXo/3/y67nXfXv0mMMnPZva9Rkl8K/5jntP\n9qTRQrA8yQlg4ZAg9Mk/kDXFJGeXf/XHYwvjRSpUSn/EfOEXnmwiqzL737p7\nMGJPyA5WeIvI3mhC7pZx02dAohFs29sWfJwzusEvnlJDEPk9zTyL3HPSZNTj\nnay7tlwdqDqENbmcWXZ1oXEdQDk7hWtJiuFK6e+1ij3gxJWnKe4qZNBWYz1g\nDRZBadFD+qGZA7GSJX2QkyQutGAs67aRaXX92Wb0thBX7/deI0N/V5oYE638\ntK1rasKXsdRKQKBFRM/DofWexTLLSes4v4A7uzL/aPRSLcQYCNO1GrtIqQm2\ncRtI0dLiIAaEhxr9Jxaa003E5Nw0UhXOzyHAw1F3lGF6ewDB778jSgoCQhel\nFDLxL9YuBER10Bm1/QC9CXXOJANRGYet3h7xdZK9G5qzq7P8kcxpQS3BAfgF\nM+14klj8yXaIyWM3HxhKMEokQgrrbqYlSpTWbT8QcYEu5zWBS5bAayQRUtWX\nL44tTiqfnRsw9tRtY5+0MCId9XGB9UxfaTrN24JEVzzRvhY0o3yui05JdD6Q\n4bz1\r\n=dfto\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQX76jWCK5aVlmwJrj34jHkhEWoIDeBeIhpVlFyVkODAIgZ2kvbBS0mgmBPQOGoZK3hVg0RzwQ4HwPIld/MLBdEeg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180528004157_1527468415970_0.7275891750043879"},"_hasShrinkwrap":false},"0.1.1-20180528035158":{"name":"@atomist/sdm","version":"0.1.1-20180528035158","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.8.0","random-word":"^2.0.0","request":"^2.85.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.108","@types/mocha":"^2.2.48","@types/node":"^8.9.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.2","nyc":"^11.6.0","power-assert":"^1.4.4","prettier":"^1.11.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.9.1","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d4db0cdfb0a8f7f676197684879f6cfe3cb0a56a","_id":"@atomist/sdm@0.1.1-20180528035158","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sioa/kun/W9q7WEqtuTZzGuUD4XFGa+eDrVm7bykQ/zNHV8REPyexF+xaXFwSlDNArpRB+AsqHgY8TkjBwgJ1Q==","shasum":"d0d931a4d1e19a5d1230ceeb17918b1ed6f9e83d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180528035158.tgz","fileCount":863,"unpackedSize":1922618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbC34SCRA9TVsSAnZWagAAwZYP/2bQuq3ipgW5evBU/V9E\n0/UfwNFJxEid5Qh4pEfgczBDFTOMugwen6YraBP882ZlYQQ+aoJU1ShqoJy5\nB4Opd9n7Xpu+jHH4RCKyzZ65ziUqWq3gIxwz9fUn4//kAbHHZDpp/W2FFj3b\nT4ScBf7+17mQV8gyjAGqZbaVkv/htYIsWUKnsZVwuTTXt7XVgbEEYQvQVgHe\nfRTTPMUBOW/neOg8xE545hyItZTMWqm579sOG45fE3ih5+LHvdanPAw/dlEC\nEYllgmdH3Amirg8xZ+JGKrTSYTFRmuNBoul2oklDISu47d1/i9aCX9/lccXH\n2Zzioj4zOTo41r9miJnJ7TcCxlmGBnlA2tXR7S9BJpboj4bYxtARL/jrNFPs\ndpHq5kimWtMbPIHQFJicf4rLUJu+TBOk+4TZQwfAs/l1kjYr3jVKBOQSIMHY\nVvjhMV6QgEC4H9vqEAclc/4fiJB/uwA0VM8YOjw3KZ5C0c7tZVVzhfrbFVtQ\n15MViBOQWYoKtKhyenlEXscdnKg/+uoK1ZFW6EEYXw+LaS9yxjiGivICP5v2\nIY2eS6AoOIt7yiImKzvJ05o9agLeVc4At7fZ09v46s+QSS3y5zDwn1aPHySY\nLMqajajPRWX2YKi5RMqOE+pz140vq/Z3P2PihmYcxYLwaKV05WW4eMmxYSr3\nytUT\r\n=CNRK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDaUxa5GQ0GPvEQbJBFh1g2nbO13rlM3Hc6BJKuDNP9bwIgSMiU+FOFeZ8zg9uLnyoNEy4IeBZObaXmuZwlQiNspKg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180528035158_1527479824717_0.7950663851073292"},"_hasShrinkwrap":false},"0.1.1-20180528101113":{"name":"@atomist/sdm","version":"0.1.1-20180528101113","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"336ff14e8f59d05b1e2c54f1f2a4fb0429fd36ca","_id":"@atomist/sdm@0.1.1-20180528101113","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-StJfpW1OR05FMR9Uh7RKtMLgnKe66BCM1kDzmrXNJU9nQm0+tuuz8OEobsyr+0bIUjy+zUD86bZz9JiKN/g2Rg==","shasum":"87510fb1ce6a030d310bb97d45b42ea5b6b8fa6d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180528101113.tgz","fileCount":863,"unpackedSize":1922622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbC9brCRA9TVsSAnZWagAAjxgP/A6zblmFaqVzR9S43UT3\nXRBbrH5Cd7J71m3y8NIXw+XF/GeViIis5aEycCmzCYn/KaAuq3gMUsUrszPC\nkp5u0sh4ma27bwzWc32RXdNQMI0ujEGOcD6jJCDZY3g+bLeRPNeALobekx/v\n0YP3AR2HpP4THNrOaBcZ5eHePYezzsPaFMKkffRucnvirgFsyGDLqWJmeuWq\nA5fe07JIlVLgTiF00D79hF7B+3zWLtr9+m0NiCTrbkkeVjJv/b2IflmBOxEQ\nCcKOF/RjTNx/LMHy25GG2fK4/C6tC6TqEqOz5lj8l6zar6Qh3FARHvy63LKf\nyxOmTMxAONVKpkpocXZesSwgN3/XQCT18LdEUVaRoVS3tTHVrtbvpqiSkYgg\nfBo9F/qBAQWlSn5SDwrx1vwhPbiiZ/od1rk7ELQ6VtwviXOcAt4owlQVmz1b\nqXKVbYUIKvasFRsuk0Yo2lRHvvLt57tJb6zRv3moMb3l5tpxmL+86DmGmHaL\npmiW4nyXhUJ76ykUdO4fGcAieSaoANkLcojVmnWPlTdcDig0bJ6KYlL27qP+\ncEzcI8tEl4uM74XWmfHMYwuLdwFbfNF3T/M8mnq/EWOarv35f4LAba1ZNcL8\nha1J6TqCeEzRWCWmt8yQZcBFy7cLw5pD+1Wt0biA2a6AoZ+/CfIwXLOcGxaf\nd9jT\r\n=s1AK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZIfDX67GAwFDTOe+2OKmpj0OpJ/ZNt4nNEZxqO0kiBwIgR1F7Tj6YX3D+mL0tq9Ndodu1kmF5zmqAPHJXR6LXcg4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180528101113_1527502569807_0.5778790165956773"},"_hasShrinkwrap":false},"0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540":{"name":"@atomist/sdm","version":"0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"56dc4d053bdbc3e068a19b373c92da26c36124d4","_id":"@atomist/sdm@0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Nfaxk7iNMOASYnDKXWsOeC88lgWzwxNc5wq+NW135RO7vJt/IPxrKRBRysIRnxVoIAIn/s7AmMGJzJrQY8K/ZQ==","shasum":"822e249a002139e191894620c7e9331128a4c950","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540.tgz","fileCount":863,"unpackedSize":1922866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDFHRCRA9TVsSAnZWagAAZxIQAJmdVKICEIQmJxaToaDx\nOdxV4fwVreEClcLnyF/1iAyzY2c34Qd3WEDVabL95zc2vJQOluqULeSnRZPL\nw6SCl+axKsY6zwE68jDTI8Tj1/gwMrX1kb+3IdduE6lZiJ+ITuPLTnIg7JiQ\nZvp42eLSapChQ0KQZ6ZuM7tmYNQhHC26RAe6R2PRbUdy5nKnMzACiwlIy9i5\naDbvfdSQwJPwOLDe6RBJ9QTjl2mFs28zni413kyF6VFHu3NjC63blpmZxx2H\nTcku2OU2mYoQuz23T9e7u1PO0gJb9dy5naoRd3q/GScKRQWTcLQhrrXzZEIz\n23YbE7R8dbtDlUbdwbzFHb5rPG564Qzqb66jO/+qe7zqunQi5csatqaSQqkR\nRdCBUX/PKsvSqBZJAZOS85pBnacaCfcfdmAZ4XF7zBogdgoZJljFpuTOSmz4\nVIeD+W7zRm+ay5TKPPnESsY3ndKkfq8+q3vJJpTw+ljOyE8CDZ04VeJ/l33/\nU7Egb8cupqFOXM3KhGt7gwC7FOGDma1DMJvhAoPpercObWhnDCf9ah3J2jdO\nAF6t/N0zi8TuQrs/qT+B3SJ1d5ARsR1DHFT0dKZJyhSNs1mrKIvPs2QeTIvA\nIPFdf3wOFUZn9UW6t6KIFKDvFRCgIT1Ob/4RRtTvXWx5HBkFZoCmb4ujEj6N\nblyC\r\n=ka/6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjFxTQZo/MxmzVYwUXsbKfPUr8a4LXzTuz78LyjpXWZQIhAKShlua339znqSutP/4dS+uX/j6AVVWyU1lcwsN+JIBp"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540_1527534031667_0.09486922347770421"},"_hasShrinkwrap":false},"0.1.1-20180528232407":{"name":"@atomist/sdm","version":"0.1.1-20180528232407","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0a5994a2ad66a26f9bfa13de3dc4d67c9772b368","_id":"@atomist/sdm@0.1.1-20180528232407","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oUXwrTS3JRlkKUQmS03Otinwe2qJjibtViO2UFQHzo7B+mUuKeb6kQUxykOILhkOaMVi8WKFfWQ/AiXk0d7YXA==","shasum":"5c5d6f485f6e016a4de352c2f134897608cc907e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180528232407.tgz","fileCount":863,"unpackedSize":1922706,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDJDBCRA9TVsSAnZWagAAvg8P/iZ/mAli1XL9u3+MI3LS\naDwNgtTHlK9BU25pzt4RHfgb166N3vZ8w040OZJLFvWh1lgkSQmIXT48PYkA\n+aNSFquc5FzqdTyNXuX5fuuPIOORoMDTvqsO4ia0Si89WY0w0bHxPrEyisNO\nao3BoiMG38NrJvRbk14Ipa3MwOYbHHcu8lWYzHR/oh+/R8DKvGXzkHnPsbN8\nyzHE6+RkLRhlzaUhyTPD48ONnI9TrzkviBUkZSJ+HRLlAIjwudU5S4odmypR\nu6N+UwiuqGCjwpjq1W7XymfFC17l1XLR01a127ouUuqVMXlBmAPxiUhM94Tg\nRLL9Uqf40iaw0XLXSmriCIJH+7DWsLe50VdsdNGZ+oE6gAIc0OqAYmzx7uz1\ncZBoXoG8zLvDJ8KkquW3pfCmxyuXK1GMj9Ps9gUtAxJ4jB2h82D6ms208NpL\ntgiXJQ4UlqQepiP3dbxH7Z9owWXdGByFNs54e4cq4BbVWU6XvYUE+VzNH7nv\nbXIGX1RQH4FyjE4xifrGg7SrKxsEUIdWCN8Okt9KQYepqLsux5IB9XDUKPuI\nPw08wZZt9rjlXuuuoZE4MIdaW/y1Uyd8U7Az1yp+aRyk5gJWa0jprWeAynKS\nx0ycKj3i9clf7m84LVTDwtV/hYKmHgZVcJPA5YDT3fOPG4vYkHqKWj7btgFg\n9MOz\r\n=kxuz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSTMGbLftdsi4hik64JJgqqJ8bqEMmCDCxp2SzRmF/uwIgWI6RWStN0HNKuqFMM0T5gPgaPqHYI+up15CgWqXGiGc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180528232407_1527550142910_0.8488508135347379"},"_hasShrinkwrap":false},"0.1.1-20180528233959":{"name":"@atomist/sdm","version":"0.1.1-20180528233959","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2787e78357d471968935600c6b3906543ba7422e","_id":"@atomist/sdm@0.1.1-20180528233959","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LxNL8F5ped7F0ukKXEomg7veXvh2c93QOK9Ca8XwojHHskux7VuevvcUbDCfPtnzXX0ZlpVd7OHZDE3zohnnkA==","shasum":"7614b2b5102800564b26cbde2197e01e9cb2d559","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180528233959.tgz","fileCount":863,"unpackedSize":1922912,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDJRtCRA9TVsSAnZWagAA0rEP/1MwW3+QLzz7VzdfEJlU\nmVDS2MO+QMTzFDXnJTU0KZW989XwWw6pCBO3r4Rtb0hCWjM+/7ubVd/LFL40\nAEhP37tHFZmAA0jyXrJg1PmfhHoEAKemsCdPspwlRgv0IRu0Zsv94cbHzHET\nGfGfGJk8JyrDxn8eNhX9ibHMTQv0Ws1sRGi4jYaC/pCkFY7nL4BurALmiXsH\nbRV62JkFFNGJHihpbi3MeBlUSV+qHsWKjA7ahJTAfbUYzdn+RJFXPjsyg368\nn+i+uRgc6BtvzZLIPRlWz9QR1AIB8ct4nSSJ86HiAbZqwWrHmbTAx9kGO8B8\npfCmIFoHL7rSvuP2NPpW+ggiRBrKH65mDyG0t1CWTmv9tmAxXmNPUc5jUeYU\nWpvtH6J+pdZJrJBha8pqxF4z7NfGdzv4cAu6yXFZaJSSgx0RCmFc4+YjdeN9\n+jPNnDKT+Ou3pZ4U3mVDUNDPMu5/8Rudv982kIV2a8sL1nViRGJ6mCGz9mbG\n2oXC5jA3LqaRsXKbmap4Q1BFNJ4gg+nPgSjhEn1BesK6qWdx+H0yvkmjI4yO\nfLWKTdtqXH2aANVGr/mUqu+4M92J93YSD+f6xMi4n9IxhYJwMUPxUl+415kq\nKg8eRlU56HmK2qMdI3OyRjq0oRbn9RJjWkHRolPjMX+E+cyAChbSDGuYxMGN\n1Znz\r\n=WFZe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFS5qLWG9Tq5IR7R3cpJZRWWh5AuzD+kuG6ZzIPxkz28AiEAk9Ud3VIdRFCRWi1ugyrz+pUhU56awrLUerFflQ4bw3g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180528233959_1527551083606_0.011029308025497286"},"_hasShrinkwrap":false},"0.1.1-interface.20180529005506":{"name":"@atomist/sdm","version":"0.1.1-interface.20180529005506","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/DefaultSoftwareDel.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0f1847ba15de8b5133c75fc14acf48ecfa872bc1","_id":"@atomist/sdm@0.1.1-interface.20180529005506","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YOiwnUEVxlZYepgA/XX0y91b5sB6Onjo8LPKmqkyBPz1k2T2OJ3JrJERwzOH2b+QNt2wRiQRouB2nO9Jbi1CkA==","shasum":"8cebffbb95990d9d4404145b3a9edeea859abb93","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-interface.20180529005506.tgz","fileCount":881,"unpackedSize":1941779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDKYQCRA9TVsSAnZWagAAIMUQAKIleSugDZqEXjl6VQxq\nn1VasjGlYOWeuPmzDB9xg4RtFaEX+E0bpPMl2wO8DsfLWLwtOhMxHyt9oFTJ\nYKR/wvs5kb/xpDdhG6+nG+ipta9wBV2AWhuNe6ZE5taS3zmpEhVtm+6ehP8x\nvsoG+CM3gg+XNN3jPKVxpR+Tho6p0BlR8+wtXvsFeU3XNGoMshc2iUgrACQu\npate2ktFKX+JLhdtDebCdyCsBK68YvqPVuPVB+7BSPHTR6TxI6KxAx66ysaX\nBvxQ1EUDdzGZlSHfWVAqMHikWKbNtjOszqej3zB0D2Lqy2o2FjYPmnFITw+D\nsXwMx5srlSxJZWidG3nW5zgU8JpIUSwzB97WXRiV47WPNUlr6Gr8iq6ed5cC\npWTEbjLghvXbhJAuttx8Utc/2mZDt5fMsWGxekZ8m9/ZChstUQsVhTphPzJD\nftBzTmt5N//9EsX9uOqXtVB+2pQLlMwgyA2B9a/3fElTthQ5afVVzUjsbjNS\nP2nd7knXUo/vFlTbNpx4eEdT2NVQ7UdmTrncnoqy/6KLtlGm+WGRl7EYqMdv\nPg4+cxt5C6prYXukB8KeK4L0dMsO+Wb6ybMyfmc31EbtQ3vhRHxx8bthydJ9\ngIiSH21TpFNRvtcD/NCVP7HN1o7mPZFWAIvV1sJ6z2ysYnHAVCB/txaL26iR\nnutC\r\n=xdji\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC7Fl9FOi1zzvsnZN1QDVgbJKq3SRv6Uizkf9payc4ojAIhAO5WDFAaowbENaE0q/tWEpiaMZm/7WnOZmUjQVpq4VGo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-interface.20180529005506_1527555598936_0.06588064060268461"},"_hasShrinkwrap":false},"0.1.1-interface.20180529005931":{"name":"@atomist/sdm","version":"0.1.1-interface.20180529005931","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.*' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8136e25bed479dd6a2d86e45d1d21853682e8677","_id":"@atomist/sdm@0.1.1-interface.20180529005931","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l2bMNI68NSZpRAZZWzIMYGg1blAb9AuN8TZ/tujg0l3G75xb/D6oIyDp4s88OJmch4ZMD2cXLn+XDU72xIhuHQ==","shasum":"3f5ea1541d5743ed20b7e2bedb88531ab7d9cbe6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-interface.20180529005931.tgz","fileCount":881,"unpackedSize":1941775,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDKcWCRA9TVsSAnZWagAARmUP/3iGRoJZ2Ew/zbZJ4BOi\nuKApZIuWC/2rJeoUOPPRodFQQLC98jnOBPHNHhH2QecP8PtBP7L3nf6NyxXv\nVK19ZtDC/2XhkYrTWPfbyW+eWmRjrJsEri2Nj3vpwbJyBwgFWAgEpRZLwufw\noiSQQItgtNWObZL1QQgqRHHQtTY7yALE8kHtqiovhFc7/AExKWh0zBrQb0Ws\nxDg6BsUsP/GE3ZkQVKCfi3by6BQlDde/yOhlNqA8i+Fe6IKiXV9sDC08VbVz\na1B++Y8fgKVSFwmNm2TGQIZzhmAiDzgtApcyFpKUcf8dj/LzrCxzyGhvTJNP\naUxqkHtTBk1kTQqKUT3fm+7+tWSpUthNdMJkEsWOUMWpXCi4Z4JGeH7OyJYA\nAx1ybMRcFx2u1drDfAy/37liPXP7thPrDJUQ5Cou/5DY19StjBB66Ocwk7kI\nOzsb7ZQEO4HllIGwF1hiLDtfMjI07Hx2kQWnjqfL7j3NtAg0rwIVtF0qR8i9\nr+0Cxg4bGrrm+o/u9y8Ez4iN2BHSa9Xy6he6oylDD7YtVVgV8Wc9VUa2l+4l\nNDGjkOgDcyoGj7mK987L4VXYUzYpGSMB1iH+5Iy8qLegzJB+k2SI2+gJNn1p\nKI+ZJ4zz41dRZHUtiK7FWLxp+ZgVU7Kry6R5cIAFmtUpI8l+O7XucvhVWPgK\nufGY\r\n=w4ZD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWfyotBMWRPFUnGH1jn1PAJdKv6odzURFUc5RBBoyTEwIhAKCjHbWGO41dU9TuOFp6UPlcAKgtqjd5Mjx0pjfi1np2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-interface.20180529005931_1527555860501_0.5516686946604523"},"_hasShrinkwrap":false},"0.1.1-interface.20180529012530":{"name":"@atomist/sdm","version":"0.1.1-interface.20180529012530","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"32bed4661f0323816fb202a91257fefdc26f5015","_id":"@atomist/sdm@0.1.1-interface.20180529012530","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l0Yt/PIWK9QXiKSxepUyAgyrnkZWpMJtxnRYyJphTGHd9m2bp0Kw5mdH15q2bY4bILpZo6xRMsVl18aYuyfaVw==","shasum":"53cb322b0b737438a6d7663f7e72e36fdef8ba3d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-interface.20180529012530.tgz","fileCount":881,"unpackedSize":1942059,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDK0sCRA9TVsSAnZWagAAT1QP/2+nT2OBNKpt3S9MgVCz\nD2ZislJVUJEweL5gx8bIqnhrFpFzdOcSbAXfM5eXSO9I6m6ArJBzYK5kSqX+\nSnPpemnvVd/13cO4AIsye9Zjla0PWPtcHXdGvJIaTARFj/d6eOg90xPV1EoW\ndfIqV7YCeVOxpvgsUO7f8HSWQBD0DLhrTpe75XMlej3ZdObqORSkgHew+Idv\nwRgLtfUn33/vRgxAY6uGIVpGH1/VAGbiw4ybMse+/2XVMErqDpq2B6f3t15A\nITUdM3N4hmKhb02tnaHRizLIjD25RiJ5AnaL+RCoYs+DFASQu4BZZlOr0Tn9\nih+yAEaVcnWuMHE1A4WVgr/xCarX4t0fyy4az9JWbnlL7RIZlbgYYRpgAKMx\nF/6aGmn+Z/EkyOBfEZtJSI1Uej0cFWjNfVk81+gSim1JybkZKaLC1HXnUCYA\nts4p7ZcxtcOl6kd3jBkqZEDFGrssClDmMveUnwLa8kK9BxYnbIJa9IwBskVP\nFeauS5XGvYSk2hGo2w5sLh75tqgumAOGNOnwLW6pHl8lL8GmLVgmr8wNplo5\nuEYcJcavP5oKKs5joJJYGQsF4DMycco0xa0dVFOUSCyEYC1dIarLzHP3F8Ij\nPu5qqSt2Py90jygHEgDZ7C/9uv9M154bbIQVZ0FS2g7eW/GC4brqvBqS9Mcl\nErcG\r\n=wY0K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBw4vD9opZiA9DNZIBLT8M1rykqJBE8L5tIdqaw20m06AiA3stJmfvPFXT1C0algnYxtIs1JIswhBITU1t39PZUhZg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-interface.20180529012530_1527557418589_0.2950909832547661"},"_hasShrinkwrap":false},"0.1.1-interface.20180529015252":{"name":"@atomist/sdm","version":"0.1.1-interface.20180529015252","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"18113ef029e7df111bf24d360d8378daa56c1b9f","_id":"@atomist/sdm@0.1.1-interface.20180529015252","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-99j0YdVNfz9qA7FB+9D9jLHxAUVkyHnz/AHUxS4Kcpn5URFchLl8u1b5cAVuC+7wwi6SbgFo7J0UO0i2LWsCRw==","shasum":"1faa69a08d96f59ceb590b694933d2bb896ea5a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-interface.20180529015252.tgz","fileCount":878,"unpackedSize":1938835,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDLOfCRA9TVsSAnZWagAAhHcP/15Xop2Ntnwu/sRxi5xc\nZof7b2VNuLnIpAdSEUAZxUZYnDZ7pW/cx6xMuHUkuFxeIPJGrBKmO6A0kpiI\nJOt7XgcOEltS6A46zmgNGamF70aUtf4o34pA04xGG17N3lPato/t+R50u2qa\nM3OxGu1ySBWl8ElehaOKzwWs0DeEaLIhA/7Bpa8xG6CRz1g0MjOmqCKn8Kxo\njo5NXh/Q+XL5q3FTkvE0HdQM2+tcfHaZw69nvGXJIyRM6S0676mpXB19EyEC\nN9xIM6ho3f3xdivESEzrDOGeD6XDKrxUpKaRiFq8c+yBdTAe0VJoV2LIsCsJ\nln5lrLkJV2HrYfSA+fGG1+J10OAyPtxEFn5qH5qU+VZV5hn5lDBTQ7sMpeGI\nUXdsdwP9e0GMyB2qqHvkX2qOhwH9bHFKdAPrSJj8E0izBLBXODJwc1oWJcwN\nFk5KsilrQMRRXdaCpEQv20UKiGivhhHlUGKWfDNQloTfn4HMML6+/3KgKhrD\nIuQPQOkEccsg6jay8/B+TDmGkOJ6S9EyFQxJSbD9ysDdlr8ed6Af1HViBpqq\noyKm1Prs7Z24Vvo40aybA3xLgTyiYO79FyL4yluq/Rb0OqiKs/9ESzbMxt88\nIqs7IWDDMde2h410pvhnUy4VoD7bT3YOeJt9t5KZLnEI7a3qVLpOoT4Q5pIv\n/Td9\r\n=4HnE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC2dQ9Cj8VOTt5J9RBM7DXoL/1qPewJECx/J5ujA3ZbXgIhAOtItFEj1czmuDFXrBp62OABudSQR/clQCCBCSXZXOtk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-interface.20180529015252_1527559069418_0.1585939916582968"},"_hasShrinkwrap":false},"0.1.1-20180529033448":{"name":"@atomist/sdm","version":"0.1.1-20180529033448","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ce2c0eadbd40c3a2c687fb3b97a890d3816a2096","_id":"@atomist/sdm@0.1.1-20180529033448","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3x0HFoZrq27Wx4TOaHpIwzTT9rYAuMyC4pZfJ1uL2/gNdITL6i06rUElpWvlTAAkyRYGbFWdHbL12qYP+JkSSA==","shasum":"4ee6134506781818cc9a2fcfdb00ad617b638352","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180529033448.tgz","fileCount":878,"unpackedSize":1939031,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDMt5CRA9TVsSAnZWagAATScP/2+xzPa4b9jCxAB53Ccb\nyzaB8cNsLB4y1MSumMhAqwo0lY0g5jCH326Z429QXqBCibWdEcPw8JWEgNda\nqBkyDZt/f0+KLMjj9I/bQ96rLdr/qq8LW0kgHtaeBBggVeuXBPBTPfGlvL5m\nsrg9WvPgJVWpGzPNEXfoozjzLgJxMDihFtoe1IY5dJxjKvCeP6NAfB+LWcGm\nS+gQeqoF8LMdlje9+3KvZkLacBk3YmvJJ1aIijlQ9bdgeCUXNIKJVLQFNRTf\nrhtv8QeCZudZ+ZlERAkX7wRTGq5uOiL8eBQoU5lwuUQ+n7AvuCLxVKI3A/H1\nTG7QkB5cc/lRaTnMs8D1aTcmbsCmBZ2vfr7a805Or2roXcmfjogWSObQRK3J\nkDercsaUFFmEiz/muy+A5zuUR91yOavFsPuwrFuWxBpLtF4W9tVWEWN6HVtB\nqsyyv8wlewA3PqR+/kquvyQIafNuVi3EfoPszgI49IFHaWRcG8T/1eZ8UVpx\nqLeJrl7AtYrQ8TQcUU+AdWh9Cyqmga75aQFOko6+UA7RN8D3qLTlbeIt7qFe\nrkrRsUwBiKjwH31BqZtPfMe4lyZpXpTJUXvEZRT/YEpktvuHOKLOKGNLV1PH\nXsPSSM9KB5xoODidzLC0AQiKm+1viDmFaWHCkn68xhTLb+cIJ7i6PLTfgaS8\nN3y/\r\n=btZa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgSgqAkceVh7e0LcIxz79wQ55kWhDdl6wiw4W+phUuFwIhALQjprjgiMntW93J51fM2ppKmCb1JHpIxYZgSPQXJ2pX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180529033448_1527565176153_0.5615006281280259"},"_hasShrinkwrap":false},"0.1.1-20180529042523":{"name":"@atomist/sdm","version":"0.1.1-20180529042523","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/ExtensionPack.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"595ca50345673fca5f933e28a5118a2b304e5409","_id":"@atomist/sdm@0.1.1-20180529042523","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cUTG1jth5YbS0puWK/37GFtjYyliK+sFU6k/qX0DMI2/HcygpS1bgObVG55wCNZKf5kSpq8btCtUrK6qG0bl1g==","shasum":"9d1c2df2e8e793826c8b4cc56c1876486bf8be44","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180529042523.tgz","fileCount":881,"unpackedSize":1940133,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDNdcCRA9TVsSAnZWagAAFrYQAIs31JzzTHR2ms7nPEMP\nV7r8IyP30FzmljQrD/aZB2SyfHeXZ6eX0oCXKUXmN0GUdApVSzg74it36x4D\n3Jn2KYRGKD4+yYe896Jd7JZjD3BodI8qZVWN3/+dyswb75sN4Xusd5qs4jRh\nTDynREZL6ZUlsgur265wT+nwEnJ4ItcNWn2WBZr4U5BVxckJslANLwBHKXUC\nZNjGqqmByNJxxYinYYosY2CX56pwSeERG5+wlxg1FuqxRDn/d/n9KiD2IlvJ\nNsolBWlJfzTmoRKjO2+ppqF/aF27hiHySfCDXdZ/WB/wehxO1pXO3KWHcDnr\nS1mzjOZloFBmPwZDikelm0KLjLnBIIy0I3AF2bEFmXJRGi+8illxYlhyXYlB\nAJRxo5e5bGyoUGanaJTXP/GZIu7KSne+DKAIwuHuoKx110qNsG24rqOSq7KJ\nNkvKWSf1sLyXsw+iz2rh+j3JmzY5KHJgOrMN3h3KWPlOEMu9AHRlgPUZichX\nAdlFol32XnA0sKxXmtjqI6Jnhj9vCfMBEuph1VhH+6WtC0r5psoyKUA4lQrJ\nSlm/rQ7ixfiiRqf3zPdUG2s1yKZg2ksfbwIxOAjFpy+LoFjP57xsJjFyc42R\nbYz58IUMP63YL8sa+/GbrjffjhU0Lit/amYyuOfzYj/t20coVdLP8IKNj0XL\ntl78\r\n=sh/u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEvO5Lb4e/m2HuBnDn69hIvejs6MF1b7lK4f9hMXT6RLAiEAwq5qb/iirqCrsf1gB+kgWmUwA3uYWFGbfXs4hQwg7wE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180529042523_1527568219347_0.9264556437307685"},"_hasShrinkwrap":false},"0.1.1-20180529045407":{"name":"@atomist/sdm","version":"0.1.1-20180529045407","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/ExtensionPack.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"186c62f51a9deb4622bba8487ca5c6de0555cd6f","_id":"@atomist/sdm@0.1.1-20180529045407","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-58fKQLIqucUzK4t2dC3oXs67PSSEQPEXlFtZSlgmA4KIzH2VpRYOHYR9nYanzVJNvQY1YExL03n0srsG8T6woQ==","shasum":"518375c3f4608dd1704d1b25eca3fded95d28b0d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-20180529045407.tgz","fileCount":884,"unpackedSize":1941336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDN4YCRA9TVsSAnZWagAA1FkQAKUHOShqx7+A+gzmPvGu\nT8k+64tZI5aE+N5nv746xSd9b7JJIrQDpbZUFkwe4jNg2S9xvJ/iQ6D9c8NS\nkt2N1yXaW6k2pjEAbCYJv3V0fLDvK2Ogovh0uN6duK6Q32hqmNcRMJdy/sXG\n2EgTy3zdbiJR12e5LHp8UgCW9fxPcv2H6VRLsTM/qiScYo/2Wkw1M5fY0W7f\nkOi0nhgyUBebYavpLNgiXpyR8KpgI7pqPmQRYIVNrFFsd6v6EgL45jIrF40Z\nva1YKzVhMLtWSL6UTInVGGM9xA/aJo7WC2ixR6c7I9X+ld88W+mKy5CEut15\nC5vRvcsxzo/i8yIjmsVFatySJxhNh6LJ1lyD6ja4zToSH1G13ek84vQaWWgU\nGnQx9G3/bfW0TND3asSQ4BYEsJn3rd7q/ZaRJ2I+Z3AFeDx5Jrb+yINZM4sy\n+MO/4D8oQDp7l1VRRITzpL8IYiO8xKCHCDQDvwxkgQ1YI1i8rcSDecpQDxBL\nAiJlTOqUzZcULDIXxGIPy+PRKyKVQFeORh8e1D99D3RUQB7JsilLYwvgtQXK\npHjBOGTZjlViXQPl8NQPp4XqfAIxxTRv5nV5ITA5u9xFebGK9RBWNaI3gVu+\nKDc1SEOiVildmSYqfxATbQCGM+cCOISmkUH8SISCmnhmDrLL+Ax6jXeeNIO6\nhLKC\r\n=Okh7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvk0yq2a/Wbg/pET6duIpNG8g3Vw+3tHWpWmwa8z5F2wIgIE9iv/2+S7vhM+l8+XBYXjuQ51wrEEZ8S3hy86S8+tw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-20180529045407_1527569942555_0.9210552384721435"},"_hasShrinkwrap":false},"0.1.1-api.20180529082227":{"name":"@atomist/sdm","version":"0.1.1-api.20180529082227","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*'","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"edf264b6d2724e6057e9e74ea40b1c3659638c50","_id":"@atomist/sdm@0.1.1-api.20180529082227","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-d2H7VYr302yjwMlqp350K/ZoOon2ZA38lMPw+IsA9XsIlhDMh6m7qj/A8+metINsGLcu9M5BSQg3QXECi3MVVg==","shasum":"2c849a41f191324b5efa3b3387dd097fef525e37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-api.20180529082227.tgz","fileCount":893,"unpackedSize":1929408,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDQ7lCRA9TVsSAnZWagAASYcP/ijcpPPAs8tnUFlziZfv\nGtdese2pPkzoSOW8x1mFLMcJ0P43+kjFa2KixEqEfX58fpoaX2UlkUGYqY/4\nS3S6aBpdsNgSxY76xM/zD53Aokj+JxuA84BBxmm5jKEnjOu645nWBsD32lBV\n/l0ks5aStyhuBv08RZVKcBV/c7fVhrXaKftUklRVT+0ZA8eZ1LbDPT2dFWQ7\n0Dts41YAdZJcsR+nLRb2dLGAs3wJJ0Gy4AlEZYK4CdY2aW/yzQ7KZDia+Nhy\nQpJGiWq79LdAv5ok1LWjrov69QytBFUt3XOdBJH7KY+xbBdErmfxwsXdpSSh\nOToQrvkNwGdKN7+hfTXXw1Z6FfZJgqK5J2sauvpZI0LcBfuaiJhqJx4my2b+\nDdNTswhwvo74lzuaBYEv5523KUoQWs0JrZGz41FWGRycMSIFpgVa9px8SGMy\ngfhrHhSpm9n2JlxHZmOR3j1o08d7+f9melh26OCPH8fQNm5Bl9FUtnbjRvy+\nhSA3eaAtfSG/JaGZstLx5Phe/wvsrbDVqClQWdp8ju3PF/dANXjTWbPqTBHe\njsEyCQxxi5oagtNIdICK/Xaq91GfZg6FYcfhbSMFshXCLFIUtc5rS8ocGpQc\nJy6MI8FHP5FLZiESzqq0AhG+5oCDRO8Pno+eeSWJd0U03LctR9bSNJH19zBl\nTNpT\r\n=HFS4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID4mQSdWpQWPTp/3mexi7FE9rAIjwkD9kMB6HmIn6MqEAiALW9WN0la9pBZwkhWdgTcR8+ftRKc0lTUrqx2QmkHyXQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-api.20180529082227_1527582435471_0.7504341850140741"},"_hasShrinkwrap":false},"0.2.0-api.20180529083549":{"name":"@atomist/sdm","version":"0.2.0-api.20180529083549","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*'","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"54f9794afee6141ec75a00f4c8d329477d9ae5d7","_id":"@atomist/sdm@0.2.0-api.20180529083549","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZuXsnvM9QK+oJrzQV0JOnenSmiL39iqjFGk/R5cwIr2PxrMlLlvTip1Pq83eitXcKdeJQcjtlCWJv19SpXSALA==","shasum":"ad8c0fc91bef7abf910506d27d7eb3e1765c5933","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529083549.tgz","fileCount":893,"unpackedSize":1923371,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDRIKCRA9TVsSAnZWagAAG9gP/2Tzf7reIM+eyI4vGx3F\nHF9ov0zV5A22sFzR4B0gXF2AVXSWpkRr7x3Ie3/gTU6jpVFTQ5oujzvCcqo+\n+4weX2j3CJ0cAAnmPsvH/CgQNCJZ4axJPwP1GihfICVcDb6PX2VfRKKpZGqA\nqsBKOj8GXIoGnBCtspV9LsmilCDV638JJ/oGbZfyBDhN3GjItDpOQNgQ62j8\nlxyycRxoN3cXZUeQ7U6+QQ3JI0dveR2yN54r1nQsD6OSuB9AQYDug3W9d1f+\neM1jxJo1+q33dNGaOTPn32FGqwFPLEYe4atltrHvJhIDHAsdds5wvTz0TIZd\n0LueV/xctSPi94m6L6toeAjN47N7itY86BHKPzHNv4J06nik2188xlg8x0jP\ngHR/NXHEFUoxx0mk+FkiJYVLVj9HOYYqdTRpQBG8r1cWsM0eaFJMqCYx0hXJ\nkMRnQu3+fRPRvEel+YJojNqlgwBKIRPEcgjQNYBHJrHKgbUXYJlr/HkD8CvP\nUCkjRe0L4y+sBiewAlewN5v2pqZ0CM1L+xq5YGFqTw5qtbRoESug14qXOif0\nEpO7DQNPntQjkoD1FSWUkJ86KyosrbGz0LOYuJmEB8K4UneLgJ2sKyXddU+H\n/OquXlQVdJ+V4hBT7yctlPAorD7ob/0nr3Bri93Tk1qcwXdpNOsN+Ayys5Y0\ntmne\r\n=ikhx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGBAkcc/iWnRxDur64GuvNnwNK4mZizfDbBXvgETdxOXAiBKJhUuL7PEx1ISRiZVzQ9XGsOhX8om81j7Czq0e413pA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529083549_1527583240715_0.5249051498486914"},"_hasShrinkwrap":false},"0.2.0-api.20180529084109":{"name":"@atomist/sdm","version":"0.2.0-api.20180529084109","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*'","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f7acc83f8858dc83e4a5b2f455936cf980ddff43","_id":"@atomist/sdm@0.2.0-api.20180529084109","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l4dEtyp7ToQtoKwFjRwMbL4r8iBaDGvUsLYS5uqvg0H+U9ylptDr4fc6E7cXaskY/gkjOJJ6SZMAS9rAABJbIQ==","shasum":"358628603c014c57d3f3895774acd624ca40fee6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529084109.tgz","fileCount":893,"unpackedSize":1923384,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDRNFCRA9TVsSAnZWagAAESsP+QCbGvpoKcGn3hWqfsTa\n0RMev93HGJmzaaANyfXGpJiPOUJwmInWY/J8QzDcz8VPnbrm7YNHwuDiNP0c\n/VipqQsQFMR20mj8KfFkt4Re2FyIOjhPTf/s8MFOKm5s7m9tbd5L4tWDu6Bh\nhbxxiimH5vciRBsGj05ZlXvyefF26EIr2smeSsp1Tvm8NuqEn+7kcU595Gq0\nAg4T8Foof6RNY2ffCUjcxK0oR4KFiRDfPCkJFl4HEw80DmY5bDLj9WMmvz+d\nFuVVnckv/BS9TVpR2EJ/V7oll5xoOGVCaaRQbnB/+VI74XTNwAIH9xzvA60z\n/sd4YbiefYb4eTxDgrbpY/V0B1lCRrsR1CV87yiYk6PfLxATF8IbiOHycJk3\nyRCKF10T3XarQG4j+/PPrvjvynvieA2omQ/h2roZXljINx06++X8VRYSJCGZ\n2UMMPcL2ghtW7CHVghgdWdyafw98ep56ELn3YLLxcgQZIzQayV9LAAcugnti\n+sHGwgtK3DUREY3nxvfTh4cJCp9E5G128F4LxBaVM2gupHkqqimrdbC9NPmX\n0GTmzsrUOr6kCCblmk6/d20cMMoJ7+viPcN/LQ6HnQ2WOudJ7zY3NgliQ6+8\nOLbIcRrUZhAGEcDrJMibzs6ZhOftHyS4KL70dr7rl2ZviOaywTVxPdzAeK/K\nW4Za\r\n=RToF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbSrWbLrRErJtdvvl4lIOAK5RaNYZ9VGCgz39y6ypBywIgXWr468zIwpwWuMoW62xiKvRlgqAgqpdegaaJFMmMCLA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529084109_1527583555786_0.584584448494337"},"_hasShrinkwrap":false},"0.2.0-api.20180529084331":{"name":"@atomist/sdm","version":"0.2.0-api.20180529084331","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*'","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b5fd4922cfee3f6ed0536f88541b60658d748f7f","_id":"@atomist/sdm@0.2.0-api.20180529084331","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wlcyptt+5C3dyPgevS0EOi2vrTXo6ADNyO4QpNLjHlrRxUrZ50ucIE8vi9rlzJqeYxx5SCZZEs5wbvvURYXIlQ==","shasum":"9104dc7f0387892f3d1f24578d5653f91355fa8d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529084331.tgz","fileCount":896,"unpackedSize":1930899,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDRPVCRA9TVsSAnZWagAAsxgP/1vsgcBOLQ+3A5L8nyxT\n1eLvUYhCYBO2AcXPRympTxe/7A97g3lkwxsEVzY6BJbDZuW1PJkEc7uNpG+O\neXPa47siQ2yh06QjakPmiykC4WCReSIV7ZXt0TW6SKIaHRF0wYDifjlXLK12\nrSgx5TqduGsmtOHSo7EusWAeBvfPIUAqa20DuYqcf/z7DuFi/uxkNMdcCw54\nDs+e7912W8ezkIikGPa8RqTBcFD9fJawmwa4eEpHUsTXdxgzPBoK3cBkIwU7\nhHC7WF4z/ZnEoSdNntf4slqgZmVSQJ5slklA5D8OjF6pxoFsMb0cf0zcxQBi\n7hpTB9OKMYjDYWSdmQIMsNhOsAyTWbNdJqz8m5TnGH8hPneRYk0wtn4Ljtbl\nUcXD3Cs3uD3h+2wGWfvmVTxDoMXPOYnvlrTrOzPn3xxLH5a5+46LnIVKElfz\niPTaU3dSQ218PbN5f/nvJfy36JSUBIfVnSTq2o49IrW2BXW3nlx8+jEPKGTR\n2OYvIRB/4nHfsWoiwwzsfiBN9QSA+L+sKw7o7qhSUGc/1Q+ObfrH4zIF9IAV\nOgNJc2xWB52qjHrhBt/Fav+dVIdCr93T1Pn8rAVBbDkPvHtA3bLEHXqUNzvj\nyEM30VE/9lCfD2tGPOekXSuwu+mT4FJ9ihkOzPrjQahr46+Cjgb3WpjcUMtm\n9hxe\r\n=Gcr4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEUuzmKV1gvXWnXzEy/Wt8XC7fwdS3a6UbeeP+gr24VrAiBhfTvQsBeUXE1pq2DymepOR/qvVOjHDXEVNziyYZZmIA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529084331_1527583700282_0.7732390258386432"},"_hasShrinkwrap":false},"0.2.0-api.20180529100906":{"name":"@atomist/sdm","version":"0.2.0-api.20180529100906","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3f68efb5325b514ece11f249908f6937280add92","_id":"@atomist/sdm@0.2.0-api.20180529100906","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cIcJpixXCo6TeauNlDdYf1Vo/Y8Ci5mTT4jvij5K0Hn/PUkEjjPH6CBpS4dC04j5iYzXI8OW9YTrCE4TXt8qdg==","shasum":"2e58bda8ca988ba9b965206675a90943dafa498c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529100906.tgz","fileCount":896,"unpackedSize":1929799,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDSfhCRA9TVsSAnZWagAAMyAP/23VgXVPVar2MMaUpBlU\n/WhFYqTN2xg8O/cUPQmmFYB/bEz2c+Uv+5oZxcluBE4eWdkN9M4UbBOPmACY\n9V5TXzkQ5lR3ttFOREUkJF1koq9DcndZ7J46Q4RU1ORuTmUhdOTfXzmVBtCE\nHURVGcPSqdlpq3uMq8y8HMlvyJmqH1KKDVP8e/46ZLQViiS7pPlG6j0U8q7I\nGDtcewKqUdpYU7ci3h1NI4KLOkX2uxMZUi4EsM6latPGxzlvGxy50SW54tAg\nmJ/tdTXJEPoMRqKc0Pg6v2giTjuA7R35V3zmkyyeWfvCOztkpHjhTgCncySg\nTkeGXftRBbIwnNlD5LQi4Kd8voetfzthn+EhMHypjr/e63Ng1CUPdxDF5RXK\nJZACGWN4fWagRJO56+otR3SfVFpSb7BxtbPs1k/CWVX65mYOr9R54hn/hEca\nn6uBBGkJjhHvGJbtPWXUF83M4uximvlLEZRqb96R10iOLCckuv//lcV3lxnp\nLCOvF05au8CPlVmshB1/G7PjN4sAwDIMTLgE6wEK/01ld2DmXpHtALSt/AZ7\nQ9ZWwPVjfWWF0Vtd78giBPqa3iYvgfmeA5CQlQEiylo0aFxklU9Tz1H3Vn5K\no6SLbTeBJaFe38ON+u9kmEfzRmz7tzgggz7XHf9F5AbwB3t/Btmylvrdo8UQ\nDdE1\r\n=BumJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWvQwjQOQIRFbOBM7+B1dbaEB6c+JNKmFPwjIclJKTDwIgRzygJ4bNI6imG5HTNkZ8Z2EeiDRiksA8+/NdAN1lRAU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529100906_1527588831200_0.7694265164915559"},"_hasShrinkwrap":false},"0.2.0-api.20180529110711":{"name":"@atomist/sdm","version":"0.2.0-api.20180529110711","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"55cddf11cf23029b9a923ef6e183eccbfe529c07","_id":"@atomist/sdm@0.2.0-api.20180529110711","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xsuD09urS782oioB8/VVW1hb1eIfNrDrkScdCtvwQNNGpkrc3ncG3qRmfSNnr8VlmAjxYPmx8tZbZsQRMyNzOw==","shasum":"b420c8dd4552f7a16ac512eba9271667fd32bc55","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529110711.tgz","fileCount":899,"unpackedSize":1929852,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDTWKCRA9TVsSAnZWagAA4OAP/idCA5A7y9p/olW0pXVz\nC1TIi687LBpdjJLfBUhZnMTG5Q6VmbMpu9OV290Hzwrdval0b/M1OBXdB3CI\n+EDfEzZa8KzFXDuho37WqRgP169X7KVFgN7+wjzm/aTGCOVolw1F0TlxCMDJ\nm+MNWawS6hFHksH5nZcgobv9sWdE0g+EKjolGSkEtj1h28LRxEORHBPqPLhd\n39X0fpKhxwk7L30r3JpZxgr3oQEld7mSAQobDjPGzm+KeBKMTn6kdNHPLvHo\nDt2X5fWJO+kUgMHSm1E3CRVcy5dGrYrDuhApqCYBOSXgxvrkDQwUQzqGJNoU\nG3USPIBQ0N+/mD5x2NIZlUC8MhYvgbg5pmp7WXXvy1oDTPMwVmhcdhgH2KoD\nGQ+3C7tANdZpGGnHNh9ibwoTbSch24X87YuXKTIdoXWECg10iWOTb7ZQV2H0\nFkAI2alQNu/0cPjcvdNnGfHyvHibIB+Rt2XV2/5GYihVlZjqJKmydgV46JvC\nZD8Sw6C9wIE5n9bgOUyRSoWUEhwJs85aoscZU8yTW40JIgRpM9NG3+HbJnev\nAqFRUEVRiZ393/1rYXKVZ13h0fR8ABJJUwGs9qqX+TZhA8eRAXMye5rBYROq\nWgnX5SZWJ80GKLH2vjt4uF64fyz8Y2xHzKiakGfMZpWdfJnRLYfa1lR52jdG\nZVV+\r\n=Mx7G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHA645zwX1dXSTgsnu8koyVy2DHtvR6V7Pt7TO+K3HgQIgXf7TDByX0f9FL806ScJRf+WbgFkdOLvpOuN6v7pFQ38="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529110711_1527592328016_0.08663400050229209"},"_hasShrinkwrap":false},"0.2.0-api.20180529111106":{"name":"@atomist/sdm","version":"0.2.0-api.20180529111106","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5833b01ef69fc8a6e2b650f955b14eec06f405da","_id":"@atomist/sdm@0.2.0-api.20180529111106","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4GV2bBr7Ff1FQvriVdRX8UuPB52YT3jrxH+R0ngfj5WlFwPtaPBq7DCAdRobLh4MDj251WS8RQ4MRJSUXgTd0Q==","shasum":"9e11d78f5c5bc075dca5e157611ba4ea5c1f3f2e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529111106.tgz","fileCount":899,"unpackedSize":1929964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDTZqCRA9TVsSAnZWagAAzPUP/1b1XnSypOIFEKd2+Ruv\nMpmtz2Fmg5ZogRYJ1aiO4V30bjyC4DuDUYq+dtV4v0b9J4srxbIVj7VBKAEd\n7pLr+pdWqXTh0ai92mrmedrjYZfNOdUMQ3EGm7dNqIN4N+rLIAgyXWHi45On\nT7JOu0nkvx7PsqrP4135jWuY69ThmrnfZxOpKBvtv5p6YTparcoFPbUcaUkB\nwtLPqS6Up6DQqCOY4SmkPpGfH93ZU8Qar8DYH8NWe4clCvoMFeIr9aSBuh6L\nwue/D/GiVpED/qagBrWlP74+4GONHovqDKakql0aT6oYRO2ZQQjnnTcxThnZ\npUwXlwiPae5Xs4t+TAj3xNZCmLiIwlUNar9+n7lUdbbwpVBOfoHZZWZDnTmp\njngH6MpiL1pAazOMVlLnuOSTFOHQz8vY+j4hz8KfeEPieI8wSyuznu7Y/QYn\n/Jonh9WetPqoVRBDC/M2hJW1P1NUQuowePUyUKn6uzX9e7lHD1Kr0bD3bS5X\n11vCd1nmUnYDhbjbWnUA4qP9+pGvFuBWM2u4J4Y613/hzEYl5/fZ0rRdURbP\n3mUCLFIxPRFTfqBX4kKHcmWw/Jx+RBrfvmYoFVAObT/MFlxDoc9pkNWW4bWJ\nNoyMkZITtiSFDm/j9HJd0Gli8SROTm+mDjqmzGx4sGwmj0JYnjkR6DvQBnfJ\nIVFz\r\n=rOo4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLE63+dovBKRT1W/9PRxoMLDhnTENRDm8sTv/opxxv3QIhAJlVIDYa+6/ey3kzp+HeQ4E4RzvnKIjOS46ckjH5E32e"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529111106_1527592552313_0.17614639391271703"},"_hasShrinkwrap":false},"0.2.0-api.20180529121135":{"name":"@atomist/sdm","version":"0.2.0-api.20180529121135","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7e8e424532f864e6deba6257d6c486f7e6e3d952","_id":"@atomist/sdm@0.2.0-api.20180529121135","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/tF290FQuUsC1XziQMGGR1cIMkMrzppX7oSaTCIEWk3UmGDWR2bPVV0cNQIHdPxAd8zxvrAUXAy2uCe4mkUIlQ==","shasum":"314316a95823ea4a18e3d128670bf32df75cfbe4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529121135.tgz","fileCount":899,"unpackedSize":1922802,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDUSvCRA9TVsSAnZWagAA/y0QAJKcgvesd0SYdg6mclON\nKNeBmKufLciOolJn0ySm+OUBA9vsak1Iv9HewQzVahe2u35dBbCuzGPfDq5K\njGAouLv84A4oD/H1l3vewAcaXp7iBGtZWL2lveXhAfVsU0rcaDqppfkB3407\nGbmHUeNA2cVMnjZqlG2S6tQz3yZ0iQYf06OGUS9yT0C8m4IPpuSMx69ntud5\nnkrwhtLAOyERfX8IMZPC3Ir+vvGntNJYbVdp8juJIHebFfEaZ+jiZV3wRVe+\nKKpSliHBpZNE75VRIsy4hcjOm21Cws0ZlLCa6gyJQAD26PPBp4a/40y+ZTq1\n9C2p/kVjLQ7iDeR2PdQYcwbdRU4CYMGgFz8a6nIznROLav8ipPsqE8qpi/Vl\nj3JDNmqm6qX4kwaeqSmlnjQYFlREZ6WUrYu0tdivRg181Cx+lbFtBby+i/fd\nvVHe30GSyl19CgSF76a8vrecJm2N36ee6voZGWqiNszNuZ5/jVyp6n5o0r5a\nodi8tolFTuYAYoxQk8ZjCdw77IFFrtNiODktzZ44zRAQqC1saSbnCzUxU0ix\nWAKvQHdKrErOMikwfKZTEwPk8ix14SKJrtfplaXYvk6td5s+Y9/mP/WC3sJd\n5GoQn83b2y+1nVYafgNfeymeY9pMzIwqR8439rwPVfAE3oK6AkXOxsJol+wb\nVm3W\r\n=AEDN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGZqbDTvWbotGLOxdPLEwXOIzlziH31FKhsaRlxQvCp4AiEAsJAaZrYN3OaWFCCKTV6gDkkSrYuJW7bVUNysUVhfKRA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529121135_1527596205823_0.20880770194775855"},"_hasShrinkwrap":false},"0.2.0-api.20180529122027":{"name":"@atomist/sdm","version":"0.2.0-api.20180529122027","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7ea8c462f64848f2f68c3fb7faf51c4ad5b85e5b","_id":"@atomist/sdm@0.2.0-api.20180529122027","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l9VRlfD5GWLX1IejyfpoGEOgJzLqsPUAq3VYQWN62UdU963pncYvTYiXwrLPtUQ3+GQyyrk9i8MQjWWIllb9uw==","shasum":"d46ab83db6d6ef0dc7247a43680741a8ed2417b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.0-api.20180529122027.tgz","fileCount":902,"unpackedSize":1931771,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDUbECRA9TVsSAnZWagAAXw4P/1krSe4cJQEksCSeVYAe\nnp4pA2jhEpvo1e4iAAOVm1vrAXex5xzrtc4ZoTmZ0TILKP5UNhm9/iPMW5bB\nIuvIJEVoubj+//ByVFYhSJev9nQ4Y5P/DMgtlQJGyMlSeAfOTbk0xx0wIrGK\nGkecdYBZxfW0G0jBsRjRTa1E2EaWtl76VOR3SETxdkeWnLy2as75lMAsAPal\na7oUgYDmg3tGzYbsAAFjctvcXeOVTT+KPuRf3UOKrXOBi8Ihpgih7wgdgG/F\nF4Skhm8xEeKOfBar9qfiYBLg5P89dtxB9gClD77k0m84FiV3XGunOY68ms8g\nfRt68q6Vmn5QpyTXqP9GNqUKzViREg9WTRO7xRAbjSbi8laRYz7cDitDkzsN\n5mPREFHbzrGyjqq1oabFRCibJft74IgUAOyt6ZvCE5GN+lJLn7wf+wImvxXE\n1S8qo9beAAxbbbGxiW73EIDfv2+23cJlPuSu6etSMb9CbhPHllQllu6u1suq\nxVzgX+xVVwcJjUDC7tsWablz3G3K5cv6p2DYNmzY2sDkCRrB8XtPI6jR4cOS\n2KfhjVQQhdXYTE9GDPMxbvkTrtB/A4S8F+gG0UzkMXdcnm4W39yVYnWOEPdC\neKIp1lrF8XOpHrFMXfQ+Fpkj2UgG/b9RBpwURkpUZYfzKR4DI7ES32Bd6Gc2\nh7l9\r\n=BsBA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDu+FO/iBtw2IqTfLVjiUgWjmY0upNtI3GcigVxoc0vWgIhAIl9QLRtIFcoB/qaTLrL2slihmsxO4bXRf5p42QHGBOn"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.0-api.20180529122027_1527596731986_0.22895791367749418"},"_hasShrinkwrap":false},"0.2.1-api.20180529122644":{"name":"@atomist/sdm","version":"0.2.1-api.20180529122644","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a9e462010c50f2d987f978ba1a3478905d741be6","_id":"@atomist/sdm@0.2.1-api.20180529122644","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aWheVoKsTqas3Y6Udj/iE6R9bckOeVXa0176Z4XYWuIBk8K9HpKNTIiEe+xvPBiv7PUxarFxtfzvGimuOJTc1g==","shasum":"09062f66717bbb613df3fc33fd06748944ca9675","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-api.20180529122644.tgz","fileCount":902,"unpackedSize":1931771,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDUg0CRA9TVsSAnZWagAAaD4P/1hLjzSj2tF/Cb0moVkk\nXy6Q/6hP1IixmhXZYH7xWlOYkTTL+9GC55CnFxApPwL2plVyUBDiP/4sfH0m\na8dlXghgT9a+KSz9TtPGhSvXuS5Vxdrpx0o1X7WfM79EXp30tLzqS6gTDC7K\nhfiLoxMZC50VO/7xgwB2n1du7y0NYQnJGtiagIqfj+lCCoqIMZYR8A2iJ5Ju\nVT5FJGDi0vMBPyIc/r9KQJIlZ5W0MK41/jViOuiQfCZr+CEjQ3/aBDodHa8M\na+qZVzToVwG7YKcOvsiz/go9GNiUHqy+LQv+MEs3HwTIEVE3toDCcBxZIL6h\nKL3JMTmQyFcOwVGUj2oustUeYQPdUPgkBM5WYLgHdzJUGdh3VPYBKTUkqVkq\nqFEV67bS6xTAzbbvivvn/Vxbt++mpevXfI207l5QoyCiPKc7u4vRLrF2+/Hf\naLFB4+nqeXWw2NzHd5uUYtaWVu+UtroilJQrTFr8j+kaIiWwd+fS6ZsYwEg1\niRkO1vsew+3imdzZsAUBVwPDcSBhUk6RhLWqJOC7i43ZlAbCpgJnl4axWchI\naSjWKjY9QdtDdzyrP0TE5qL2XSVMNIh6r0r1yP8xVgmmi1YGirF/FxcoThWR\nYHS6HQd+tM6gyS6WTGjTe08YVrp54IIxuGGetaA8ZfBAgyXVWSZq34lya9bP\neCqb\r\n=VER3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQVLTc+QzrkTRoAcYzSG+t+McQPKJbxzt1ngOvdVnBngIgIhPgsjDOjow3Pnj6cDlXoCIPFQF3stQTfyD27zUIAtA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-api.20180529122644_1527597106977_0.6764225278210949"},"_hasShrinkwrap":false},"0.2.1-postWithCurl.20180529152121":{"name":"@atomist/sdm","version":"0.2.1-postWithCurl.20180529152121","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cdd655db09b7b905b9a3444d3d2f2a5d266ff253","_id":"@atomist/sdm@0.2.1-postWithCurl.20180529152121","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-15rCIxgbcNkHG5a/z91UdFSntKzK8x+HVgY1hzyYbUH99W/qXRKLeH6wESHOFbVl/MHVJLZKRqWzdzsnXjhoAg==","shasum":"4f1b4903a759985b14a8fa67138bf15cdfea0b21","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-postWithCurl.20180529152121.tgz","fileCount":902,"unpackedSize":1932459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDXEOCRA9TVsSAnZWagAAjH8QAJFekIcOqs3J/PDsUTN9\nw581RT49pyWULNfjWh4Adn4VDRQpPpvTYrjN94CDUZYvW6SGeQpzpPm4D3uE\n0opfy6pXC5yyju2fYzZCiNcZHqSWqa0K9pXCQeggo1NqscpQKNh4gdweeFL9\nJ3+iqeKp0aO9g4Svx7/FGw1pC+6didSGXvWfCKDBZtfHDGqglPRiKBnn8enQ\ngtMkp7lghUc0Wfv7Re1CKp0sZ1O+qlfI8vEkT18IbP5v1ZRFX42riPu1wMfi\nbJi949YemebZ1jNBoqxDGod6asTk7jb/l+CkSbZRftmHPvO5xkOTCcYhsUc1\nQCKXuFtHZpv/OYR8fI5Yv8nARA81rj5J/3Gn8iI4iC4EBRA6hCj7Kc3yhesM\n//tLAz082rM1mmxiBaA2JUI4xWNJULxzHgdwWkm3Y2WlcOvzQc0iGjVtuAA3\np8UbuFlnJ/BjGUtQWVhgKNyAjt+hT7hyEL339kd02Zx099nwh9CEnza9M0e/\nT/ILY1b2QViUH69fuj1F03zoJG0dDsoePu+7FujvshEYF5v53PBNiyJukLe4\ngxEWIiFE7N0gbvTXYGAGwUgyXDf9XTHo+8PHz40Esz/BqX8TD0SFGZV39CPv\nId22gkz/wFkFxj/6Tf2i1N5vcqTT7j2Td3zzUBtegCOtGibdqP4jr0oxTrzt\nIMfw\r\n=+VwF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDAwDlWr+1OXmBEMHLulcPz4J/bg/Y1013GyMbdvVJrkwIhALYPVAS8CBGMX5xigREYo5Zw85+ZeyB3JgHnlffa2mwg"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-postWithCurl.20180529152121_1527607563811_0.3520841886865169"},"_hasShrinkwrap":false},"0.1.1-postWithCurl.20180529153923":{"name":"@atomist/sdm","version":"0.1.1-postWithCurl.20180529153923","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i '.*/listener/.*' --i 'blueprint/SoftwareDel.*' --i 'blueprint/machineFactory.ts' --i 'blueprint/ExtensionPack.ts' --i 'blueprint/FunctionalU.*' --i 'blueprint/wellKnownGoals.ts' --i 'goalDsl' --i 'spi/.*' --i 'common/command' --i 'common/artifact' --i 'common/log' --i 'common/delivery' --i 'ProjectLoader' --i OnDryRunBuildComplete --i Config --i 'regexpReviewer' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3564a4ee230f6ac1cea4d6ed3d22fc354964c0ed","_id":"@atomist/sdm@0.1.1-postWithCurl.20180529153923","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1ki+cV1a7bDetYQnnqznseJsf4x9jxRYA9O9ZtuJzQlwOioaiHPZm/MxNXIqO5DQcwr9rmSQmJ+GI0P1MK4XAA==","shasum":"3a9debab56e391404d07f150018d5f799eb7c4aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.1.1-postWithCurl.20180529153923.tgz","fileCount":884,"unpackedSize":1942025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDXVzCRA9TVsSAnZWagAA/L0P/jIfId2fAkmMoGu6LiHk\nwTK7EcU+ses/ij1NeXIIp+UrBkEq0iTJge0AK/tZ5cs2S6t9BFJLLxlcGN4r\nLOlB76vDK002PmYP1l4d7hCkl3SK04wagx2N95/tx3REaFq0aIEhQP6j/jk2\nxA7PH7YeocU7N1Iyd2pOVHtAMVza6u0GpY+yB4ZuZ91VhXgZe34mew8NdcXC\nO5+nG11hMQC1xLebUcMHFlxn5hJZjGrQjBEe7Yo1w3baxR3APathmnyglODP\nl4XPS6FO1Itz/nOC1qM2ki6bH0DulaMiHysX+/xjS7G1Xb11sDfNkyVmFkNj\nJkDRxZcdh4NmEPsPjDhpNAUplaOuXaeI9IUf9f3QOPhJ6o1u1nZtGJXUw1/4\nYoN2+YLCQLaub6GkRk3prcKTFnM/3DEVi7rRkwzOpstMUecdWkrlb4csBHOX\nmw3cpoMWJYxvNX2NAUd4g8injVoDIi24JyTAuFF7IsaUzfZZYMKHuprgdNQf\nd6Ffm4rHaTNic0domFq3rB0KEIxdxG2xViPHE1NT0nPs49A0kjGKlYjCje31\ne/s4Wy1rDa9uMHMs4zSsQQpeKs423l09gOaKTt0mAtnNr0s57GCYDDjI1W7L\nv+mJiLwzGpCv1ykGr5E2l/Witajpyyz0XRLgMFVhav+ehb0XqnI0H0cGOh5L\ntAuK\r\n=Qz3x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCW1t4FQAeAV/u8ETr+/mjgSHPpiCBhsIR1RuiUqkH7lwIgY1uxDZOuBTdwPyfu2N82MDgXthScM/mjJtF/Aqlz1k4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.1.1-postWithCurl.20180529153923_1527608689400_0.08201846598109785"},"_hasShrinkwrap":false},"0.2.1-api.20180529205953":{"name":"@atomist/sdm","version":"0.2.1-api.20180529205953","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"304b871b52cd7ebf6e3ce88f71b119cd58654a73","_id":"@atomist/sdm@0.2.1-api.20180529205953","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BPzBPiQ4Q4GpO6a6bGgozHBHFnX0ZnjgmwYlYRg9CoVrOGpATsLqsNOvxrLDwy07pgKOGZ6mCOrF4C/zjmf32Q==","shasum":"a594fa9813d41bb7c61f9a0e3ffabdeff9f86b1d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-api.20180529205953.tgz","fileCount":902,"unpackedSize":1931870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDcB8CRA9TVsSAnZWagAAv30P/RVFAa+/AFj0Xn3UIFP5\nx95ssRSwJpW5B29JVkqp4fQeZDKqSJNLinIt+rOFCP3LdUMvrG/4k08cLbRZ\nN+6/coPyu/Pt/HyRD+wvCgrVKPB/8ebPMLH9WT7JNicobNhvq3otVUHW5yBB\nVqpGznO8W+nVmPmtrBwRwKP5EO3kyQgHtOeDuhNvHgM3XTLS/NNKmAO7BKGu\nfwpAJ90HKXedmIpfPlMyrn3aciVepOc4fuT+cdF0w9kyK0bhhBoMfnPCb/cw\n3KSjXqyzKXRyHYD+RjaIabdjVevKDhnbmhgYqq9F5InUIj4FoyJG8p6th/SB\n/gJDQlvmU3Pm7Xjh3B0oOXLOfxQVUvbb9LI75rPk8ImoMR+X/etkOhNcTb/s\nYOog4CQsbSWmaIaRb4s4ZG+QEgXb83AnTnBIFktiGXuI9vP/cKxeoykPV6ne\nkPsXeXLM+VIRlNCcVCRopy1Lms1E8nHEQxc5qNCEqK1kmecfdz5ofk1YDn98\nXirA3Ldik780H7oOosuhjqiLSUw1GgdqhFDvJB9X2+JUz7soDiYwrqM+8MIQ\nqGK45sXD1g3Ht0EeU5/uRVO0caKxei4cqXf64RHQ2KY5vjiyP1qoBqdA7sCG\nLR9j9xp8E10DQ79f/pT21m9E647p8zBztW4L4QL6iFUUxw8lDxdJsLhDnHXh\n4ATk\r\n=3QgN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEZ0zr/FGoVsgVQl7wS1OwcMIsxXsO6gti98pwaxvl3uAiAdYW6Q/X5t8ge3aCDy+cv3NCwtJ3IRnv7Kol/UWPRr/A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-api.20180529205953_1527627898875_0.9759587423214688"},"_hasShrinkwrap":false},"0.2.1-api.20180529220151":{"name":"@atomist/sdm","version":"0.2.1-api.20180529220151","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f6644d2a1c1c27151c464750f3018399ae904591","_id":"@atomist/sdm@0.2.1-api.20180529220151","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gkIxwLhylyZOt1sRTnBaJ5JkHKKa+npjnnMQTGnaTTo8UJgKj22u3hdPN7DJHgippa/1Wpw25tivia2pkeR+Lw==","shasum":"033b0aa752b5d42b7e204cdafe0eb586098e2e8f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-api.20180529220151.tgz","fileCount":902,"unpackedSize":1931972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDdNJCRA9TVsSAnZWagAAv8gP/0D0VGyhEeshaMTsKsfS\ngsCRMClHaRzH8OrzvyTU3VXA8elpXQ14kn29O7DqpSPmWdySJ5688QqtTEKf\nFnNF46DGEnb/VNjxYFNTVqCpKDoste5X9Z65VTxiGWF+UxObFr/yFY67fpc1\nFPCeFfebGy9axwS+n6Rr/tqSA1jrK5ZwfwyqbbEgxXsC2622e7HiTET9IGi2\n8HJsynJtA0hi66xrhqOjvigz6k+y26RkFXyomN+lYioW+GChKrJWEd/1Culb\n1dVSSEcA1JUqG9QRrvxmFbT4nL4WZRph9sKkODTD8zsdBpBII95EMvuaP3Dr\n18q5/+jgt0hgAK3d41y3P8+56vnA3b0wf2t1XiJxn+Q3zMMFTvylXBLY8BBE\n5+Wuy1fI0yaVfkZt83+CF6h5FJ3daZ5zZRsMchsFwwMeRWHhK6FywkE0JD5c\nwn66/BGVpDZsjcei3HtDAxgTSU6lwa3rqG9WeLrWjpHX5GUKSsmaCOqVh5aZ\nlDupX1KC/TR95t6U8FT1fXuhcpJbeuk/1cMvRCvLIHRKyF6WbjbRC0CZb5IU\nKQWs6AECcHSYbs7vGoI0Tqq5Po+9ChBwlNR6YnmvKMr2cAJATVeMp3pJliCG\nwaSW2NRMmoxFuciGqhzputxq9x9+DZp7yO1jMmm5mMPG3a8RwdZ0GcIqvPnm\nPll5\r\n=etJc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTLTsSBodQoKOS2FgmRVX3ubGHb4I75InJnJWQI19TXgIgYC+YZ8PQa7k3zybLO3ZArAURBJWGtZAodXsOZc12UfQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-api.20180529220151_1527632711066_0.2896218004399831"},"_hasShrinkwrap":false},"0.2.1-20180529223510":{"name":"@atomist/sdm","version":"0.2.1-20180529223510","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b4259c1e632c8b3a439928a1bacf9c8f6e90ba0e","_id":"@atomist/sdm@0.2.1-20180529223510","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sY+5Gy+2yGIB/5FzPVpYNJaAfD9ZOFfDVoLkaU6xBLFa6i0Yv/AxHEmxLEq+9IOfhSktZf6qsBJIb1RmUMWnYQ==","shasum":"21130da2b80b735456ca7c6a2280ba1613b572e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180529223510.tgz","fileCount":902,"unpackedSize":1931968,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDdbECRA9TVsSAnZWagAAMBwP/ipWQCoYXu7vE3iNIQjJ\naCmNH8bjYCYwWdyM9LOwrkmTnNkCP/hBjHlkcGckUPO1kBiJn7ou+xgyhaqM\n/56vUIR5YF/crPlqTm0RxhHIWIk/4UvNEfIawaAlcvPZTfVXCNyW6eTEPxV1\nJfLr29A2EpMA/p/kKgXeXlwZ/B88iFF+gZfuw76spN5OPg4/GijYD02tNvSj\nbW0z1LSjQzKbuRt0HGT+KrYM6jn4n3xaI2+IVBDoQk0N9b2zfuXDJuXs7a6a\nV1iLTgpsv9HkXazgONJNZfi53E123AGfpYJIVkTe/4hYp8IxPqXd675OGNTQ\nALR1nHnLpitEgzZJwFqRpFBjBgiwToRfS9Ucv259OYV6GEeyBqalKw6Z6i0E\ntVG0Q69APG4LQIZ7BkJRPiOTsfIxUDpPoqxRVhASabc2aSNHQokb/qKxa0bd\ny6oQDmJSefUQ0b7gukYZ/DSeFpQ/HguckuJRgSbO+vfnJwUYCAHosUW+ctnk\nqCT8f3x9/LoFC3JZg2b2LY09c/sv5gFlcZTv954zeqhIH//wGRRVMMYc7Fzo\nKei9gu+5vXEItbU/nCJAcDROBPuXyoIpwhkngJ7K7YI+thPORuqCoNdmnC5i\n6JpacHvgx91SsdSOTRBIWM3CIo11++fi3ChiC54a7VzKM0XsLxVthDer6LDJ\nOA3m\r\n=nBot\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAJsJM2HeAmI6bJpoP3DT84g/UkN3nhaV2KQ5q69WMuGAiEAnSvUgQFzPswXuFqQSUn8TsKgXWwHDbIJVEPRtuJxgTc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180529223510_1527633603004_0.6574302571697042"},"_hasShrinkwrap":false},"0.2.1-polish.20180530002614":{"name":"@atomist/sdm","version":"0.2.1-polish.20180530002614","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a3700855150344801c8a9b02a2a7ca4efe46ee6f","_id":"@atomist/sdm@0.2.1-polish.20180530002614","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wJwFyTbBIHxthYkh7GYwCrR7m0Kk5vNf1O+LR9vVIWo3wQJrER1VTlAlHBvkXYgTGIILqAV+WqlAOUt1imS1JA==","shasum":"173256e32febb9d275b98f8bf89f72bf70885c3e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-polish.20180530002614.tgz","fileCount":914,"unpackedSize":1946100,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDfDSCRA9TVsSAnZWagAAyn0P/jbfiIcDIi7/n1iRr32a\nhVw8fY73Mr7GnKKTge2VMYt3YzDKZLRVcXi6r80Xd7JKuJgguy9pKTstX2Z9\noPAxE12dEjwQZEv2/l4DDhN5ThEzLV5ljB/3NUIIMjOrlEvnROuYu9jWr4t/\n2g6YKcNaczgOqgUw0TFGILfZU/6Z5bdlDYYFb1CRYZJCOOe5PXXjxBi36Spl\nNWOl90E/AU5cOkoJ+T9AjH8mbSkYiN7d+SLuq2uBGA50fSEUH6qfKvZLwV8W\nL2Ut7qF7BqaCIKdzY7KZgXnOVt1rGJXQcb4tj/8+bZC2ymZcAL3WLnhgcsTh\nbgxCRZCn7RsgtkMN1rLSOOmPj6UZTgV9PupKFXHbRJQgqwF4WUTWYTEOo7XS\nbbviPdRxCmG5sPrCankrnb1lC8OipVVKLc/7oQcZW1hi4hBXwUVOFELZBEQd\nksCRuom5mFEaMdI7Kxj6t3Sm7iv/dPFjq/utfv+7N209MlXPb+fP4WdAXcsv\nYbVGOddnD6wtf4+n81v1YqkpzomD58zns+iRL6tn4KnzqAEcw9POipr3tmqp\nqgqKnMStF3EIW+SW/irHmmgUqiXwslwFocv+E8dGF/Dzjiaav6vnInr0pKOc\nCnbOgh6qf0l0HR2nhf14U5UL9mere5nso+vhhLw1CYTIxdFRqgL129l2XWnE\nnRPr\r\n=pqXT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFbj6IGBus4wPx2H8/RGL8wAct4rHaOt0u67/PSo7jfAAiEA5d8ijhN2XHr60QFPd/j75s04+CLOx2TGE2Y9SJ9SNbc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-polish.20180530002614_1527640271608_0.3840997696620967"},"_hasShrinkwrap":false},"0.2.1-polish.20180530004736":{"name":"@atomist/sdm","version":"0.2.1-polish.20180530004736","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c3bf8d5d9b387395d0ce35d40411362ebada9f43","_id":"@atomist/sdm@0.2.1-polish.20180530004736","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-N6jWyrnLg9pi+0t55FrhbZIPV6+6IMVFbbCAvAn+c1rK0eCupcNAyIHpQyJw7oQqz4Tm9l4qNQrWVoWhNd3nsA==","shasum":"aee8ceee403d97f48d6f42872a03bd00b18fe9e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-polish.20180530004736.tgz","fileCount":914,"unpackedSize":1945561,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDfW5CRA9TVsSAnZWagAAJvcQAJBUujJn0QyhJ+q/mY+4\nb8u6eQykuwOVLI+whx1r9SJ6nziofw+NqhZANoxfmiFDhE3eO9ZL4yrwgVxa\nYMBBmO2klb8SqyWwtv1rKmWBE6RCVJ6oMfxD6Xp4gOnE1RwY6V+sHV8Tt05X\nJLCbtUrz7rOqDhA3PSWQyKfVHJjYHtwEOLlKlym/7Nqt/lts/OmKxgrStgW4\nfRJYceSddRdTzyqcSLhWCZ5RZzZ6pAIcDX8NRF/xdyYY8+fOrJMPEAndco8M\nckECVyKOOX+b7NupdTQEDyTeT35kYbAq+qh8AFy+IM0TXLCCKm7wmsUwmOOx\nDqLiSX2XAgWET46FI03joo5Mw/kak8hsF4bp3cMYRzw7Qtw5kXEamblbDadK\nrTaCRSU0u70eOZ+lY1j1bnTtBPMarD6ESUNzOcLyFcy6NSe1aRos4tjujaHR\nSY/0dL5LQY7ihBBgYThJlYjZPM27Lc0zM1O7dEFpfDJmXHjhDu0z6V0eU1cE\nT5PXr5NCtlnHVxowwLGd9BwSQGXH6JpkYwPhGY3+RnuvnJVWv3dvzlwjaJ+X\nckiYayvFCANBvg2I0gOf1rcJvVwdkTe4+OK7Fo+kfEOffQrIRXfTQWpQ8mPq\nUPX4K13YNOCH4qvP+PoZx+8Q3720A9rGo4tyAcdrX06SAWCax2ydvEer1bDp\ntn70\r\n=0AfW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCnmL5YF2GPiAEE38ahD+H3R5cKHz7CJxFmS/yR0MijtQIgblWyh6fZ/OBVjNZSxjUI96y9xbH+EGb240eDoNfi8Zk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-polish.20180530004736_1527641527740_0.8177508329049212"},"_hasShrinkwrap":false},"0.2.1-polish.20180530034226":{"name":"@atomist/sdm","version":"0.2.1-polish.20180530034226","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5ad7b371fbb0cb33839cd6e834784f55aa5a6414","_id":"@atomist/sdm@0.2.1-polish.20180530034226","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8esitEADAoOBax0YGroSIBxlUbFLdteU8/UwKOMi9AOE1oOgOQcvts55+crXSyut8HLc9UpN9X//kKxShuo/bA==","shasum":"d2fc95e882e09c25e3bfaab246657d2d65efd7c5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-polish.20180530034226.tgz","fileCount":917,"unpackedSize":1946307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDh68CRA9TVsSAnZWagAAmfsP/R5Mz+CK9VA02I5WKW91\n/Agaxwy7+lThWDx3kEFvkwUybwtZmbFkpSwEo8RNvxNPDiVbsuiZHMwx/NY0\nHpMPHsjCGoTKGDcvuvDLXbJSZwssOm6DzT0gt8cdT1kPg/ROQOea0qxN+cF1\npdpIUjTP4pKHWUgvBUJ+nRte42uVt59tzb0tX0aYus19WWwM1j8teoQRdths\nXRkMXUTapUZlSgko2yLv1Gvq7M50tHLHJzXoeuaEQo8PsCDZ6Z9C7LZPNAN1\nQTfYh98oh6D9XIJtmLCbaQyTE3a9qfO1mf+imGyWsAd5y4M5AZTF+LJRbIZG\nGQVHZLxeIetspHLXx9EflBSkMhwcZLtbLFP+bdH3cZuo4l988qvxFU0aNjG6\nd1+hsNc8b4qUy1JFRSlysyi7tjc9Y7QTR2t6DbS7Gk0VKxzhvYUc87+OGuDW\nZpHJ5GS6A/7klJgciVJCI3V1p/tYBPCaAToED8f9q0twwDIN3mt/HGXNdbCm\njaGeCyi6lGE+L9nA2NLf6C81/Nn6lqx/meE1xKw6B6aPCJ3FN1AJIGXdF6kK\nLcX6ane/UGfXWT6rr9xU7f9JPHkRIBjIZpykIyrSmEoyiSE9CobCyL3xJFZ8\nIva6BCi3mNsg8NmaZzIK1TvrvdnR8OTh41pqTv4o8N6xbunOkstC+ydijrw9\nyw1X\r\n=F1FQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAfoDir1WrL9BbSSrpoPRHaERpKkOASOpYNqiDktrhDLAiEAm2J4buIALyEqSx3ADetUR7WU6TSr1/PhjB45r7/X/bk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-polish.20180530034226_1527652025901_0.7888681037316665"},"_hasShrinkwrap":false},"0.2.1-polish.20180530050908":{"name":"@atomist/sdm","version":"0.2.1-polish.20180530050908","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1ce0a4889426b0484ba6c3c92d037b8c28fa6126","_id":"@atomist/sdm@0.2.1-polish.20180530050908","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C3svKAxxlSwUCSkPJtnK/qsI/lqOxhe829OdbcSYUdXrrJ/q6y3ROpq2zQeiWBeplhrMDM/cTEqFYKiFLTUVoQ==","shasum":"55440f1a8e2ee8b26c649ab244e87e47c0657957","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-polish.20180530050908.tgz","fileCount":914,"unpackedSize":1945561,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDjMoCRA9TVsSAnZWagAAq38P/RjOrmcDYKFm4MEjiV08\n/7K/2mJIMxdA/o4id+IdRzLCpVR1n7kPhq854L9mXwikXPT4L3pjA0WnKrmS\nYkuVSS63pNEZPqbGq6pTTjrvv61YMsJb3EZ07ZL+T4Trx2XyCya1UPmMHOxC\nXCUB/T9AE6VWaboihwAuOSAvvol/TqKwiBmwNj2cBlI+MrJYkrAOuiAKgTXD\nsPOZgyvaEgDzR26wdxdmX2tfGGNJiCmFrku5UjcHHFbnp8E6iX3UMwcMtWRR\nd60YLLNegN5BXS5cQreNFwc0bwzRRmLK/ZsrUHPd84jEIvYV6fbxc0adVV34\ncTJkAzlDpzlq/bFyR+JocTZgYhbP2AaVhZn86qfPwodOC40F9bVycLZBAPz7\nD1UIFX2X7Bs1Bs8EPZh36jtsPjk71qtu5B51imOfmJRiXfvjUVxUdu2Ku7CI\nsaHCR8GEwRS7fFBvF4JklJjwkjyqwtcqRixW4vX6CrMqQGMvrwa9oIp5QDfr\n4Ekb2af72nXLbmfzj90phYthE8h/9cEnteWQKOe5tehx0F1FzBfZ+diLXK3P\nWmUyQmeRIyBoJccXG953hFlUveOz9Yq8rENffgJbr/Dci+FiAi0pZmsRJteH\nMvhj8iN522hFMIYUBEcHv6scdjJJk42Tsr0tV09PALQ4/AQiJJOmbhNQR7Qe\nRY4q\r\n=ZqVa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBn9PvFcfDW7HKZTDmAeDXP7zOjULNmByn9veDjQVNIpAiAs4swrhtDinYMq05LpjD/K9b4Ia0iRlI59r7RgH7znmA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-polish.20180530050908_1527657253815_0.11310965109305093"},"_hasShrinkwrap":false},"0.2.1-20180530051024":{"name":"@atomist/sdm","version":"0.2.1-20180530051024","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2457bcafb925167fc129cf90077b9b96569da196","_id":"@atomist/sdm@0.2.1-20180530051024","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JN8wuudumBRn7DscXyG3aj/sVO1uQhSJlO3pJcm/0wdBaacwV6861khUZ1JngkcMeWPKSfcJWzxdauqck/DBZQ==","shasum":"2bad7ca1fe70cd4c5b160a82698aa4e161825fb1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530051024.tgz","fileCount":914,"unpackedSize":1945554,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDjNuCRA9TVsSAnZWagAAjhkP/3J+lMQC33sHywYsa1+7\nokBjkKEj37t/kXuhlzep8rV1yoLYwBWWff5STD1/qq6e7Gw3ktOlKxRxjFKM\nW3jFUXPhoPWlTn8SqbO9MJ9PI/NR1lw4DFFQqpKiveuunQy2yK+n9z5DWCqt\nYPAfHleNQR1e+4SOa5GQw6Z4Z02wx9rP8bMjv6OTaJRtlea4iUD186Byuedr\ntFfJEqSoY2WZ347TwYUI0lDaATL7K6XCRvPKma4hzaDoBJQhzMiwifEuMbjp\n6nezrntu7F0uG/N2s5hvQBVCZ1q4L6m3FInj6gYiuILYsjbUU+m9mMNvs2S9\n/Y6gaQdHahIzYmz6n/DvmrVG6LCMCdYu6ubvcSxJLUroftJ7h7gLxm2fUiwL\nhwTsd0ZYRBWuQgCF6KsxpqKPjXs7s4HFU4l43PG/xalI0qkNFSdk8ZcToc7h\nNhq2rdW6+Z/utZSVlsUhDekxbSSC3g7fqfW0usNz7PDrXzRXCvQAJHrafcTb\n35E6BQsXVJceyVJa5RPcOuOHMiomnVQUb/yB3ttrb+s77rVOtrB7kL844M39\nVjiifM70NxPba3bJ2ETobKc2kg0gb9LO7da0xal6oE7fmnh9NbczWMa7mmgL\nNDlS3xXvUP4m1q40V6sPtt1/NOQjG4Ej76m4gfKs4ldZVMyl4QL2eFtXCcsl\nrZlo\r\n=2DXl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDM05NMOMqU9dqPmmh854pojbWOM26usaoOBSUo9JN1KAIgfarbzumzZyng47+QOOg7mpdOGrKEmSjRGhVdPV/SIZk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530051024_1527657323837_0.8052656421104036"},"_hasShrinkwrap":false},"0.2.1-20180530051716":{"name":"@atomist/sdm","version":"0.2.1-20180530051716","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"756fb609b25550012e3e1346d0b7769967e71465","_id":"@atomist/sdm@0.2.1-20180530051716","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-N70k8TI0Qhadf3rS8wm7k2tmjTVuJRb5zFUO4nuBpSbKZVf13Yk2dIpU6WE8712QRweXTE957l4C+GScacH5LA==","shasum":"18860c05232faa44fb4d9c1c480e7acef5e182a4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530051716.tgz","fileCount":914,"unpackedSize":1945607,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDjUTCRA9TVsSAnZWagAA4aQP/jgoMDXnEB2ijRes0HV1\nZWVPbhidUHXJnYuJhcQNnEdM3Iqytx8bRJzI4kGiZEVy629418F6kIrDQjNH\njw8XHVKwAbv3Eo+9zvjL5GSgEDf3z2sZivENXrw4899uKk4qqehkIldW4/63\nFjlrFdM7HlqROqrL/xyMW8PIrF7QqyT2Nna9dRAsH19pnEWi8Qx9pRenBQzB\nIVYAX2weWip4mxu9gTRBALPZJFwPAXzaFF5XA4RL8/M7TjmtQKnNB5G/mdR9\noOMR22NwX0Vaw+3IdsucsPo9QnkkEZ6IprLcCt0RzO7KE0mXWtkVZDxNlpSe\n6MKe83uXMKLodp3+37tORxkqu23EyyytOGUExSphvrB36lOvDe9Daa3NdMrQ\nsUk5lLx0q75bXE45ALNkO0FcCXw5TsyhM2sIuyczNVjDSbjjz5FaTrIGH/4i\ntOmvY/NeS+6sM9k1/KeXyOa2bmB3BWgvoSGWmrTIprwIXTBFENGDSd62Bmh2\nYD2Mw2TsAMVxNBjwyqqiAEpBruhsPbebImxK/qMR6sZItigOylDTeudTErll\nxG9PrVv7upZT+1Jhi47ryp6/y/CdOHbLs8BFLzeU2GZHM49msfU7StnQnEOf\np/pfAnhKL8cfeQF8TuKYcAok8j9+6LZuJ/vhvTgABFZPVCg2faJv9zXZhgE6\nGt2Z\r\n=tpEf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFkuWWYcdu6EkY7W/Sd+3DZVqwub9TNVwKCknl007mJ5AiAqUKphaUXn1FwgljTCk7v1rZLxAVr0jyvLcfiJIs3Mmw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530051716_1527657746272_0.7712428332182524"},"_hasShrinkwrap":false},"0.2.1-enrich.20180530063850":{"name":"@atomist/sdm","version":"0.2.1-enrich.20180530063850","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8d8f11314889a78297fe297ecbcae7e470ccf908","_id":"@atomist/sdm@0.2.1-enrich.20180530063850","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zFQ1hDid3hLUlX4scTio6Ned73/z3CmAAMgYpNQbPEAUlR8H7mlgB2KY2j9LA98ZUfpUo1QnTO3XhaFqKsrdbA==","shasum":"95be221fa65c713f70af4a5804d59364844c6037","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-enrich.20180530063850.tgz","fileCount":917,"unpackedSize":1947851,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDkgnCRA9TVsSAnZWagAAoxMQAJqm385B1dfR/vEXrM0q\nKbOTYhGGsWFb+50YZwH9OS0IpsVKig47ezUwt2glK8cYF2Qbhyc72AoTSVCs\nq8vy2JTpdRa5O22U3Kn14jFQoIN3W+Auy+JejcJgJrRZCUeqKF2IxmgI+Hhh\nGKX79MoQTObkgQ3G9iLtXwN44gCKLNF8njaOGAPvHzERd3OM9QgTWxtfY9G6\nmQU6Rz9GPTLdsi5R9Rq6769CBEILD65HmIg2MNMvBqKPVRrgDF0w67vp8isY\nYvBU1MEfeXTKUReXwBLeZXkrncrWIaYxYR5ROw3QBi+pWrjs66H3SUao7tK8\nQlYD91M2Gfr0MbW6o1yoF1AzU4lVoQATAT2sKpKaCMjTtX/yPdZVgtQdFHM9\nEZQ/lVAXFikRywXcVbp/eKx2PYx8LqgadWkeU5nK15tjS9JsRb8JIrsr3dVr\noowvrhtdU/x37j4bCda7ZopZGF2I9a7iZfOg3bmv2K3ukxbpAArCklBv2MSg\nh1tFbfrytQGGtQyHFteF1K/cDWZp574boJTFkWM08/NljXmzVa/pQvvs+d5M\n4Tpr6AQIRMULkLoxH8td4gZTpUYDSPrOHHg8n73nIRbKp3xUYwGTisJ+qdip\nmbXLmmXpgdi1PDdZqid6rrWXy4NHqpKaYiEr0nqSjBDDarHTiwcJNdnb6jYk\nPQqM\r\n=4d1l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHRTDBYFhKUuoJC9hrdLVyIrBi0L8njU7uSiUiARGibQAiB0cNb3nWrs4QdoFrZTG+kL9aQxoTr16amGYO/vu4E7qw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-enrich.20180530063850_1527662629111_0.780694653576866"},"_hasShrinkwrap":false},"0.2.1-20180530064817":{"name":"@atomist/sdm","version":"0.2.1-20180530064817","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"da40578e70dc3008dd9065eec38d62cb11512889","_id":"@atomist/sdm@0.2.1-20180530064817","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GvyBgsk+g1+3NrEJIfJHpdU63lFC03Gq1IuxyQ9QclnY7Zkl+Iz2H7xry2hWgedLhVYR/Z/VIJYbuvMofDw8Mg==","shasum":"35dfaf6a869d44770687642dbe01cad6202e684d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530064817.tgz","fileCount":917,"unpackedSize":1947844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDkqRCRA9TVsSAnZWagAAq3MP/3A6A7k0fU/kHMDS0hQe\nocdH+Ydt0mLr7PcLD6K5o7hDathXXhhEEWchA29GQsU2r7YUBHSxrFwBLmZv\nM88hExtFU+yzSaj89uIFLMJHqY7gi0ncEq6k2/YvuA2AzOSFcxlxGbev62Uf\nLjqBbmDDOSFVOILh82ViBySb32bvZ00WK9kKW1x3Y8LTQSxR3ekbwOKfQwDQ\nUz5eFCPQWd/JUHBUsVRGS6+B+mQmVv+jstc7XuxfjxXZLLm6pcQx1lJKkGWc\nNVBwZiJhGm+tNcBdC+aZR2gY20U4PBPRFbTlPF5lDJSfN7Gt6go9QeSciIHQ\nUTDwFIg5c/qwMiHuwz5o8atsEUDPdE6lf7BYEjuTSILIIJu3o0NLiroVnqXh\nfEyYPcfdCTFQDSXs9xSrkkmyaXQO1O9ysZI8trW9ww1ozL7e+G2UU3FvoxJK\nMFAJna/XQuLyhj6zpDSZG+bvgJdv0+Ar1miu1w1OngXGMn8yfUqrcLBtF7R1\ndNV16W2a3BGVEIhhki7wkK2LKnf94BSFz5mFmItvhGyDkhOFq1cKn9f1/BDC\n8I7p4k87IsUnIlJWkozHFc00NsU+PaPiQK4bPDsPt+trgM6wt7v1my/ERy4p\nutPkiBNFKDBidmjD50fjDMtJa7yRX+aM05E+bg/9V4NrED/DEfjFAeh2UZyo\nPVjn\r\n=0+tP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCp5RVUs6dKY996gq1IvbHjnixlS4SfpKX0o1Uc7reFowIhALJv0DnRqr10m9Lu1z4E/rOOfdt8NGRAuNgwqRQSaQ0y"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530064817_1527663246758_0.5411896791446442"},"_hasShrinkwrap":false},"0.2.1-20180530070203":{"name":"@atomist/sdm","version":"0.2.1-20180530070203","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a9992ef436161aa4c5eddba05d4d954f4b78ff7a","_id":"@atomist/sdm@0.2.1-20180530070203","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RT9lqdrNpJeWwtpO9mkiMNwni0f4qJIa66OvoZ5OWVv7vtTjcZxiKtiOMJzqRADVLlV0MSBIoGvHC9MfDlFEnQ==","shasum":"020042d12578b806806fe626fdc1f96501f7da7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530070203.tgz","fileCount":920,"unpackedSize":1948802,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDk42CRA9TVsSAnZWagAAdqwQAJi3D6htHLxFLSIFL6xz\nUf+u84UbE2CHyj5I7CYlgDLwhCv0hrQJZDAQjXWeiW7SIOnsrddPSQIkQ9gd\nU5CFSB49JMcWXFUNwm5Mp2eYuAKHbUTBkDBjPhAfxCfx25l0Gr9gee+EGF5R\nSyPkbSTFKKBKBJmDXIvpfp4MxnMeI8hn6skUNLR3qNkOVwMvRI3kjLiXjVny\n/rdGEeicEMIfDi6O0uMDM0Gj73HLXQCVb2e7eE3lIByTNMBQeMLTcqQSgeAf\nasR0SJseTdi3OfjSi9clzraGSzpEzO55HbfC0XOnvjdZrS4QPXmhPTa6Ag5J\n9EU+RlSqZuaRqR7gSqY7TuQgUGb4WUngqq3MUDgmsojX+UbwiMJJC6ANyb/S\nY6/+VpMpIu941SWFAzdo8UCykAGkMg4hMf/AsdtD5Lwxm87e4BAXOc1PLq6Z\nScX+MmsmlBZwbgR44KZI4o0evN33f12XQj/bNsamOZ7Der8GfvnVV3ym1SWw\nI5YbGdaS/TExdZhdqCuUoawvZeltpBKghIp+xa2Rjdch+kCjTQJZ1VBIWLS4\nVioy+X9+aJVpVJP4Zp3YoNdtlFHoLGJxhr1XTWEvh0BC1wR/T+AsBzCqwpDK\nGY2dUBlzTtK70WmekMi3NGpUQfz/m5ZpVFaW7RUcxw8HaV/q0hXs0jZniG9g\nAcXX\r\n=tX9D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBbsKOF+1EoGMM25hexKsf/dWOSHmeVt4yPKq0JOVW0fAiBS/wneslFqmrc9pfjaKZ4rnHVOa6s4/LSuMQEVw4tIxw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530070203_1527664180288_0.4080042578972678"},"_hasShrinkwrap":false},"0.2.1-20180530071633":{"name":"@atomist/sdm","version":"0.2.1-20180530071633","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"daa3160273e873e2ad7e3ff64a24f09c2c4b0cd3","_id":"@atomist/sdm@0.2.1-20180530071633","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hnsz7xtSJJ2q67YWUCscVABzuvHU3pyM4OpyLIxr2785qgeglmNYQtkicp5Tz3s9fHpWMzZUBom84UayF4Zj3A==","shasum":"f6399db5bf39da1cb9defec94586685dd7eb2f28","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530071633.tgz","fileCount":923,"unpackedSize":1949360,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDlHLCRA9TVsSAnZWagAAe5IQAIHpY7h7CX2vuJgwB5HS\nlIkU66e00S8YsQGHGMgoj3BtfXDs2+sX92YcTeKMoWZCZh9NiR6i1HWJgeX+\nbOzRoCjuds/bTU4PKNjE3cT/yLo99TMRmhZehXjGGc6p0r/BVGeeeAQoLaw/\nQ07dDmS2abvSkuRZAXhsHw41Nam87keUE87eD8CoxIeA69y1DV+5YBnx1VAd\nOT0Fx9uhKXOngrFyZMXaRO+kkfOfA2tAnugV7s5tUIsRBRZ7WatUluH0Fazo\nmwOFTtG65S5Mso5oTjKzMkC5EgRDfK8jsrM9EqP/dsKhlpuTu0WFinzdT2V3\nrtsEEsEWnevHrXVUcON3LbEKDdzph797qEbimznQF4Lrtv11GPeH+5J6BM2x\nwOxnpJxgHJJRCrBkR3veq+6y7Pi+2o0XaH165vmYOCRBbUlkfATtRZj8LgQx\n/1ZC0V8OayRK1m+yktFfeVTBcjuQJHzE1SGXirkFYdoOQx/qhua7llyTso8A\nGHNnH6/p+sfGIMJAIIgcpF5PPE0kKYfuW3ZYxL/lSfVXa3uThJncFL3EHs12\nIJku//bXtR1W6aUUpmITbC6lPKFSfBB/9jXKgmU+0B3zwdKJZE9W8wBSZCgv\ndSlCO06khHjz+XDnwOAkvUIvBDYnTzIiz3p94jBBD9W3wmZNxzW4U45bv9QC\nO8Fq\r\n=NOq9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDdO3BXY8xfzaVkIgFHsz64sx32qCo4wBzmqZvvMLBpMQIgeRkPTm+kcPKk9CtLWknRxAJmM7OEUoDyLJrUxEaU8qE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530071633_1527665097924_0.5895271892111542"},"_hasShrinkwrap":false},"0.2.1-20180530071326":{"name":"@atomist/sdm","version":"0.2.1-20180530071326","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d62eab43a4934ec940ba551f7d6209e508ce65f6","_id":"@atomist/sdm@0.2.1-20180530071326","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-opunMIKEMV5VKd766/9KFV15QtTEA5mVEaFSM8T86DEZqZc697afQyZKjC+zCrHLnzqVz+rcjp96ym3zU4h1Iw==","shasum":"6f88f5ba6c315c611b8f842af56e17ea02fc61d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530071326.tgz","fileCount":920,"unpackedSize":1948848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDlHeCRA9TVsSAnZWagAAId4P/RvSKlOWjGSsQGLPFOCV\n2pqAO2th8HnAS66otMr1n6RWRnkDa9oUQRnXvwK9Sdn9ZEkcZPPsXEzjbVL3\nSfBv1uXIVwFuenX9EP0uLkw35qP6aSlO2w4xQIx3qwnfyvK52IYZJLViOxP7\ngelz/Pke1qN0AC7bDzGZILcKhnXpC+xSm3hXLbohL5ac4uJe4N+OhNoSljZp\nSO4mYFL0uOU7HRdXjAlfIKaEHusRN7haKZ7nFOK4s8v2Q/IF5HvHfiCRj/aX\no5Vc7Ul9/TMwRfa/1bTT6VGV3RvNvsiQrde/dTzde0u6GvPm0L86eCulmsop\n57HOQNFlbK3ZPnI4Lww681Ssz4R9hcpKYqYcfHeCafm0GHH7xUl/9TdjoifH\nTCskpnpfY/KXu73qBOHEqT2yYTB8SD4ZFRfHun5sF3op+Jv/CBDk8Wy6hw+H\nJHuhty0UPA2DMF2AM0eu5QY8BjjXwvwcbQMEdL1XJ446X18RQIQHgxV6pEcz\nl7w8CkzpWFiaI6XjTqEHQMU+eYaqhls7HMd3GkBRswld9SlWN2iguw8BEM/v\nFDRTiZSYsoAaunwdEvLgQgGB9/V/wrrTTIgimCw5GNxZ/aRtdOjjoUUDkbGp\nviKpPUE5hBKB1Syj7vJYNiVOJEk9S0J4AGHYhaOYukqvrlKd0BCwSHsjwdHK\n4bdt\r\n=pGlx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/xkjqrE5XQm2UiDp/JxhUbLRlNuECUnaxBwTiCoTVaAIhAKciX2vqtUwprR55V0yuGdGMX38LH8wOGS8OHsTkHdCP"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530071326_1527665116501_0.87113996839282"},"_hasShrinkwrap":false},"0.2.1-20180530073225":{"name":"@atomist/sdm","version":"0.2.1-20180530073225","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1062cbc3e8d91336496c05af9cf772127de793ce","_id":"@atomist/sdm@0.2.1-20180530073225","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vp1RQfPjkPwPAVJI5hjetNpb3DQK37Kh3aCzAT2Ksro5yL6ksHBtpd9YmragApvhXQpygF4QUONAic/P3w3ARw==","shasum":"ff6a47544988e515a7f495ec579471ebf9bdc2f2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530073225.tgz","fileCount":923,"unpackedSize":1949443,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDlSmCRA9TVsSAnZWagAA0y8QAKGEjA1ggOGrkYeDoxYl\njqNtTUKWlg+k7dCKJ4hP7f0N74AKzfDJA+IPjMTaQ/b7swgJrpiKZV+yzrtK\njUyZymbCVJuK6rmXG8EvJUQjhRUR6zszh/GgvgaEyZjzLmgeg4AHGeL0H7Jr\nhVKCo8OiuRPg9FeNZHFFT+wX+aBkPFVuQ/8OIRlvqcSp5qrraOsWE1fch3e1\nDasI0R3gUE3Y08rQn7Hk4HDb3UrBdQkUubU/r+g2uArQsNVkV2RMpwWuBwRz\nLNaMlUQiLskdZmrCO/4ZRkMKxDIru9QiWJxcUsiAdnKfQXnvhPkp66MewMPT\n1Kt8ua9KArMfbYbSYIX4PGVYE+8sE2C2+Yg4HE72+B7FVxgt0xmyvpCSyvAb\nhxmYbDydMQA+nxczZY8ol7aM++50LjxsKRP7/9/c5jhQcjyspbm1qqIYSnXZ\nbOljdAIsL00FjWxepqT8lCnIKBvVRQJEiGbkI3L/BGH2tE0SSGp+EfC7qSNJ\nRMj6k6h9AryX521MD/x3uhp5ZqgxW+HB3FImrHazS2Qtls6HiHJV1lRev9ps\ndaQ9u981if+ZqVbnxepA6m/xmzv6kxlVapqyf13qD/vMraqz6Q+4yH1Vj5MS\n2CIcrp5fDiXoKUqQ4bv8r5PB8omTesm3GtTNTn2P/4v/TvRAsw4YKIac2FmE\nedoq\r\n=lM3u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqyDtdKU/8zE/T/TCbq1rR7C6fl29tJUsHXkg/FZRYggIgc+MhrIBPTULKVdSrGZNhUiE4dHmAAACMXHYPuITVn5k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530073225_1527665828453_0.3109288182438168"},"_hasShrinkwrap":false},"0.2.1-20180530075827":{"name":"@atomist/sdm","version":"0.2.1-20180530075827","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a5ad4a8298151a57b4c6e1b5ff7182e427550fca","_id":"@atomist/sdm@0.2.1-20180530075827","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eFCnzjhWEJR7yuJpfxMiCxINth+WA9f+2c9PLsKlKLky7hcHmSOFX9gWVE4I93575+XYeuDoZ2OIgXImNdu+Cg==","shasum":"5dbcd467cbe5d49e67cfbcd74e78e4b342ad0401","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180530075827.tgz","fileCount":923,"unpackedSize":1949442,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDlrHCRA9TVsSAnZWagAAVjgP/RPrv9iaRbuWc8wyc4z+\ny4iKCPf91gahaJMbhmsV/AB/PoOnUL0vnuYMQ4oQO6ZjwbUOnIntc10bRkWW\nBzw++Yhht82yUpW92Ddz2BKaUxNcarrZpuB9kitSqkTQ0apG11GgVdOB9mQJ\nzD+VBs5puAQOfN8SQ+gIRYzIZ02tA53nI4H8ZqL537nopH4UZX9fmCpMEkFq\nYg07SgwqJffJI0o3+PPFl04ZWT1HyMc+wY8uvvr5ZkreHkwyp7x0Bc/vcCnD\nhnYYrTIjX2KE1ecpy2dqcLI4bunPHffqXu5NsMRez9Pp05Kz+b1jNV/+6IfD\nj8G1Mz1/+KsHPcfjhG0lLGnU/eaHxir8moF734EwZCO6ARUxcv0gHZt8AnKu\nFDRUcAbNaKMBJKZSViWjvmq6A6X6hWBReoPrLufUfBQo2q3vBu6GVhjxYqh1\nP9xjJI9PrSuG6PL925cYizckvodchUDoJ2PJqlNPmeAihhU5s4fcvILe59Q0\nsxqr9m2H/jCxVsYXYO8j29khwG6RiwKg4TinNEnZUsAhcyd0mHRqeNBv8+gW\n/3Kjtsz9kZjkJKIj4NfavEk850pycPB6PFppnNumi2Bq36D0e5SmnHmrzmWN\nT6ljQ+pw1H7fiZi0lR8+2eDZKhsof987v7qqoLNgtnwHmVQ66aX1YlDYzslz\nQgKH\r\n=o0Gg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG52u/iy0Uee6vxXAsW5/wgZrykUm59TaXzkTeZBlp3VAiEAiofdU7sdEvNA4D1rDkyMAw3eSRJZy5qlBUngxcbticI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180530075827_1527667397227_0.6131512877803067"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530090025":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530090025","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"905c259e8b2e91651f8fafcd2bb326a932fd7d93","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530090025","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Z2+cZ5J3Evw1AWQ0A3vOfeBuz88+U7RJOyihBxH7nPabbVGEUszF2haf3aHuuVsPO54Z48B+mmFa5JdNZ5Hy9g==","shasum":"818e67c549f2bab51bb2fe8eab740f936d5a00da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530090025.tgz","fileCount":926,"unpackedSize":1943667,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDmk8CRA9TVsSAnZWagAAurEP/j43vLtOtZbmDKcf9uxf\nklsNXRvCvqExe7gj06tgbrTJ7PvjTHKfuCricY3q+468SFv68cpN/QwtiSz5\nxNZaLFA+dbBVGGbpCd7X43q9uaFAUqfk+braNaQtlyou9PHQ84MPzdmCHUA/\nd8m2CLieybTivqPBxrQtAwM7IM67sffQduPqJ5vUF2ABpTnv5etGygKPr+Iw\nVNuH4uXEQs8ITmViPWEhBmO98p07tSyWPhuxvoLmmNSGC41iw8a9g1uECbiY\nxredxsfEu8C+YJD9OTmDxElfIVDQ+JaDX9CL107Pj7yVbz4zQlbDWY4+w/aK\nrMA4hsG3BqawENWwzXu2QqTru++elHr71HlHs4ZdZ5o5LrcINJyS7nbaNmVE\nVROjEnGFu10QIPponzxRmEKfnM68zBVJo7m38SrOKG6qjsnJID0/BENY5MFR\nOQsvWPWCu8nLRIFSpmoSmw6G5R1o35xU2LkEufFshA0XlIijG/deol1Fd30h\nHQq3nNmv4rERZuR4vnAJrWEMtYa2xTj6IZ6DxXYFZpN3xeBBmSDjxn2gvCIs\nqqrR5aYMJAU00qeQiSFhwAs/nKiLr3qug/RtRlfvYoXQUI9mQoo/R2jQrkkG\nmMcwCxyLd1VBR1nhn3pr0wOb9Q2UeNy6oyOB1lxQ76R3WKlElAHqJkcvx9/k\nBsuL\r\n=VWSA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQDG3Wu4Z80LH2F7DcwVus1+6ERReYc1cuxXvasBjKW4cwIfD7EukCaiOmMBvTnLDxaMi81PN5zedJM4838nFFbOww=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530090025_1527671097816_0.696195242123717"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530095850":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530095850","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8d59c55d1e0c81d1b3eafba5547b41c0743c82ea","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530095850","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uVvzlaUAEg0hqcVu9A9+Gy2AQdX1/vAjFM2eD1Y3q16Y48oROM+B7aIGCif1n5UlykknJki0mOXUrQsUeeEv4A==","shasum":"7a1e1eccd11f413fbfe5352f5c54e6495954d6cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530095850.tgz","fileCount":926,"unpackedSize":1951769,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDnbsCRA9TVsSAnZWagAAXI0P/0u8hWiZZxMVKHjC2RLV\nFqy5Q4vK07KEEH5H+PB58AX/Pvj90meNAF6+DjudVWG8g/r7tc5TlTMXnepn\n/GbG7mRo5krt4MSeykdNoV6dmdxC0YgIJk3V22nQ0+ApZVpNQxNQLjf5SUUa\nwtGIil8GmEBMX8L8c2OfqxJxNwtFdWyMChde3T4vhGlNgR0hpWwI1pSWWru9\nvGnfiaDxhIyL8LH0HDSHst7kbcRNRo/FLkrU0XdJhB5N716LVBcOeQc1peLD\nFkYJW1k4wmPaLy026EsGYl3ZTvBAE348wC+xw1Z554l6tBIZYljBS9g2PwoQ\nb/sxpbEEWAlhNjUhjLXBoh5ruUZ/0PiFtT/2wSp73Fzod5Nb2h2gBvHBxBpc\n/DBjYOhA/yRNWJ/7jDN+lsVPZq8nMOrcRFHkWarKNDQSTVOp+lZu0vGwyiV/\nDzIaXn7+VXRyC2/Kg2PjeD2GdpJXD9SR5KhKaXQCTcFXqk1+04bK6iAHYBXN\nb0c9NMDT/3uoMqhDKEQPwvE5DMfpDPYTMqhSqw/1bhGDv/kyouS4DfTyARg2\nV8LzbWD5GmqDiq/NePRIUhC97k210Heka9ZABZimkl7dmEjULg7gJ4EJlXyZ\nMRRTlWMhzlBgX84T2rTn8luYyyFlF3B4KoUqracga3MQbf+cTz0yZGbOPTOE\nTgDM\r\n=/v0i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPuupvt6/xrFQFVdG3sxKhkxg30dORhfwg46Rr/8xphAiA04IzOwhQivxKUo4YEmFA1wmygy/4Q6dzsVZo0McfiOA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530095850_1527674603078_0.13044736257717293"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530120209":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530120209","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1746b966b1fd10b6f6255115c84ce99d72ee5b32","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530120209","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0aq7asujDo6P9fkXW1ef0z0KhibyHkYshmV00lLIDcsN/dAVEl91DaBfL2Rs4zLzLunzzcAQXpuaNhTFQK/SBg==","shasum":"0d86a7f4da96aa3a81f4e590e3c51f63b6e16c39","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530120209.tgz","fileCount":929,"unpackedSize":1946734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDpP8CRA9TVsSAnZWagAAohQP/01gZN0Tiu/nfjN1rIy+\nMYAT6kyJXzmhemY5dKWc1Lm5ive8AajvriKBCg5SCuVUhszw441FtP67iDHv\nF4xSmrBEGTB7Ga25wvuH4jLybZrzTFbdqPtZ2/hZM3KVb6l+9DMAOvbLdIs1\ns4ucvUie74ew9AaUBT9EJdPBA3/Ee5i1VLKYSRO6DRGTw4DY+inABL/e8H1O\nlz1BXlMUX8AcOuJvHHkWtij+Y5AlVQfJ2yK8HQp52sdsaMUPPuBHJ/yAoL4/\nk1uruFEZz+71Df43bOGiePSP9+FLBlV3KDyjWM9eMfQlOYZQXFBHYMLMSEmu\nFYdj7S47H621wZXXMe+WzbtwV+bMdmMRvkRPiXlMADt/xuVUd9zfHCR2bmKJ\nIvafzruBdaOghy8l/pHHJ925H46lFV5PpbRaVEINcCu/7sTjyE8GbevPAbiJ\nHHaD0m3jlGVSAIuLtgfTuAZzT9j0vzbRUu2dJNshVeCt8a7bo+jXaw7GVqcl\nCpHw6JB5hqAw5OS9Qq717BSeGN6ee7je9D29t+pahWi94OdWa3oQ2PNasssa\nPug2Ua/qwypk8YHSrqKSKYnde4qKH0qmiriULMA/atTsa6RMDq9ZcwxE/5TL\njFpiwPaTuqIFR9/Bpjz/H9uupcCgsObj5QNB48jRMM8nVF444NHAt7YaL2xp\njRqu\r\n=N0dx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCcfntH0hVEWn75OD2z9hK/EG+FafQEqh2LmC2GPmeSRgIgQYT5rYjqU+HJ6DcspKqTuxb/UotJ4W2uUL8gu+68AQ8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530120209_1527682042770_0.2835698419053452"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530120342":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530120342","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"67a080482fe3a4986271dae60aa08e707cac8231","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530120342","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S6WSCQX8b6Ieb3BXFLCK/azIIS9keS7qeFX8CG8Mar9ofYaMms2gnFO73qIWn4jfaXEUA9J8ymFpNEI0OCTIug==","shasum":"65bf723fa31e67d170f63dae953e27a5a6e803d1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530120342.tgz","fileCount":929,"unpackedSize":1946396,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDpR+CRA9TVsSAnZWagAAMbMP/0dzhSQ+24dax46ZOzRv\nochtMyfMi+IhPEDeXQJxFJ07Ia+X6SnQCdWNG/F/o2fR7Mh2A62FPi92Ucxb\nvL3TD2Xo3YnCT3CYQfN/WVLdDNMSCcRkdzNZ8gw9hJ/7fP0YTyLRKsmqpTbt\nl5NLD+545zNFjBSx/+CqGZ7wUfwPXf95UqOhp+Yk34JTmUTwyGPvb09N/zT/\nP8nUaKsZPulXZKFwhC2QBCwLOMAwkcfMG1gjERLcaDsTvlPYsnEPUF46l0Oz\nZCNIhmQxt9jDY1v8/JFwfYyzIgximRGpz2S3s5qxikH3JxoSGx15Zzd381lx\n6eFUvpWFry4sr6M5sqo/Naa1kS723ybYIXjhVH+qu+NN6P1SA5X0OiN7Ae3X\nYAxrWf7W9BZUs0Xiumu4S9QTqEy64ZLGlMowUcL06nPVsmfVSFAfePkrQ3Iz\nIqplSolH5Hmf8WNr/EI5ToxyM3kaLmfV2x5WhO0IGUIDZaEv6CMEfWlkBJiY\n9WvLqzqr92NX1Som37PYGUTy380d5Wk9vFngXmhKxZ/kQgWn7dBCzPWnKkAg\nATS8iTVasHlRkMk4Re1X/cq7BOJoukzGbcNN9Dpte7436/O6FALorb93rtNY\nxO0yYfSM/UYLSU6L2YD4MRW0MiIujd/XWzXM0nI3Rzy9XiHen8n85Y6dLRoX\n/sAs\r\n=IFJG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5D3rfu/SoZkp2Ot6oMNcFGn/X7TpkR9kthbbz08eT+QIhAOiegd48vuuSEty7n0dPPnPgFOiRa9UXFhR3OfhvstdX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530120342_1527682172066_0.3029779943285593"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530121242":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530121242","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4bdb115d2c2a78e27581d4ac4ea1e96efe13a634","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530121242","_npmVersion":"6.0.1","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DMdFNguUfDjNjrt/0a74YK9TrwOCQVBZ/pzgfMfA9FJnAwJJTltNRpVlw+1G4xS4wPaN7OagPQOgJjTHszJy0w==","shasum":"3fa920891a421adad05598c555a82a5b47b2a5bb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530121242.tgz","fileCount":932,"unpackedSize":1955138,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDpZXCRA9TVsSAnZWagAA7ScP/1THdNBxy/FOmgag60kG\ngIap3e5mskNzm+19gjJZ7pX82eMih1sQv/Bf/fGq2JfZru2N21vnGaUKUdxa\ng0hBgoE2kp2RLKR961y/JI1Xc1vEnPYpMYBEROmaGrLrffgyvxeia4oswcky\n6nqzDKAG2iyPZ0cURoFzVBa5LVfe9rFco+yQ4bjk/hfothr6HYE4qjQJwwq/\nM2v80t3e+Vqja14H/BejF4qFfDCQPYjkbDEDa7T0FEolUVfNQNFEdT4x2WwZ\nQYcpTcqz6IBdgz/nzI+PQCmjhxOWrEjtMMnehFqEem+M4eBWqr/KTEvN1Qmi\nlQr6vhgNeNHnZ6LcdHUsaY4h8GqiGWIJqZz11d3LtvLL3i20WrqrSUMRflcN\nSDJcHYvpwZHEdbFGb1hwSQqwYCYJZKC74YeCqRLz2ME9g2lZxL8EgkiHnurd\noHParMz129eIKce72F6FQnhBRCPH+xzo+VTNYq2RMojjXuPMo7BALNJ9wXZa\naU8s09gTUbRM55wR2nvSOaVLyK3UFJDmcjH2IKupjNg53qNqafB0cKvjiEb+\nE3h5I3v6gforzlufd7xy97wKt4ANG4GjgvvcJpAughMwcgc2OMy9fbEoe4xo\nWAMdyKz9eQ3qyGCRcn75Vx0zuw8QtNjl8omoV6BkP02mJ7EyNptbcEb/2ebu\n/M/E\r\n=JK1+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAgCYlmV7j2W721lt3UOwrs8YHAdvqEPZ0B785neBFLVAiEAy9k/FTaQsxpuWKI0k6wnRDBfBhtSSTlcgMyye28EgCo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530121242_1527682645284_0.10944835448057177"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530162437":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530162437","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/r1SNOyLhJX","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a6c3d2504037dee0e3d020cc3a9cbcfee6485284","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530162437","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NZIUzzoRPl+j7wH1+LdESN2utK9V8tdeMfmghN68iPI16suCCIoT7ztVp1X+s1zIMwLwKQO806hEKdmYF6hrhg==","shasum":"7b26cce8f7a0daf5572bef4c5b0d7d4418a99094","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530162437.tgz","fileCount":932,"unpackedSize":1955137,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDtGJCRA9TVsSAnZWagAAZIcP/32Owrt1uOZWirwS/QAY\nv3X04o9PzK3R0xJOoodlXm+kN8ccUMBrX6+jYjb/fECy0HDtwpfrxeUwN1B3\nWkITBin3ZVsrBhqVBgb8IN7gSvuBeP0ACbbFDPDieFONFxZEljHiguNxyDSF\nDF33inXuT8v8GZOC36jIpl9rVh1yde7l5R0ZJaqqkyzIbUQ7qhTJIdLYSwNj\ndaX8rYOEr79AqDmtFOa2cLKhNEFDcb1tRmYqAx1C51Tq81Jud3oCpDMMZ+mW\ndULYOWwAH2n3gr9w6pa7Mf3Lw3dU5flYdcvRzOGhQ285Wti/phxVqErY9JP6\nKKJ0CbdX+DwUkS831PD77ECOadX6FBd2iH4jLTmYP3aOWgk8JlaMAanmbF6a\nHJleMqt+Yc66dEg+KghU+fO4Pm3O+Q/MAW844Px7cAH/fRjLqG6keMeZzy+d\nRX5GHnfbcMbgB9K2xMzEehnnBnvENg3lqcLJrVYCmA3qmUbKJxS/9nNhNKW0\n0Q9TLMIEpKzZkvuAOtllAtJi0OdHs5jKuruYngsjKXaw6g6egAsj/HBaG0U7\nv9uX3v86i7h6ux5T7V6msEKT2QmteP3R1Th/DtMi1TBUroZbhimbcOr9LczX\nSUPpirL1YA0tghLrysMVGQsf7uGCncopYLcBtQ57zKMJQfgvpxQOMyKwL9xk\nwnX+\r\n=bAvC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCeHyF+yqxZU76deYNdE/so56q9TxBLlO43JGtTER7vwwIhAPnzhG4FBdrQXpH75mRvFNk2/J5bEhpVaF1gYdd/eON4"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530162437_1527697799353_0.3382129113504544"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530163753":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530163753","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b3af8646739feff046c0873b851e5215879ca6d5","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530163753","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+/CSNBkRa07SeXAb7h9Ja9APaY/OavM7dwNKQvRR1GwGiXTCH4DXD8Q3TzFBnPN7eWP5P6QXOYpCqw4DAffB1g==","shasum":"67f2b6f2c34ddf4c9d950fa687cd5a0572c4c3af","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530163753.tgz","fileCount":899,"unpackedSize":1930395,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDtS0CRA9TVsSAnZWagAADo4P/RPjkIQUaor+tyr3dile\nu1RaQ54Hr5n8SM/8HOXVKCq+qCfb+/Gjzv94cUGBWwk0zruCNn2CaBBej9md\ntNUWRBMsmRiHDCkiRiuZ3p07Y1Qp0YJHzaMuvQ5tr57ikuRSXkCCkCymzCT5\n75DDHV9st/4EhCCM0ttyGhBztrPWVd9KPPSzrkezta8ujIm0g3mEtJ8R+nET\nsot8lBKJ8rWSmC5qyCJEXCOEqbTw74rkStcFhp0lXw6W8fn/0g/IrNceCXLs\ncYpfYze1VHeqkmvfufOPdr7TCovhmUWTgVJvIWlGjtOTnzmw+6TD/p52amNi\nacJPZXeFBu7v9UBHTDcDiY+hmIYvYjkFb6d+IWKSSUjmFBNjE2UhMqfSWeox\n0z4jJX2Y9+1G+3lAdOnHNdUtoKtKXKJT4ujkPLHxnD8YOz9bAQ7IwgaRyeQn\ndl2CO9gEU7uTrXTQOQ0fyeNOhESmHKQzU/oUPXC3mgx90X74d3mRdmmC53pH\nYULh9p8j6Ypu7OVXJdmK//cfJLSvRCMBVmgHodCkeeQ3SJt4IzHCDQtNi/WC\n5Gfbvj5EC11hSe7GgPQWmQ11uWfCIaM2Bxzg/B5S5OSAXl4C7nhyVN0xfbag\n7QVDnJv8BWZRhyojEkIDhU+O9euq70MAgJ+aUWAa2nM7hsRMz0o6zMWLActn\nXTCS\r\n=LWTp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcoYl4jhzNhLTiFGTGchdoSDKI4/yMAypdBq0wOKDG/QIhAI1sym1kBTBkNxTUEZvn+9sKF4XE2fih9PahuA5QP6Zq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530163753_1527698610554_0.43137437938410916"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530164525":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530164525","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"055dd4399f4a34e63d6513a126484a4964fb97b0","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530164525","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5Jl263l7/szXkJypkmEMz08LxSVbXhjYD6qRLfFDan5Rv2YItxYnT5bfJsIZnYu6Rxq04viSYRQ29OTA4hzGpg==","shasum":"1f65a3bb822c31181294691c86104c12b767b2b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530164525.tgz","fileCount":932,"unpackedSize":1955137,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDtZeCRA9TVsSAnZWagAAk7AP/AtBoBnC3vtvxHRXu6ii\n9Wxl/rtvnBjK7eSNryoQilsCID1MsjlrsYNsxiTuYoFmVU9gSwukEHZ4OIdZ\nohIV36xUibbQ+roL1EBe+HJuvnO+/u7Gh6VBpxpZZVKvRFY/pJP7bmz8b4S3\nWo3rVVXWWqM63IM40vti9/PQSZylnL3r0Dcq/7G44lvFk4xsSndB8TBWQwOX\nsebiC6szRTWx53qzFvgGht+JNjrnqfqyFwIiipTcllu+68n8M+TtlQ/sF2TN\n8iYddTby3aaukgWpobT4hIDSMy0AHZoHXqA0nFAxtu/aPkFrgREarUJenMcp\nf+5dQwMp2dS5PqCXBs5Wpm3MNVSN/B93igBkYkt8kuoN1d6PtUq++UN3RLzG\ntJiHDIIjmN9gqr+pmE4fErQ6DMwWDfR64GxEH57/cCLagYP2JMwi27xv48g9\ncuyGHikWg/FdNcXDmuUeGOrScCvtcYt0fibd5ezGw91+saVPcil2LChaYzJ1\n9Y6gIL+pUAN7buHAYG6mJy8sP7py76vD3i1rRaVUPUtpgKYRMZJJuQszIthO\nefhNdPQsFNXD0XayHTXjdqWFH7CrJ/CRQATKlElfEvuWdS7l/OVJEnyRK/D8\n76mKZYYqGe/xnxN4m0iyxsFgKqDqRU66o/Fyd8mwVbCSrnD15/ZPONTyG2i3\nH5i1\r\n=h5YR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEA49TUllx0JWGsM/ZhFurNvLrbHHlrhafgVCkE6XKhSAiAORGezin5QhXIvZl4JtPke/XIwMCbchLAIU7ArbrgpQw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530164525_1527699036398_0.48965580416292065"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530170220":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530170220","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"af5708ec8ae8575b943df79630d618b6dd284e19","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530170220","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-P4yp5A2dAIhc7G923n5g8OUZhyS/ig7st6vB7xy28YIonic50lgnXOtQeeGobyJewt8Bjx6nZFj6uLL64ln78A==","shasum":"66b83a64e92ec6c01273ad4816a369e26a7bccd9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530170220.tgz","fileCount":920,"unpackedSize":1947513,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDtpRCRA9TVsSAnZWagAAcBsP/iJj53V0QUGUWnVtzOJj\na+G0bpoXF9dJbBOENw83gPdswL4yWpURmtCMX7OpX2TDMs0ziJIS/XRlTiOF\nhhgoNQAHSfN7VaqWL/62BldKrmC4IXU6O9NaRv9apFSxJHcxxn5wuzfQWs7P\nZon6WNN4bfz1KWP1zAiI12YNfe3O7hcgjdH9r5in70A6VgJHa7OqH6J2JLlV\n7tQFcSr13OBB6dhMIXQQU41Bepfm6tH3LzScVIu+6bVvJcuAtspN7IBsDtQ/\n2IP8EasSnOqfodwyx9ozPojOQ9HciUU66SG5KK1h+ZeNWWh27ps2h9oXMN7s\ncqVvTWfuJefZlQo0OlGlWBkR8g5dbjaTHuvSjL58aX6DPVpK2nVZUjGNbSig\nQYrluLpInXafXrHhKJvxOv/NbpzGhv4VC3dhv7wWXhpm6awSfYcJ1WjWMZOa\nQ0+1PCPXb7lFSc03CaifJL+drVkSykM0nPDpjYaEN7AVHoF54sXMqm8pUJHM\nuvM+X4EfZA9amp6frSU+UBilYilDVRYjL/MPNajNC4D+BhlXUgLhU/n/QJUC\n+adOtw+8BqsLmV+Sp3SN2AJRB38bjWIBkxJnTtUCxlt7jf78QlROiBtkFSG1\nr0zWKcFrtQfCqmkhbiUjTGZgwOqDwzUExES6xuV1bTlN6Av5N5sTft58P5vb\nM+EC\r\n=M6q4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFgFZgHIkVnfaAg1LknQzErHwAK61RwJmML362b4YLAjAiEA0Deahpmo0zrLVdLqlBd/WFkQi7wQH7jtPV1QhWuEArE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530170220_1527700048501_0.22148809931451163"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530192143":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530192143","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6b10ce8a92646599e42e2472ddaee82faa622006","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530192143","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2lxmxULsnmZkvsTYNJaV68njoF0acLSYyjIUAZc1+lQQTffxyXrannMBSkOU/FVYKn1AiQkggRwehP7qQA48BQ==","shasum":"7c5206c8479f177c4edfbd3b4b1429b66a314eee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530192143.tgz","fileCount":920,"unpackedSize":1947941,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDvr3CRA9TVsSAnZWagAA7c4P/jtSr5rXN2NZL1iiStqn\nM/U6FOwByOm6/+ngBr31LkEN4sSZBBXfXF9gNGmMTNAiBhJiIhsriPmU86At\nz0/bnYZYDUG/6Oy6L6oOVFX8G7YmNlMJyMSxDaq63GnUcv0KNM/uoRAEiJIn\n3X8hQ2IypLMXLuU9HE/vwFR/xS9+qj0nDqvrrsxwSmpI8/LCPyszUzBf+1Qj\njfueYXcYu25LBZpqozMp2bTezq6ZehHHeDov5NmRSzX3S7z6bItESowKOvAN\nuZvY4a4RmFgtS23g1ERfAgAkBfso9J2nBC+hPYp0vWG/vBiusBgydTv1qg2b\nntOc+5zUCXEnANDn9n/uOzA45DgdVTlq+idgKm+eYb8ZxbmR+I6gCWuOez9e\nalf6mKuhuPvOD6kjp3xeiOy4S9T+0CCqlSFzQ/ysNVLYUlPNgmzPV4gCY2jQ\nEjpdioJj88gPvGfvkjmqSVW5Nx99+8YTW+N0oI0lC9Gtf2ta0KGwBmLUiBn7\nbSceyOUBlmSsucdPsJTY1ygbazPlvdab/WUKS3gGd+89G3JsrXvy6VL6DtOt\nE/QeWHpzcjyx14bgR8acbE75l4N3YDvhAC6RXm19v0itYgnCoE9UWJp7XYXY\nrVLdNwPz57cT44KJIvpT0dOBo4756QEP/EX8pH5AzBtl+S6y5RjJcIRj2nAJ\nyjhs\r\n=fDR6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEqIg/tfxecpTtMOl4CpcWT4w7fXgMmTMZo7hMoPu0dbAiEAuOduNCGNQ8bwWCQsEJyuT80CUw1tzYgYMcWT0DYAIAk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530192143_1527708404173_0.045268930456771495"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530195034":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530195034","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d9f317a22cd88a64e6dce2a6cb201fa136f4c402","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530195034","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oqRc/+uG5zSZkycEuSaVw3Z8TpWAQJlG6Kc89xQ6Q8jWuYTtrTa/G5XflPuAxMBfr92QkB8IGG8Oj4jtlOc1Aw==","shasum":"9941c30071c0f9d842845a864bcddfb6e44efa56","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530195034.tgz","fileCount":920,"unpackedSize":1948186,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDwHICRA9TVsSAnZWagAAI8wP+wbwR/rBKYqoTmv1CCIi\nokzhvTna1H1gn9yLysXrxUf2i/1aTjtfImOUXeifzTSroJ38ghBUu5bcfDgC\nWdt/Z27JYxDhxHbINqE5Ws2uE4djNXr6VU7ui9hMDEfuR4G70Jo33JiUptOG\nza+VrLe54xr9cwycMkSw8YE0ludP9NA3OKqZq1Ke+kH1mVeUcmSF4G0RDFsl\nU7kCDrB+RrK0t2rxBiysdak9uLfS9tSJWkrDoE0l2JH3xrr8787jPz7pbMI8\n/Bb8KUiYTfJlYsYrcBCs/xaKPVCJ1vHReO7rmwIhjnHKPZa28qQq7RqHmDCP\nKadlNpXflmQA2zzyWRJ4LzqesG4ntbQRUHCxvecKFLDDByR17DmvhZpPRuYR\nS71iLG1DD7pNT+o3WVEBTimaSZogmiiaLwE192nB/AEgF+J2NpGFIiZndFR6\nPXoUOKB7J0RHa1WnsvO4Heu2ha1y0KMXGT9ZB9xFWCNCtozr+xvr7/UW5xbh\nPn/jDvYv5t3HkHJQS+6BJxa/mlKvsryKenJRvESju9r/1hYav/8FXaSrziNM\nwYhyAaVs8UVdjFh1ZDyOdjdGGd7EGGOifFaOvxA05lezv+0YVFRyMGCGG7w7\nqhFlfjhFTXWdiodDm/3GpFgLHc7c0sVJ+TyVk1EvB/sTvpJlICqnv5DH/1io\n+ai5\r\n=HTuT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/XMxWslVNmxs0HrbHh4BZIVdVTtpBtAEbsQzlEa1RCAIgRI7ndWHgnXl1sLVCiVRTOIiTNNkECQzuuqaQtzCvSVw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530195034_1527710149288_0.9416690046817981"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530195232":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530195232","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"92abb7e4d335d6e54a7ca51fda99a07634134a31","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530195232","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bW89vOHL5As+m4qFoR/++FWJmMmK/OktCIhZaIWCmi45OydKUphnmQTjsGcKOWxTKkN5NC4CxL2ZecsSF0T3fw==","shasum":"d91089d7a68c0806ee880678a97a50bb31b7b3cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530195232.tgz","fileCount":920,"unpackedSize":1948324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDwIpCRA9TVsSAnZWagAAtYAP/1Q3iTT8RJ4f/40T1U1M\nE5v2pd/qok7X/SOhtMJZWIZlDwlmMcIj32DMZEXOfi3Ni+eOn8S+7753+iyo\nMj0WmlXleWWuUmIGE7yPWQpeN7/lxY6YYpgLVXiPxINn99ya6iqAFzWbvAtY\ncEE4M8jA5w5NqxeZ4GadPHI0t7g/f1aoalsUbTVVwkKQoANexfty2FvSnSsc\ndsMmsQl8y+LDTJMy3za5b+hLkiAaQDOxBUziUlp34vc9Ov2echKIE7tooLJ6\nYwAf68WSSLV6Fvpq+dTVGuIIPRd9nKXTugZ1ouSJmLiAbTBjEzBtVoO9E67t\nB3x0m/ocmfkJzP5cuEHPUV84n4OvbdnFLya5+XxQ5h0S1UKBKKOYcIXmjVKb\nSq2wWygqpR4BZVsRrbejqk5igLACYeeKSjkdgAoa6MI0yfy7qDCDZ7FuIFLv\nJ/LF+FWAAVxAMRdmzJpwn9zTKV3IsQurIw04xYnPBi5A5/Zy9QhI0P9lu2eg\nS5S5IaeRV2SraZa/5yY9bA0sZsfqAe5sV0YByzOVfa6c5E2lIEKUD38K/pAI\nsqqQWtGcJ0rPC1DXFvOqpfsGOr6L7J77NIZ8Q17B4KzGx76zb3+gK1v3hbF+\ndF+qtMGAbws+mNL5XgCWoCplZzc0LORNUbTgWrPP/uwqWwtfzhJAaXa448B2\nkXjo\r\n=80X/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD3xAF9iz0xFXRnPaSG5oEsyAzm4xcTQsCXpgNWhSYR7QIgHcLgfNSBOZ07KEbGQYbVg5wRIDI3Wh+g917KzQQbiK0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530195232_1527710246522_0.8635641018301239"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180530211449":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180530211449","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/BJxzuM1V20G","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8eda8991646850ac25e3c163f620678b48fc1480","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180530211449","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nY82Az6kqAUWgscSNjDNHc8ueTyMbd+dDb0eR0muleAag49/Hg/DYL/4lA8EUm+fingh5RI5/PDmoTBIogLvew==","shasum":"61dcd3ba532eaa7d3fdb6c08bfc1f51d27ea0042","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180530211449.tgz","fileCount":920,"unpackedSize":1949213,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDxV8CRA9TVsSAnZWagAAFk0P/04yZHhWhyUJXDmXHjcj\nDc8lH2kJR6XSMGlDW3JCFont9UZp4EzlSjDkhe++23uj14D9nL5vizCquq3F\nafdyqWtNv0+2WMhRoMkwnVt7rKnLpN27JNqqPNai8I5WIYv02Gu5Im0Mpl6u\nsFUdudgVDd5dNVxDIO8zWx4aIBVNhTv8CLw885O3EpkfdGMcqQ//ddSdQg1O\nCc0dMRa+XFgDQHVdBe1Gmk92DBZpmR2p2FWzlHe2oeq3WWTo54DMSDkM0NG4\nEa6499uqL47RIHg+2vWb6/4oX9bwSArOw/SknpW7jUx50gEGTQnsITSEmlCx\nBPdGhOFyYMlbJugOvKbdgsQeu5jHYumHvRW55YB2/ftRr/WCsh7QgX2jf8yW\nrcEtB9m/ZarxtpssruLXSUdv3UVqbPbOwq7lSuV8KQFu1f6R8U0C4IS+3sYt\nkyOZKSGFybQ1RhI9IGJqALFlQGVI2wYJqpIXy29H8N5E61n0AvS2tolO2z2m\nyZM7sMFVimBXh6DXyLni8R7xJm2jj31iED15ZptuxmOFemoFsKrnQ3QGswOJ\nPOqn2VanCoMMIW9my6DNc74Dubima8nCthIW4A+SUElHXGhA4opKvjkLRdsb\nfomCE/U+a62A83bClanojJXREF67KO/85LGxdEnexWsP1+5ZQROkIItotXwm\nF6Bc\r\n=CDW5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFnTEOozzpzvIezMv/t5mC7TMP6T5XM0L36VQ/2BuF6UAiEAw8qVg1RonQuqfP8CHEPBjVBNWDoMzpS1SOymjQtDM/k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180530211449_1527715194970_0.5740358346396683"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180530212852":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180530212852","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f8418fe09b1fa5ef5fda9249320dda92895a3481","_id":"@atomist/sdm@0.2.1-editor-sdm.20180530212852","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-h44mHqTmXAvR0uzJeEq/ew3erkuquqhUZ4vhudmLrwnOEzOeyRv0Gtfhf+DtTgl30zsEgOgkCvgN2FsErbXQhQ==","shasum":"621f296c8f00570102f096fd2daee13e2c63ac80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180530212852.tgz","fileCount":944,"unpackedSize":1962318,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbDxjBCRA9TVsSAnZWagAAPBQP/2mMmD9OnWKWxiuXA+Xp\nG1OVHeNfRxslBajPeWfDdH69Z+DohlvZ2NCOy1DjCb4Oc/yzN2fziso3ePEE\nhUt9Lfoy5P8lEvkPS+uagX+EEM9wVR9YdFlRaAjL0J5TJUj/R+JA+kmEfmJC\nz63zce0GS3Z8c1EF8KpfWHgTJ2aaeWYs3LvEDUY05nDTvlEIyVt9EIsfQLPi\n06MATytTkLTQ2zm+TVybmclyqnqfEALKcxyKVd9il5UPGx4yQKyGugd0N52Q\nT08MRwVTeezRUBdXsRRjmfQlx6a/ukhF3tf8eZZ1A85o6KussX6KUqpR4t3x\n2xFruPqmpD+rJqkOLL3HdkeGkvLwyMw++XOjxqAAhzNCtYIyYDA8TFZlzk0u\nqu3wsSaj6SzZbs7QQV+pDPDo/8d7m1c4IVkgmPY2zWXhKCS9BD/hAfJGyMFs\n5ERVNHHTP48kRSarx50OuHbaXKEaH0alsavkq+c8kZQzhpEkH9/5AZ45VGeW\nn6lA3ogjVTCOs0ULa97RcDkgydUzBbNrfnskhDMBedeKHfL3qp2fg9l2UrPu\nRG2o/C+Li7LcpZ6R/pG9yTUFq6sp/mlspWN3EGLHH9ldm4FmW1APKiitDGA5\nHH548vOfcSfMsTzMYZeA4iMm7Y4SkRrwXhr248u5hnK0PxwOC4ly1hFi74I+\n3QN1\r\n=TCgn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLBkvORQODeE5Gl1NEKbvnI5U1ion+zoenqJtNPkh+XgIhAJW0VwLaRJm7dG7FbdLma9yGTGrKf5wTq0ZfptBrK4pQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180530212852_1527716031845_0.2780446011668527"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180531011547":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180531011547","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"99f58fd69766274dafbc661c9771be295cfda990","_id":"@atomist/sdm@0.2.1-editor-sdm.20180531011547","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qEdXgR//rXXH5BKCfJkFRNEVWJWq3gDJTmZYpjDym0iK1iAYS8KawaAY9HjvHyKvW3sayxHgE9kc5FCSaUwyCw==","shasum":"80da84351a597f02d4475cd1f42fe6aaf4cf6b9c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180531011547.tgz","fileCount":944,"unpackedSize":1962543,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD03lCRA9TVsSAnZWagAAp/kP/iYHzVG84rcoqPYUjeiY\n71RSfqRe/n3MkpeZZErazOyaAIUB3txlm/5KByy8n+oZwaH7C1pLOxkc00mf\n8UFefIyq2Rw6OZBjdzkNMA3r9lnvlJO7p9BLUNENjz/UFCJT/TsryXdWaHWp\n4kokJteILmPlXL3FebnAhVetcoFlcgtFwrNtVa0rVNVmkSi8EjUE1v8tkL00\nMo45NgpGCXYpFzFCHem3VZfL4yFPwt7MvTTDkEs5oe25C4lWsViFKFnejZl4\n8PhTOKj9DnogvGRQ+bMquv5w8eN7AE0StDD0/jq2LHvio+WCnNTRsnyQ/WZK\nz1U+6+pvIbTjxFHK6wvZwOjZdYjUwMzkAEvTp31u3eYK6tJB4j2suiEUSEN9\n1KOTs2jNHm3bTBkDT18eJJDr2xOD/qRiVUmoY2Bpp0t+jTIo+ZKX1/4hogeu\nS0G9iQZxcB2wctZixeW32L9rwjCQn7+tmEA9zJX7ShDNdIkY7a048bkzpwkM\nAIUWWK55u5Uu2YRTaBCAb+Vbp8ZWIbG2tgIXr//IsrAmo8DO+UbppCFtIdjQ\nyOxdN0mvzoWfgc5xjuJ5ZuRFMSRFjwcxTuGjEx0rUreIK/3fPxFVmmWTUSTa\nyOU2iPUCroTG5eIYCuS7J6qogfL7slc1R8AWTWgZ5C4HgLhkkQYWshF7w94z\nb97t\r\n=Zetf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHjtUe1F7MUBuu3PXM0HZfOmZ1V9t3FQvDZPhZI0dbjzAiEAj62rhr7weus7yobihH6cXvmrs5AKL3lcYzB8gY62RQE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180531011547_1527729634681_0.8629719877217172"},"_hasShrinkwrap":false},"0.2.1-editor-sdm.20180531024918":{"name":"@atomist/sdm","version":"0.2.1-editor-sdm.20180531024918","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c2fc8cd6e245e1a549dbf1dea7a452f3316e9ec0","_id":"@atomist/sdm@0.2.1-editor-sdm.20180531024918","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-r63j7bwghGaIx6DtLKhwLYA3RCFjL17P05Y2SkIVjBaLzT/d31RdfRu4eH2IIRBG4BpkU2UlJ24e4mNcZjRbmw==","shasum":"4e71f45e286cffb05cf773db2c0f2a3f650861fe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-editor-sdm.20180531024918.tgz","fileCount":947,"unpackedSize":1962935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD2PWCRA9TVsSAnZWagAALF8P/3Ns2yFrUeI884tDagF3\nfT1kMXA8FjLu+JsUgd7gE8fWLcdRD1RuU/DCiw/cuS7TZ3ghwstzeSideSV7\nfhTUSGxeWzo/5cmifvzyHLLOP+Y9NeZPLzK2NPLYvsvIw+ZP3qd/1uTdPXTc\nBV+ccOa67OOrCvb+dSj6i26SeDLBAtD7rU26Ts6iv6b/PATVAJz2eUipyA/I\nhBaxzNw+bkTUf1AQp9dYeog/r30UMP2pNfaNesDZqVkGx4MYYyS98febJ04n\nSpF6A7yV6U7KnSgBEMmpXqEXvC/J5RMXl6fMwKmUyQTbWzweuMbwxLv0HHBP\nKJDuxm7CjLQgEYVdRq6SsUb++Pyk6igjW3PI/88xqccrzaEAJtOwDWA6QC+9\nRwFqKS7iHmAmytRTY7wQQiA2c3zXQJ9XwI5i7LentsTDJWm+XNDvEg1fSwwC\n/3m8mMHn1p05RYi3A7RbT+uNEefwlCdZkO4tRAllZzgjMKhnio1FZmG0hQ+c\njeHGpndUEdkjYQhSyq+XIuAzrBm5NxorImnQKTrvdIdfLDePiR36ZDh7Wws9\nMbRNGwV5wtsDdc765OHDmPNK3FV2NE7EqYoKyqB4Y8VijMSf/2QJ1+PkxuzJ\nG5VqEresilwp8UvJhaflVLLXOhYuTkTGRkeARJLZfOiVxCgbohQ7vuUKYfiH\noKdf\r\n=jwIZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0571IaFoiyHk5XTtTzPoiXUpJ92wsIezD/unJbmPXNAIgQwms88Yqo6Qf0yTJHNDQnaSdFuiFgsJq6r08b6wOMTA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-editor-sdm.20180531024918_1527735253250_0.2091212307974677"},"_hasShrinkwrap":false},"0.2.1-20180531043448":{"name":"@atomist/sdm","version":"0.2.1-20180531043448","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"de1ca07b200a5b4a20801b82d4314123932013e4","_id":"@atomist/sdm@0.2.1-20180531043448","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wU8kl8BJZSyfBK9KXgODJpW7PLIRhr47Htdce/DX9RA/ea02cvGeTJUtUcH4Zw4UnFbBy6Ms9A86ro7pM/02eg==","shasum":"96e91a10747e3d3e4070d55e2d54d2033f149577","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180531043448.tgz","fileCount":947,"unpackedSize":1965436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD3yYCRA9TVsSAnZWagAAL/8QAILZ3eCGlL08ZJekGYZG\nxMmF6reoZPNuncDVXMojtCh6uVRJpFvV1e9zv/5n1wCHFonDswOcYsgmzqbV\nwmbucH0ES8cWw50WKDfihMX202JsS58ADYY7yMAW0mZqXmPu4rFEltstSm+/\n3UJWTATDz2jBXZZjpzBcUp/b2NV9P38hGOWbD8KKkdKwBiGMDEZ7D9hNobza\nsMoFb2cNRyTUWMaD1/uO1nr0Y1tRl+ErfGnph3I9JuAnz+4n3ARLHLszPtZU\nCJ5lxYA2udqJTA9G5CrVtKlwE2ceIIj4Kl/5RrDRfB7fdK+w1tHUjs8sakIN\njjvsRc/sGShBKT6zZ2gfvl0w969eZMflSdKtsW0oT2LJxlGFXdfdR56jpnxT\n2uijuRaVZtQn7r8BqksUAOPqbc4Xe7MJPCFn4XBD18VgTO3RlcXbaeeSJXBm\nsSc8Spg5WEdabkjkJCPB/LfBzPDsP7s7u2M8ZVyoECUv4jcPOoEE6ZGSDvvu\nbR7LLhUidcIbvbwcOd9iD6I/+9lVm77FdmphfFgV4gvzby/rvcv4EZufZNDA\nJhobffbqYKJSizLaUGRyoTf4gPjucumonCqq9dIbhx5VSbz6Wa+TiEGLR5gw\n3VHeHjgDbzRnCcuw9yk3mJqRSHkMdyN24QR5Bw9NVWjDAcTM79xzUw1R3WGY\nE7HS\r\n=6Mig\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFffb98fkT2a5h/OUe91RdA5R41D2zu0obTz9U7Zt8P3AiEArUFmPbEU/5+HU1maysqdktR0Py/4k0wFwP1G0A9GyLU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180531043448_1527741590296_0.9993992348685683"},"_hasShrinkwrap":false},"0.2.1-reorg.20180531061248":{"name":"@atomist/sdm","version":"0.2.1-reorg.20180531061248","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cd7e0121814a34319f6e7ccc5c296c17519a3655","_id":"@atomist/sdm@0.2.1-reorg.20180531061248","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BOKQTYwOuyRoALyAqcUZPV9C/FhWSRg9tRM3a2wrlMcVfkfh7rN9RRHIZau0SoQ/ppyWf+sXlAJOXPzOmWA/cA==","shasum":"7e4a83578e9ed96e578297b8aac40b7f8c38b6a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-reorg.20180531061248.tgz","fileCount":944,"unpackedSize":1961942,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD5ONCRA9TVsSAnZWagAAdJoQAJutRHOkt4niHMV3Xogi\nepILmnK3Yt/0pDrmWmJK/4dRF8SUTxV4Wvf4lK6J9yOXjc9TCWuKjhXlkrNA\nrdug8p8GfLMar0MexNGbzdgCpXE/lfHeHQDlXgQBxHoIhCe/rr5DUx0igF9f\nPXsEFccK1oPZAJ6uGHaH867+K9TpxpMFiIf23ClxmXXQnaPKC5q+Vbs3pGt4\nh6/gCipIWWtGFkXTxBn/HciNeLA2ATNf9FGgk1g97l4CAo5qB3/qgTow7bF1\nTNiOO7LuprCooYEoGrpreqjj21cVJqGCAXDcb24nKv/TiCdTImMAzpwRdhig\n4psw/nFJvA7ccBD7ntdGVXGpLfFzWIW0r/msHVa5jNBV6FiAhX2kCuM7LMus\n3qvpGvrrLatKgkJD2e1rGvq9xjy3jzDMqYsTGnOm/b77khMavQeVfMt+MwFZ\nz6H8Veb35vy8w8KnRGH4FhtEcDk56MEJPJbt7PI479pcIiehClWYFPfwttvt\n4S9jZ4tbBnfsVuvwpvTJNbTc5XgJ3GxdNr7krodMzADIPgDp/pW4Qp4VEsZO\n7mfT9FirUQ0FlWQvl3/zPNtJtqQoteVA9PN2rC82tzb7JIRJ2r018LCZufid\nI/USo+RGlOG9NaHdcWsCNVJn4NjnausuGWZWc/enikx6hk6MAL9hzsWrQBNS\nJFxh\r\n=xzuX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHoR090DFWbUMHV1PK5z2cxNu9df+DB7vDXpIAyy/ej0AiAZ0jVOKA5u+eVcQiC3RAu70UgZawoHVkBTLj1FkELuhw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-reorg.20180531061248_1527747467427_0.4630661984511739"},"_hasShrinkwrap":false},"0.2.1-20180531071516":{"name":"@atomist/sdm","version":"0.2.1-20180531071516","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fb52bfc256092a02fa67641bb3177b19e0b410ba","_id":"@atomist/sdm@0.2.1-20180531071516","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0LeGfI6ETYEERS6onP8m3XNswdIK5NAwWU47aTT0GUWZR9Q7t3i3N2RSaMNOZjNondAH+8eNlhf5EeIoyxAhDQ==","shasum":"3b78917236875d90b805ec7864483c8ac2b4aa73","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180531071516.tgz","fileCount":944,"unpackedSize":1961936,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD6JNCRA9TVsSAnZWagAATgQQAJ5WMuAx/S2D7CtTGSun\nimDu8aUcCGU/P9L2c5ygkvcuQmHun0O0NxIymxsKuydDsxNOdlLFB6XcWax8\nID3jABeOlOuDh2TMNvh8Jw4VbuWfpB7nAyNC1d3k8Zv4HOvAtN5f02XC/eTI\nK5YydTA4TBkqcehjC4v9m0IvYSpAmepq54VZIMY7/o8DAKGonOtiy+W0/ARb\niI2xZm9kuaPFH+0/IfOAkETk7M+UemYxveMVUw8o19Mzgx3IwUUdyPxB7y++\n2/ypsHxv5a4C4J5sXvfBMWeAT0/yKtDVKM74ekdVLaWZh22W/xinFGTtcOND\nkf07kBllcuij0nBOw2z2Fuj30/uE23iqQ7EYkHZNDk+kWjOVoDPscYBSCgyo\nkQXxBzN5dOIc+I2ouLRk8U/2RHnCGg1lsOYrxehg/XP6qmCdvuurT5L0Du7C\nOpkrR8hbvqxw+W+ADd8ZGoN3/fLxGrRtTLk+OWh8YSWRh6mW92CKqbSCTjmm\nvztBGp7Hl8ZBz7T2eWsebst37GfCFnfDmbnftLbfGG1SaNlcgJcDBxCpPZVn\n30a7dD4wuGdg7KU/J14Xo1Xp6STsXfw22qPfziqHMnG8zFRXDzfDIFmB/1XN\nzU5y0FPpOpBOFRzALbsXz8mQqy3/P8YWGqAPaRMhYzSl9KLJme4kiTpMvxC7\nd5xW\r\n=+BgX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4ancemZ7cXCysqPvF7H48WRohcNeY2a2Fjy+tyFvz/gIhAPTOsStoefRQM5W17Iq75AlAKfXlgWIEcacoTvArpswC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180531071516_1527751243640_0.8677417507081169"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531135229":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531135229","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4544794ba6fff1f444575d7f3f69966c4d927301","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531135229","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yywQIzviYD2Kb47UWjw/AqE2QA6bk+cdYtAiCjbY+NxlNrFAeQFBnV4N1ai7G9y9RsatZhHfyhus2TagOFUDAA==","shasum":"483351d71a2509df0e848e80f40afccaabfea502","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531135229.tgz","fileCount":941,"unpackedSize":1961834,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbD/9GCRA9TVsSAnZWagAANXIP/i70q5KVi2ZzhEU73zfd\nhutHiaWUzX2g5VXtz/rhLFQDRw3ORSq8cfJYiCRjbEK3yq34dw9OkV+LjMlQ\nEJqhSE0TzDL4/rc49lo5qdAiqRBUdu3UNzzCy3psBhDUZCrDkHFvXzV/6VKD\n8GIQ2HnFwuSI4AlSQEdARzYnU6Y8varKi8eO2Pj1DhrynuE5QeaGze4bUyeO\n2gexMj95vqHQDnj7WnkVX2L+x+kA9PZ6qMYcOUAy9TzCgPaM9SkDlki4f5wp\nuuirqUhCnojXKWbWA+6UD6PxeBa01jcN4JQXvftb5gIR4QDnzRecWEy6NKeE\n4hAMk0pmGCCGDZRuAbcoyiuwtXo51rktx0Lf4DH1NUjXmpdtfn09Ab3xmMyH\ntd9ZmWbeO1coPX0Xyl2ucAFjGFDEkM/yOAZAlZ++mSmauIJsUnkb8ViKvrH5\n9V/X6zULuKlGFlbdgxYQXm+8xe/QzWZbWjML80teVQuJvohq1cFOulKCIfOP\nqL4NpK4wYtveCar0R04PtnFlqEuUgfoBmX4DQ3pnZPsJ7ZHJv4omXk6Qgm6M\nnvNyCaKriduYomKXSeekOI4IE6FZP4SBnn8+WY75AG34oR8FplyBk1UFUPGq\nNPEsyzMGiFckuX3ElDun1evieVWK8+9Lmo47pn+J9C8KrEHJx+9ZlPCXL4ch\na78O\r\n=n22G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMOt2uUEcPhXJmetQUXom+/6DENa9hiQK5gvbM0h6mLQIgDfCj9+/igrNb4sZ39CKB2kX56WudIDha+d7RH2REmrk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531135229_1527775043373_0.5564271835423735"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531144715":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531144715","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bb2d9327b8d04ebfbe39bac8e0efa8f3e77f0435","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531144715","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DDRQSDWgSpR223Qt+I20PxVx0w4N5VrI5gSOE99u3SEDby8VZSPqUQ1csEge7PxEmgOZ7G0f2JSnXHGxK0f/Iw==","shasum":"f29a5f6c725efc7beb512653b25211ff8e5b8d3c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531144715.tgz","fileCount":941,"unpackedSize":1961868,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEAwWCRA9TVsSAnZWagAAkw8P/imlDOymfJgtjY4vAeN3\n1O07gNFPRwaF2FWujTOLRxb25w0+aMGhb0kszklOMaOgh9y4GQCSLEFRYZXM\nZcgcZlDnuMC9hq5K7fhyRNXR7r5hEXMMfFd1XO8lG/B0XTCXemcMeTdDEFoz\nMpYdeMaOwGchAKN/1P0hC2ok7rwhD34VfKuFKXwBta9nen7uWX3p8/ljIys7\nYyzE4sKm6bxGId1dlDuLlENqq2UceOCSK5WmEUiMcWzPm5RUvPxL8pNRfaoE\nBJnGz5z5qh7GberF+YgJLrWpc64Y4x3wX6rT/ZToUFsH9TR8nJivHJ3FjS+N\nUzizjLMQ9KUtCciIEeNH6+Y6fEx3P998LIZYyP5vwnscYZ/2ANGij86+ZlFs\n1hJQI0pU9lCQPJCMlsAuFICvQOY5jmmA5DML10AnojhDxvQxjFXOfx3c8Uzt\nw7N0tdBe1DdEm2LFGbYxEARZ9neQPW5dfYWb3dVAR7cv6uF8LI4VRbMfY2jb\n8pE1Riig2BN+CsSA10XXW2mOy4Jo/hODeRLOEFY/splvbH1Iwcz5K/UuqIXR\nfIIGsl9t7ZIz+DyJCzhMIv+JBuSyPhMl3A6QQp9AAOkiyskB1mW6b2glglV0\n3l/5f3lbhp2HfS49fXrqDIosgfxjel9EeGtu3/QkOYSNezbC65k6EsdxG8iT\ngS7f\r\n=f12O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDf4BISsZx0wuMH2Mv9ZtIa3HfN0EqL0Y8HkwQXKo96NAiAzGHAvqMOaE5094JPeia0qF0gg8TJI9ktHtIxquyagQw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531144715_1527778323934_0.8987028204929337"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531151230":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531151230","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"aee59ca9772795f0f1c432a19283f10c79f2eede","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531151230","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7MRfafQcr2Ppd2U6l0KbJ/2cLUqUE2oe/A9XB89Ytv0ChMX4pHeuQ3YZvOn+dVrgv0P3gdGIkosvexNtrSzvoA==","shasum":"810bf3899902457a19487a0c8d917a708e58d4cb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531151230.tgz","fileCount":941,"unpackedSize":1961884,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEBIMCRA9TVsSAnZWagAABK8QAKRnDlLFgQPCZSCHUgJP\nAhLdC9+irw6yR7fmRTaAUZbFGHXyoMZQtyIOpOVolD/kY2ZkdSfTSbrukIHM\nmNbaQ5h+WRhbEFc8pFGJFzSShd9dmacVERIisZkn3+h9bjh9+dlJq7OBVGUB\neSD7FccDJiMgMJC7DbavHaK3TVko/p8CcFXzNIQdHjXghiow47hth9ozcVh6\ndFJ3VsLCvARVtpKV1v2DG2yYqbMeWzH6Aq2kjTlQ8PyX/szOR1fkH0ydjOmW\nxYhMhSZmy9fQ6SnU7x2f1lpFllG7apPH5e8NhUmgXjZNe7EikRws1anYx1BW\nPalILTecmJtlED8v8xsmRcga/VPvjUXjWEU4tR2BRn9YZ2zUwmWGp3RNLE+Z\nK03SaD2YkZR2xSzXzCFmBUdRW/ddOPkRTuiFWz3a1YAAScoLC0Yv6J05wE9Y\n2sJkatFrSXYwfGfTPTAiyg9sCMC8cvOw1mFCUTyVwZvd3/UcRdQkxNXyJZFp\np1jSCMdX3s92nIEq/DAUXalSzS7gsBQ+BHhb2KOR4FyvwuTT+jwCl5bW0OOC\nKDGmisP+DbkE2IN22HMHqe47J+uDIF4yp0JZ/ArZqfbnLHEH+4ISRf+hGrKA\nZG9DiqQVVI/vncqZjBBJRNX7JC633bVpM7DbuzU7p26wq1O/pZcQXJ3Rt5Mx\nW2dJ\r\n=SJsT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFPZeexINlHFwaEumE/tEoJ0Nvbirmi+66EUDj3sRnq5AiBj13V3V3GwEODzor3Ih9wUS2W/IOLkKQbVrD/tMBnJeQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531151230_1527779850324_0.08321762662532417"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531152246":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531152246","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fa44871189ec43b4a85fb656e9a76489e276a5bd","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531152246","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pTcI3OLX3Aij9CEGd5SyfD6Ay95xr1rcDrueg+WePb4X9RjsyqD7jYUqQom+brMhj1vmOmm/gPgQ9Zk+Vt2ByQ==","shasum":"faa75c3adaa526e7d63911e2a8577f64756dc62a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531152246.tgz","fileCount":941,"unpackedSize":1961900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEBRiCRA9TVsSAnZWagAAIyYP/3UqNPl08gsvsAJYWfyE\nYnkkIkwNncJbriBRSdXmPZpzlz6rGH1i9bxj5vbLYqXlxHaJH/P66PmAm8KL\nG1ZkTUPJDDULavSJPCkrfVrPmVqoRUIUuTV2luyntX4txryiL/JoPloNyB8W\nNbm3hInmQG4O9sVmM4W+5W2hux2K4cVBACbtCpNenCFC6c29GmOCriMBYDIW\nnQDDcQqQL6qXWs1WTyTFdtQvzgw6zgNVxmIxy2d1Ecr7Zq0JUCvuFwbkNweA\nPX6V9oRycOYM6N27nIoPAkxOlFwAOT02H8qnXpN2OH7MM5s9tuu4wJ0fFzLW\nnPam6rczKF4qwSgW2+OZX0WNdIxjyOMBvyWSlSFQgukBEvx8pB/k0uvcZ3LY\nrtLw3O/0HTtgpKBrt/+JFaJdTlWsema62vQJOz4a0BWIpbPYZuw4FtmpasCN\nEDo9pabKziYtzyrlLYqVmXSskd7bwPzgkBJlMcVrFjWK1jgBX1QBrJ1KKNlV\nNI1NYSReVW6I8yUesIP2sdyZcfM3OKP9INpJb7+XZ20O/Sakt0U9WCXHdBV/\n7FcF8ZdjDLJSxkMblpthBh4QFrlwBIiGHPZpQ4jHXOhJm903YfRI4Lwz/z2V\nfvpgDHixMJ4ahj9/VY4EqJ7oDyahm1OxojAxlue0i0KMRjqG8B47KuHjS720\nVAqv\r\n=JIJ5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHWBCIm84FV45eUygaMvI2OukLZdzVsYpNMrlxbDmoHNAiEAmcPnRFXYWcgZQYRygJIXL1LudGVlZNoZOirLnjoYI0k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531152246_1527780448980_0.12026927802213083"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531154422":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531154422","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8058742e9730773931d85fc40f54ab8be3177075","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531154422","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hjkJeDDb+8Qflp5gEuMN1ahdesLWdpmKwB6itqNYRFXlDSOYDNh3hzeQvx8behbu4Yl/RFGijsKtDdvRxiMt/A==","shasum":"5643e88269e8318949d0dac2a3f3ad839682448e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531154422.tgz","fileCount":941,"unpackedSize":1962120,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEBluCRA9TVsSAnZWagAA07AQAJy/bKkFMfEE5DMjnOti\nCV+oeqkGB+YTl81pH8NUCX0AuyQ/mNQyS986Cvou+1lUglX/7IocVSY1o0gv\n2eL+i/VMDqrzNj15NESVyRO4bv8XovX3KLx0RKHy5Ay1XDU/2cLsb+j2Z00S\nU1/+klXV15g67QeK8yFbveFTkve1Aw71gYoMdFuMayLHg0m5BMGwXTVM1V95\n1J9Tn5IqxpOPa5baptgUeB43SGxwdVgZHjYGLW884EuIcKmU2RZ+w5ItgiJc\nytL4FjtLMQeQ0+IoWdY4e+XijfUL88E6XJ5wOixxEyAWFmqhjzyegRA4Z0Jd\n6TD/yB3zcrnXpltg0OP+gfWuIk33VzLJuiA3lZ4JitP8BEWfdAI1zdfN7N7Y\nujws+DMXJyilczFDrgHXe2xZ/7dloJi5vg1fV25Tum6q2FghyOdeoX0DgUL+\n1LrbUzduVBs3DNDnHfqT/J+jR79s5AYj6WrMqLCwK1qLXd/p+6DITE82dOLX\nblC1zu9afi0QkGoyH+mi5AUNlVYhbuwfQiJJPJ65ogdhFf/ujaoElafNsD/V\nNyp/cOOh69Q4ww0qvJ9tXQP8WieKcHCePvWPTjqO+MladYgE8aQlSjssX9n9\nl1utA1UtKxUeOOgDKk7xoIwVTLQCnrD8HZB2seZvBPiT3O0HFVjJgkqa77f3\nb/NJ\r\n=S7e9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgBX+wg4yXSq3CfwBnQkP8PDwqRcDT933YPqlBwAO8DAIhAKcRa3iJ9ZaObOKWorO1MIsDV/Ir8IOFsJMHtSSty/JO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531154422_1527781740771_0.5403971750368728"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180531155225":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180531155225","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ac430d667634ba6224581766062cc5837f77657","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180531155225","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WJb4vklkRqOzN5mG2La1rIu84SD2FDIRXQY8XbXlIEymPEprloQVYabA2JUI9B5O7L+y59o0VgOsFg5AO7WO6Q==","shasum":"143c3f101303ae6cc2fec3d8aa6ceec86de6a0e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180531155225.tgz","fileCount":941,"unpackedSize":1962165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEBtWCRA9TVsSAnZWagAAHHcP/2lL3MT9WeglBQ4q8jp3\nCHlTzzODqBVC11PCXmXKqJ7Kj9gm6kaDJmignLziNFdztcsYjgE2n/vEu1nT\nimcnk5ePCixQRxb4Hpdb0EiuqQ0JB16FVVJcQ/PjWDROKS9iDFTrCLxFopEa\nJ8/g0OzUCAF/0LeWDMjz89lX+7nXHc5WcIxpu4qWOCljJnsQwM8ZdycHMyzy\nUq3Mu4864g80PmubqpWbXvavpUgEIn0eXIltefROsyxULuyWCYT/jYdBmqRg\nPkLcH6VBGJmzKoCQwF40VU3kJ+pQyqROogwo91gPB5rAYPIITFmM09oBm4Rw\n7ra6Ooixi1XIkydSED5SgEXUwLDRE2aovaPFXfvKdwZIPE9Ama17MT8Qg8ex\nV1Iaub+M4Fm3doU66BVIY9NO+nm18PnFDxqP/dOHG/P8b8E3SG2d90Vd7Ka0\nkjDTfeBrfc3a5y11nrLOxzuOd8wdlGa6QiEx88yUaXSmKyHgfeeaK4BV1cZw\ncwOg2VO6S/w1GAflnvx8Jb4ZMhoqnE6eCpnxWtC8dGJNiu4vVGeilOyEBJrP\nTYkJwz1JjuXjY/YFh6Q3/z9nmDK8lSsfiMWscM/7gEvOBh/8MWvrWA81mG8T\n2JJAZoHcfxCaXhIwo4P62Bnn1mBQZRN5AW6QziXYc9jVv6QYw7gOglvEc5Nn\nrwIl\r\n=QH35\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/wC0cYmZhd74GZ0Hanig7tIuYJQ24w29fQZ5WRV3AZgIhAMu0EoeQzo5vSyWzKNHSOs+D3Sp7zR9AZ0QaaKYcM8B0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180531155225_1527782228342_0.35597089389784786"},"_hasShrinkwrap":false},"0.2.1-20180531233725":{"name":"@atomist/sdm","version":"0.2.1-20180531233725","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"572c304a874e083920073e0e8bd6bfe39af3aa90","_id":"@atomist/sdm@0.2.1-20180531233725","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-v8CFZ2EmxbUht7DgyMnK7ul3Q9w4KewtRqF3UcpSuwXa0payXXPZqoB8xICJ2RTE5v85tw2uNPwusxqgti7mUA==","shasum":"7129febf4fb1e233c734616e813cd54096e2ec77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180531233725.tgz","fileCount":944,"unpackedSize":1961150,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEIhcCRA9TVsSAnZWagAAsJsP/2CC9+3xkQR/SVBaY0Rv\nwWh2G6GsULqUJiypU3zFM2cefPear2FVTxpzJXWouO9yqwbSfdKsgtbWQ0yw\n1kbHc4QUM+VqgpDMJLQCkUHcRPp+qs3YkRtL65NVIJ07RMHkcNzvfCLW7um3\nn/lC/z/jZiahp1b33bTE3js3lZtL2xIKCrT2uBwNEqfL3k/s6YGeb+cXR76Q\nVV7QKoWjxrJQHpHBToXiL1upjS9Wm7V2YuIcizuVjbvvSDTKX/BNgg5dGjTZ\nNFLM2LnGMTBKsI7vleDGk9LHvEKPOZ3BYDdZHxpUrqTvORJtZO+1iv2c6eBh\nYFIqVTsvBQuIEcy3SLX8zfQWTpgVZYVW3xwnJ50l0JqenMchEXHC6nJYsexW\nA382Y8mF+egDqPF+4d6aXBe07loVtLtOIYCyCODI31l7EN0dODTCL3xAFp/3\nRvHzNz2HO+tB7OFcz4zKbLMroJaRN2oGRSTUwPP1Kg2e85DhxyvvedQcMpc+\nEJkT5He4EXXKfq/zMUaVpJurywPfvoknr+/5hXpDLC4kVZy+nfQlkCy6Jf0d\n4B2vx9St7IGnppvhWioBj1NE5MfHgb88Kq9fhf0HDGakntj7W3O311OxabBY\ncORH3kfxEtypwpBdF9FU99BChxMEn75uxcMwbnwMbl6AzySaibUz50D83zkg\ndGlQ\r\n=ycVs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAhBgIdZ8oOFUdikyw7WttrwMgPan3ple0QzfJCrk5PIAiEA3yX4/sxqe0oLnc+NC+DXjfj4CndNCcQsfJCPGdbBWZM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180531233725_1527810138958_0.5496145390197351"},"_hasShrinkwrap":false},"0.2.1-20180601001211":{"name":"@atomist/sdm","version":"0.2.1-20180601001211","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"25c34f5aa214ca8e825dfa3ebd2e2d46db40832c","_id":"@atomist/sdm@0.2.1-20180601001211","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3LnPRPqvCTgNO6EGty4fTukI2lT+8V20ltdGI99n68WJyiBbtpPLES13xasWxkBh/NioO8FgpGm9M0G282MWWg==","shasum":"07eb06a566dd2f7dcefed6422532d63b4f03cd11","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601001211.tgz","fileCount":950,"unpackedSize":1965571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEJCFCRA9TVsSAnZWagAAAmMP/0oqjl2XdIygVMqt4hYU\noj5UeuM3rUilHbAr/RSCP6+xCwA8mUbPk1pMRv0rJv9pD/5RWLVDFTWzd6lm\nVfPYLwFf3VAhR5xVVK1O90Q7lPmZAePDTnItJRsYc38Y/Wdn5t6Eo2asY5qD\nGe4+EBHSkab/OZranUcj2A2tJwuc0N6/b8UtokL5FiENlrtc+glCSTwp1Q8C\nYR36OHMpQzY6R140hU5uh6ZRz1T9YLDRiQKv668lpXoRMfumUNSqw//4/nBb\nCK1vC3dr5Bk8/HDyw1hLo0MuN+/cEkUjcpHqo0meY5dabmUe0Fp6EVVeetuS\n0uBA4OdQp8mbZWwIBcZSyhSR55EfrZxIYHRaiI2JV6Ufuq44gC5SwEFl4n/M\nMGUb2z4r353i+v6VyFHOqjKhjw+/DnUf1lBYvtPjX1ou4Y0jIXhCZEO8XPF8\n6h2TfSaPrfJbo3AloJQQ7YP4YxbvpP5ejqSsRo4D8E2SGJWoXusrxiGkzPmt\nNTUTypOxmN3qIYFy1ipMiOKCIpTEAgQqjJolGk2oP9ZCp63M+vFeW5U0EZXy\nW7QugGLfLOgVRbL7REPrfPFhYu1wAUypZf76p30HwrH2oxO0n0XJP7WRPpB+\nR77Kaiw3K3Mr4R95sOOQrNZHBnxc4iR2ESXsbbg50WrLSwgQEx4KxqQFO2IG\n/ZBq\r\n=WTD1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIETX3cduyVLK03RA3ihbNmUlSVRYPLO+s8pyVzSM5g6DAiA7LoUhalqKy7lhL0ezQ58AEacCz+quhZCpxlrsMAPZnQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601001211_1527812227745_0.9568023796655072"},"_hasShrinkwrap":false},"0.2.1-20180601004700":{"name":"@atomist/sdm","version":"0.2.1-20180601004700","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0b239f7bc603b026afda16e5a37908cfddd70ee8","_id":"@atomist/sdm@0.2.1-20180601004700","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s1Og4n1DW5DNqTfdWwgrvUpxNQCmtWFx3CxCaHCtB4L2AQTA++48ndEE48f1gH5Hvi23vx5t8vRUqPqzPyAhwQ==","shasum":"90d237484e6efa04659ff942b1f5d59448b952fe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601004700.tgz","fileCount":950,"unpackedSize":1965614,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEJiYCRA9TVsSAnZWagAARTkP+QHtGmmpVcjlZpAadWeh\nwjfUDy0CPbqlq5BJy+WqM93IOuBe6Js7K+GdiAbRW2SIslUjcjrkX1i0KSE0\nWPFaHx8CKyKYuXT4UuSLtcIODA2vT8w4SfpyzEoQh0Ko1G9NSZ4dIzsrwrAY\nr0/anbDo2kdID9vV40HHOuthQLuJ8I/Q9Bk4zLt5i9Xge1YZMti3YKlZpBUi\ng0UNCGg+ItVV8wu6v/oUhFLlgFnCPMLrA4fvb8gQfFtQDHZNzelnGXqOU5Se\nsSOfu5O6x2UbRBqI7V6luX+MPjEuKm4Fpr+RDhTMNQjnQJAM5cZPBtNrD7Wh\nTpA0MzckKb2bYAkObptCasWKbm/kj1y4htMxP34PXLHrBkP9oOjYDTvqxwuN\nTK0gvdnvxeGMdRmTImPTkfjMVnfM/IFWb28jdmDo393cneIuVSeuhsb/lw9M\nyCtlbAxhOXnXD9nlzNc4jQJtMKvQdMzKfjIiO3qHcq+Nveh98bQ8U8rl2MyT\nxFiselngOwH9BZc8P50lAOGUcn1OHR3ZYQNr8qZ0lNvvedeU1vhDvSzITpyu\nWVa0aufYbGioX5RoXfNWTpWh0sEpM9EPdriH2M1RdkDeQbYZ9jP7ytitiYTe\nXvS4SF2sQ/fuZ0bXZ5LJboZgV6phURE33vblQj4dZU/fVI48TS8c3m06cgyU\nhiGn\r\n=siCF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEBtzcokxrSWZ+gHmfAoyZznaY4NB4Ez77izwILkYNZeAiEAqgT3LQHBi44F/N2PArb0fm6wEbET0YLRk55rDYG60kI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601004700_1527814294046_0.806694202401161"},"_hasShrinkwrap":false},"0.2.1-20180601012851":{"name":"@atomist/sdm","version":"0.2.1-20180601012851","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1c08a85ade4414be8428dec6942f43e848192221","_id":"@atomist/sdm@0.2.1-20180601012851","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-weptCJ2qXfjRdOz4QJesJkiJrNpNVOkJeS+Jk0HENy+Q1VaO6wo0JrScf2sJ4bRqtgVwiO1Iw3NaFga3i8mQfA==","shasum":"05d64a919020898a31dc76c29eccc8244aeae5ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601012851.tgz","fileCount":953,"unpackedSize":1967861,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEKJ3CRA9TVsSAnZWagAAaJwP/0eiyI8Ivgaddw5dHp/O\ngaC5YUw2Y9dQ0a56gwEp93jERWJQjFZeOgeKlTsjOPJDjhk1R4hSpZbLBVIv\n7gFDGr2OZJ0BP2AOTH4bbs/1WFf9Bilmi5HTdCntfFm7wwBY9LDMbHgEp+v7\nmPIxL5glMpXg5L9DRfeSMzCe1BPTzX4kC8XWk7hPwGo7xhQA0L9v036n24Ys\nE/ICaXV+wMAcOfp6e0r2Wmh6pwlUTgb54/8Ulg6VlOsS2aF6NygGYjNlQe3D\n8m9NhjR28lWAqLWJgTnue/vGJgOo0Txqy/5QW4rZseebP1yciOgqCgWErr5w\nQuxzj+TXe+Dy9oSDP2jMAobsuJHxQjv5n1kepH48GBs0L0BlS6zLgEr6UZE7\nVr/dQck3Xp6Px2yk5W91AMx2CKon5Gl9/C5N9pr+m/JbR5upKw3MWkESEyMB\nxYCNllClJPH4NBSRlfRgXEXe6ui2CWu1DRbt9z4Dbaty63VvM8OhMXBHg3sx\nR4MAZU0evzvRzPQ9vHcNcq3c/PePPjWtk9PBfgppBKNj5skdz5AlYhgfY0eM\nXmLsnF3hE+ABRYe5o/CYb6aiYk0JFS5jcbhExBD2izi81FKAK+TQkOklkiRZ\nWVrNgdtr7EiU4cHy/veA1NbKCy44NYtWH3kTQYP+/mbChezpiFSPs2Q/zbku\nb2DF\r\n=BW92\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPE6ERRFf3seMhpbfO+ouP5v/Crf4vmeaeiw0t/2yH+gIgLYvw/aNg9qpgkfBOhYedLSVwiE0tyaBtngo8TY5JlRI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601012851_1527816820728_0.13793980705206854"},"_hasShrinkwrap":false},"0.2.1-20180601014418":{"name":"@atomist/sdm","version":"0.2.1-20180601014418","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"849937c2f511b253dd67c8265b3871ee545849b5","_id":"@atomist/sdm@0.2.1-20180601014418","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-52A1Eego/ID7y3MMwIkZEATsFJEJmvxvYlH/VrVT9rgZ2eitGd+wsao7qN6PAkQlYX1bdIztPmvaOml3o8UlwA==","shasum":"b22dbc9f4ba842c1a32f52a7428e07b7a63e07a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601014418.tgz","fileCount":953,"unpackedSize":1968193,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEKYUCRA9TVsSAnZWagAAU9sQAIN9c0YLPuvLl6L9nWkm\nRN2/KTQ0M9dlhE3p0+hpj2ezHUA8Q0YZPkYsgxSMKmSB9GtdmX/LEdOrV+4H\n8mHYY0kP5zYBBFFepl7HWbnLcIuJKfKzTsIJHcMTiPCVyBE2OwP/2z+2BpMt\ngcR0u27GSXFlji/1pKwqG21V0SQf1hrlRo2c7sbZYl7vwvO0ocZXByKmxdb5\nvNPFAtMpue3LEk8xSGPML4v2gTTkqLfgonDmIV99psmtDVftHmmOYn5iYm7K\nFo4/E1wRMbhAa95fX3q/9i0jXzOFlHmMAum6RY8e2vA2me9IutPnJiUYhzPE\nD1S3KosifnhQtp4my9sOm3d1WGi/2am4c3DlL095sImcsQbmT4HfrMg06teH\nUxbZZ6TMvEoczfxky8MVtWGNBhysMED3avlrDoFEk/NawemXsMeIMnDZKNvD\nM4vf0BW8fNh3tMQq8HHqfHakM5lN/OxxDdzo0keOijOwFvmwWZ0XcTjHsOqM\nu/biQ4MqSB4rrlzo32onONx0o5Taf7wkl9yN4G5HhGx+RDN29oy58nV/SZ41\nrgDHSiKRQeJQSCfClMgMZyhIou5Fp13OCft4iGZd2U0kYF/KW+zZJpYNCLTq\nXCoGEpnQITVD+oo3b0IVxddNRONo2oaEAbCwCKLS6KbSaks/F7CF0P+ziCbU\nbB+q\r\n=1jPr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9oU/vg5JAChUA4K6hYBPmQWPdDJTufzyOPW35TFKCzQIgGZzpy0KEZTUSCo99KcZY71yAgBoCAEITAAC5brGQr0w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601014418_1527817746917_0.8622079632859656"},"_hasShrinkwrap":false},"0.2.1-20180531025539":{"name":"@atomist/sdm","version":"0.2.1-20180531025539","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3ef6ea9b5eb60d059edbe5f9c2e2e055682c38af","_id":"@atomist/sdm@0.2.1-20180531025539","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gvNmtvjNqHQ2TnboCmGxHI4nw9koAZlCMsvpdW9fmnLRVcyfEm/G5QTXeCcZvpuviB3n8Ge2lJUmtlaAJKYxWg==","shasum":"8ca6ca914a81e8f66de161b8cccba7b88b793473","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180531025539.tgz","fileCount":947,"unpackedSize":1962924,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEMS+CRA9TVsSAnZWagAAz8wQAKFm0WAhN7miqpbv1fnI\nE7vgsgE5eHLpeFJKjS7ruyNEQXT6MyRob8fgHFQ7Diq31t/Q0x+y0Z4Rtwub\njotiQYbxfEin8AJZYDjjXUfi45RT7PFcCwreUETpp+FujGUzy8cNhenjNHWn\nOYb2ORT4fJxTLfatj/7pPyCF4eYYqg339LN6lJ0NjMo8fU4mdzdYYai61cjv\ngxRzuYeUnT3AQ3vWF+pFNCGsGrfFqcOsE+3nVumqTC6weSsFO8Ogv1Ezjsem\nsGDq/6e7T04n3+i6KMHAQfPQnXsoJMVsBPi0RHSVzFMPTwz8+1j4eIC8ytba\nhwyS/YJTgaeKrtNBNIfIfGnzQyCFoRO02LuwCcZAEXPwCrJsdf1bc41cywqO\nUHKyshh6OVMld9VdXf4qj6QGUmSy2BXY8A1k8GUxRIs5PdRCRAqcKYI0sO3u\nK8tV2sjcNK199cxtZNApmf2BCQz/sTUvf9zy2/jfNUPEDHvHtKHTJOSk/8jD\n/mg95UOcGzYgg9In77zosKN2QBruPBeucqpV84cOd9p5+3H4bEDromrU6+pf\nXOV1DxyjfJHvEfa7ND3NRF/RB7V1tZ8aUjg7fSU1CjBwbjxIjPIWgtV09z3i\nMbs0ko/6Ouyl42H7YoXYq961jEBdo7tcTg0OGv60rChA7j1/aU2eZaTOxJp1\n5n5u\r\n=9lLm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBcqqrVtC8JpDjVP/qU40BFW45msT1KinkYUp0dHmg8vAiEAzUFPI8kDLkwCeVxmqn1QTPsDR4ZjyytvS5QCQsE0Ohc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180531025539_1527825596344_0.9467315532788172"},"_hasShrinkwrap":false},"0.2.1-command-context.20180601053941":{"name":"@atomist/sdm","version":"0.2.1-command-context.20180601053941","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5e176495d6eaa82304bf92f59ddd39ba6c36f11a","_id":"@atomist/sdm@0.2.1-command-context.20180601053941","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-b8v/stso/8e8JtAujy9NB3NeGVEvWe4Q+ky9yxZCC84w67QORwundQcuMdchfKxCReNFywQnOLayoG6SiLTezg==","shasum":"e89afa7d14d3d46b4fe54ddf168b28f95740a011","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-command-context.20180601053941.tgz","fileCount":953,"unpackedSize":1970483,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEN06CRA9TVsSAnZWagAAgBIP/Ag2ntRbnaxnKxygDfV+\nhmWukOFUATDr2Fd1+Spch8P0qiIxk84X620XhF4jQklTbJcj7KhNxUHnwvrh\nBL8LICO1eu0XZHX+OcRjC3s5oVNrIovJa5zHidXlALPZtO+PrWhmhCmKJl26\nxj00TaMHfOtfIIJrYvcLH0HwlU2CLJiMNRemAahUiRxbnc9bnfMi76K+89ut\n6vUuIGygYXT8LKFG+cBsEHD+Zumq1HiqqIOyLXdwD+OXsRMOrAvDiFFiJabo\nSsgUSj0zWrp7MVJkyU3hFd0HwN3rhHzpatsnXb1yNO9FOufdMgTwOATjnL7B\nF82ROLj4Y4yCFe9qqPrJCQIPJzot9QaY5cuon12aWg96BFFuP7jQuiu4m2Ww\nRuvIs28eJoo41oJ057JivpzTjuOCyXXBG/1dfEMbWjwN7axClcFyloU4rp0R\nDv3foJSWz1T/rcsURifshEKQSEj+7w58BK60q5BR6QGyAEICWLSVCxzSNREq\nDIVvB77z6okeDLkPSPDsVIZ+Y3QctAq3jU0cMtaaxFxeW2zXxM+TJI5JkNGY\neFh/tx8SELQRTWhD3bnvOg6bR19zS9eQfmHfnLO0+VN06CpZa3G/9WqXX5Ln\nQR+Y7CQU9ydTvGOHFFCFsJ8aSjnkv/g/9zGilac0qUhLPhG5Z2RvD1pSOADF\n/fbr\r\n=XiFS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGTxHJFoqaBahoKH2VQcKf0dh9kvMaFN0t7muOE9JYEmAiEAw2QY0suYX5peezGKDr2fyDdeZAln6GI5BUU8jbbEeCM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-command-context.20180601053941_1527831863657_0.14254872317398792"},"_hasShrinkwrap":false},"0.2.1-command-context.20180601055341":{"name":"@atomist/sdm","version":"0.2.1-command-context.20180601055341","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d73d2997532a707824010d9559c7e232dae9e628","_id":"@atomist/sdm@0.2.1-command-context.20180601055341","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vmErGTjcvEO+OXOSQirB8/2vAP3OhHf+Ai1xQSOWFRnjE71zWWvyLCEh2I4hS7yUL0AMBy6JZSCDSIGelvDKHQ==","shasum":"412b8775896a579f2927a2e21b37554976dbacac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-command-context.20180601055341.tgz","fileCount":953,"unpackedSize":1970624,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEOB9CRA9TVsSAnZWagAAvCIP/0EmsS+bcTm6R3lmQp/d\n8mlNhp3PDZySw6VDW0wonTNdjGtdEZsE4lYm6nsuhOhpQTWTEt2rI5K8Su6K\nd5635WS6kK4MxPyNv6lK4dsk3JyF69GKo7Mz0g9+ZFFch2jmHWj/BxFJYzXZ\nfxPU5aqgPA2uPa3VHCDV2/Tk3R9wPn9cnUtaOF3z/x5ANSTLQj78vya53PFu\n0G4c7gjnjUoVIDsjlqRAp3k6AbOhpFnBh+jmdzu2YMxxzpy7oqXsnvmEE4L3\nUG2yl9eCWbG9+3K9M9yvdzXTUBhrFcJ/0qtWx8B9jd2Irxs22wTa34ejpzBR\nvgXwysZiwjdDL8Ce/6aBjjUtp3HSOs9o8LooQL66PTqcVkU9CArP87ng88Vp\nhJtvU7JlMivoXa4TPEspxSmvN3KjMzw3UEdqA0Db6P971+6PGCCWmiM1mah3\n44BOgW+tyOE5ExCkJxne9wbw5VL1HckjYwjW/SHylBrgYUmRCuvQ5596xs/f\nUPnHLKfiCMrdwVOUCqRvPb0CowER8xOKRD7ON6qfwLBBFMtvsJ6p4yl+TlH9\ntCQsb6M/6g64kzPPoAIAW1HPk61GD6PuW5PN9HdIf76CB5DgMfpFmeM+RgAf\nooD6QHDytcDuCO1exrtED4KeTR/4PmNjzkUOYjmpAmVwSPpMykNWxU9xhVOk\nYGGA\r\n=3P80\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH+ZsNLsTteTS82z3wOJV2b6YTwHZkOF7FysaDe1zZ0FAiA0dw/oCMP2XfMADuPzQqIgqc0C4jLSVWik1b3Yyr46eg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-command-context.20180601055341_1527832699951_0.5844194357334576"},"_hasShrinkwrap":false},"0.2.1-command-context.20180601061921":{"name":"@atomist/sdm","version":"0.2.1-command-context.20180601061921","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a88e4aa3278b3a85ab23fd56291039facd761c3c","_id":"@atomist/sdm@0.2.1-command-context.20180601061921","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LPO17+vydXB84MqJpb3pie3CqmuI3Z913D0vda09xQhyyYGkL/2MmJsI+zppQrHPOWiJKC7ZN8EJT/tRE4MnwA==","shasum":"0e62196b203a1ade365495984431fcd8adfbada6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-command-context.20180601061921.tgz","fileCount":953,"unpackedSize":1968691,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEOZ8CRA9TVsSAnZWagAAGoMP/Rp92Dx3esHO11CMBdOf\noMTlrlmOFCfFKXC1qJnHD5HiFCJ5M7FPLVDP9DK9N8EKwZscdukrvzyUBP/k\nL8sRoEXEN5tXQ7XWSlYK8h5Z6Oimg7LCGhSHut3rg3y1esyayvcQrtL3r4j2\nxo3S6FhfEwMW8JWV9Bnik2w6A2oeoXI06WuwQ84Rl66y77VdfB2MhK+kBEQ2\nhVdyasfi41zcHE1ODFwe8n5ChSjZb6Tr//pVRIZp9sTYu6adQ1xmR9aU/c8q\nHLqszMImC8cT+KI0nDeSm4IRGDjwoIkvpka1s6EdsI8n170tMDHAhFhdGJEx\nIbLjN2itciAt+bhCFaifYENCVts7In8P6Xwmu9RCVx+DNDWq474rBMWiDvxH\neFL3C1GbVCF4NC6MxbOee7T6GvIeKhCaGiaZCVJEVaqyzkmdqNlgXreXK8Ta\nX6QylJRdOqDLMbVDKlkDdiHf1QFZbSGWF9z6bVpO4eR4fg57fXIC1uizBNaQ\ndsC8OyB6SCJAmonnnpG61eaBOvnmPldwKHfLeQDYYTOQRFrDdEFNC0ghxSEs\nBjSzij9V2WG8DtvTXrY8opa+RXvxJniMNp2Zt0ijuFkUu2lMxteclrXSUHvs\nMeBJjWabV7wjnmTbR0SfYH3qVivOsONAACeY6/90R0zgcnmmQqjO6dAnFgYa\nVhkq\r\n=cnQx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCu+t+Fq+gSEtJS84mS4dvm0UN4dtCZiQ1y1RevyxBvawIhAIznqzCZmZG6839uL+j9Q29Fi97+bB4QPObTN80hzwPe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-command-context.20180601061921_1527834234701_0.26742816443718787"},"_hasShrinkwrap":false},"0.2.1-command-context.20180601083159":{"name":"@atomist/sdm","version":"0.2.1-command-context.20180601083159","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d023e8aeddf4ffb36e896f4ecb11c86cd282e112","_id":"@atomist/sdm@0.2.1-command-context.20180601083159","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sD/RKJTkaqNT2HXrNo53zsNb0pWlnbLDBNkPMMny1QevmncvYMEDH+LcHE7CXkar9JoIvE1miPOPW0Z88nTf+Q==","shasum":"770eef20ffd6a0f6f19e0d18357da6b143b254e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-command-context.20180601083159.tgz","fileCount":953,"unpackedSize":1968964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEQWhCRA9TVsSAnZWagAAq/UP/iyLFxyZFeSf2k0Pyn4a\nxmhJTSzGtOLAOJlFTiBQooKBQAW/+agRGC50aw+T88vrF9Pf+BLdN3h+hviX\n2mHer8/RV6OA2qbR4y7rUuoJKiTukAj0UfAtejCEqF1bPCuIT+PCCHdVRV7n\nYuOrywF/9iNpW52L37HwffGJ/dQgcr4nnjvdMZj8yalw0b68RKTRdfz9gukI\nOquqGYPuynvtt6mfm8cDFbgzXnf9Z4ezau3FE8TWBhVUtPU4OtSvZy2as1VY\n9/nUeutN9xag6kD4Tu8q7Ub9wNPHhbXaCcy3c2/Nf9tOc69PF5pHdGNGYyfi\n/qVHoqSiWTXPWAylNzncWNKoyazFiXK+zyt31yn0/FwiqCcoiYipIQWhMxJl\nNIdPMgCEbzIZkEpbPVVmEGq9SAo/NazT5ckbTBYL0EDBPog7XTVy/5OBBKd9\nqt4HKkRzmdiVON84S735rq1H+QDRHO9vCbB55ZvHFLIgLkV3+98i4yyTtDp+\nEMNuUp6sZSnvKm1jNmsj5WNz2RdmPktlWRgtC71lwvUayrZF0os760Mx8U59\nE4vwYMOz/ii6A1/OU2Ri9yDATVy8YNdZQ9MT05AzvdMcIGzNKnx3c4yhflur\nfSQosqWZf8RvC4o8B5pC5znxMeGfdScENL1HnNHrmFIDIdQm89QvAz+2rxdf\n4Fvz\r\n=Cf0B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEs2uLmPbfy5f5TO5JD2ocS/sFGz3b7alAD7mUjyX5BXAiA+S2xE2jSJ5Za9WDxWyOcwMDctAR0uEe4JEMt9SG3I1A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-command-context.20180601083159_1527842207611_0.5244876495728497"},"_hasShrinkwrap":false},"0.2.1-20180601083910":{"name":"@atomist/sdm","version":"0.2.1-20180601083910","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"81c3c0dce424ddce13139efafe0cd86ddbcd139b","_id":"@atomist/sdm@0.2.1-20180601083910","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JAsjf4fRQDPsElCi6ayy4GQKDFT96SsfoUizDL/NVx5TrrtB2pZC+tLh6FhA4pxRp4TMN5s0FnasJ0F1NImtJA==","shasum":"6c8432e7352aa14bbbbccf771d543a79c95bbec6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601083910.tgz","fileCount":953,"unpackedSize":1968948,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEQdFCRA9TVsSAnZWagAA18MP/3C16hwvScDBheq85AwP\nMaMg+7nz91an/ZD3DJnp+uFvNe9wYSFtsI2MP/x5+ZcjuqMBT149W8JGuX1/\nus8/MKL1OHLVVNWo/AqwWp/meXxJrqW6mEJEms7LDwEEMEGl2v9I6VNT23tq\nUls9tA8DUS+r1qabi8kA7+m5K2xtfENiT58NzrYArb0zpNxqHWTlxLKcb7jX\nBh+wgBdffIeO87KmueCNw9nSxIvw4wu6iFjvzMMQqrxgWBefINcJL97yub2D\nB1nuhexvvPc0g5Yx2YjnmVVPHOzasuhtPjyP5Twz563DmJSAyuxrJ7Ui63hZ\nvYI5ml89XXLFjjIX72mQ4NYSwMZb+PhbrVIvD0GY2bWvk0Sr79vzO75+myeL\nes6EgYI51srWB/CaanNQqJ2aOdfwkq0uCgTvdhA0BzNsYvoN5Gxf6BD78yT3\nGKqh2KnoRtmNh5MF7Yt7Ul1GO8DFXsxd76OjOOfU9Oisw1wDrGg7EeK+0buf\nqYG+UbGRVE8IT0hpLli3STwj0kOBa4oNXLOB7F4LruO02xdO8PzVTOxJUnAj\nPPuTN7yD/GOw79O+9xUjr6dx1GwL1/anyURNAK4GVVH/J2HdC64/5f/LGcwD\ni466P6tksB+T4MBJ7fOC28S3vuYzywdxOlmJVNyxnQ6g+r111kGlCX4GB7IC\nNj3+\r\n=fMbi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICOUjqZUZVD40UYdNKKyHZHRzzEjNcfgJzXCRtfO9tOOAiBhPAU+k61Y7bH0jbRh8XLMzjziA1JkTU9hlEO6DdAViw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601083910_1527842627409_0.6595331848881487"},"_hasShrinkwrap":false},"0.2.1-config-options-rework.20180601123701":{"name":"@atomist/sdm","version":"0.2.1-config-options-rework.20180601123701","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b1da2c69e5553ae14c82efd0b4dffecd38544791","_id":"@atomist/sdm@0.2.1-config-options-rework.20180601123701","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LrpfDXykxEdbQ2ySQCuU85gO9jJ9TJvQ2LSlEdzbeUNHggT06YdG7tIHP/udV1p+EF76BXnHx5MxQScGiXQxXw==","shasum":"1b0228e7555ac427ad336e29b15c88d63b93b239","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-config-options-rework.20180601123701.tgz","fileCount":953,"unpackedSize":1971582,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbET8dCRA9TVsSAnZWagAATEMP/08fjHJrkYSvVBKF3W9A\nTlU1HixkKEmrqyhmDwY5zdDCXxC6Rez33/HB1a6Hilz0O66p9JCFNKFU9n0V\nJt4q+B0P4va5p8Z9G0JvNUepBH3+GL9Xjz0mf0ZcQN44jSYTK1ykBjMeBBbK\nqUh4KbSbQMZ6MjNCgQJuvHGtGd9SyLgPlPoXkMufpuCJbaJHOWQZ7KHJzw6E\nG8o7qDnICv6hs3LyJrj2GzWnpYyUI46dLdp38+1n3Um3lNARPVo7opKSA5YT\n+lcklgTS3mhBmZ7zXZR1hQcBuC+rM7FqgpdT+okWc21WZ8fW+8aR+0LDE4S8\nZk/wpkIF9GPHOkxGcZYDKj2II5Gb3SlC0CFj4iVbhf3GrgSJFFAg6Z/NyhsV\nZvPVySox7ofiQpiKC1EupLf/+55ACO8scQY/rLHxxAqVJ9YeDWESVzTSuaUA\nsIJ5WjQeH1aUIURTdQNZB9kIUL8Bq+kRKNu1rXMMV4ot4PCbV/XXcunmoLko\nbGPtUWi5vI9NbTYmZAmwYoCrM3iTZswvP0XDN1yWlYr6TT/f73EgnxxlL3we\nnK1LaQqBWX5xBUPxABWduo5EbgCz8k8UK1DHVRLHRA2qrhRYXhutsPC5RGPg\nwhGp4P43NPPDw2vnte+a0a5J/vojElxNS+y4OYC3DAg/hswFyU3OYZTTKNPu\n2xZt\r\n=mq7x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnsVeLGKORJHlpGvU5SpRQkVky7KXSaRqse49+gO1P9wIhAKR8GhAivydjP89xNGbIAr0uEnnf+kLYdsHJIWUl6hCu"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-config-options-rework.20180601123701_1527856923336_0.825656267301041"},"_hasShrinkwrap":false},"0.2.1-config-options-rework.20180601140919":{"name":"@atomist/sdm","version":"0.2.1-config-options-rework.20180601140919","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e1aecb4785898c5da5081ee9c4378e135ad461bd","_id":"@atomist/sdm@0.2.1-config-options-rework.20180601140919","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FawPJXkWvCcfZkCYz4Rco9SSsxCrpeVhUOR307GUZ8RzZepPSsKdLyBya0mCZZMIfpfQPbTKB48EBvwSmtrHEQ==","shasum":"174feda6bb571073422d2951a58f7b476e68f069","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-config-options-rework.20180601140919.tgz","fileCount":953,"unpackedSize":1972542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEVTLCRA9TVsSAnZWagAAv7YP/Rw6fW5Bidtbz+6ZVVD0\n7b9Wzt8uyhCK0tQGfUmRpVFBIf88Ops/3GubPsTgR0SRygR5nnYlTKn08nCf\nD1JpRtcSr4vVGwWyxl9eSFPfbbQDkYRE4twJBXq/rvt68ouT+8wwUnzO6raB\nYsAN8iKi22oWu/qWfUHILxL1tQ+bTQyHDPRjGNL/l06PNUC8PJVAUizcc0Ft\nGl4BWfwCdMJFTTg+DhYem8NeNkij79kbm8Bc0TyJ9cAeKzHzHa1lfQQWYQkF\nQeUxr3HQDJsEp3IW8/OmYrQJttZST1Wch5FI1pVWiqzjkANpLe3OfVB6Obrq\nZmodVTIMN32ZE/le9whQ+FD3jjaZZVf7VvP5DKcHnPGzHoPB5GrjzmXql418\ntjax4Nc/8ZFqX4TU55DT8ZC/3uq3HNz6ofr6wOk4TZ8SAgrPwjxbdZMVLEC9\nH/Ef/23s6KD4cjwzWh61cdB7v+sD32dw4ErbVgV9ngJNGXeW+xONlolofnvt\nCAl5uyYC9RV6m332wy5h+9ZAi30axG8gwtdI0/MpWK5xj8U76ZphnI4Xd+9b\nAvyuBSLLgRuB3dhaUb2Ul7hJVNQD87yXjisCsfh5yQoRZ0GugF0++Xcw0dmh\nkvgfJyLjkeW8j9jNgakJJjcX0i3M8vk0pm4Veu3A8c5kfI6qQhFu4sDrHbmQ\nJZmD\r\n=Pgdb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBPb5RqQ4cdjrG/UCkkm+ccNJOPYmSKYmdNDjf9ZXSCvAiBvMIKDgfoBDK1Sh9hmfBOHCVVKymOV3ii5WVs/rl73RA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-config-options-rework.20180601140919_1527862473608_0.7251325986525345"},"_hasShrinkwrap":false},"0.2.1-config-options-rework.20180601230814":{"name":"@atomist/sdm","version":"0.2.1-config-options-rework.20180601230814","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"16102cca12541a3df627a0d04952edadfa8df176","_id":"@atomist/sdm@0.2.1-config-options-rework.20180601230814","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ArjLhpqmjMFJsWzXs9AFyJXmIOk4SY64DlRbHCXWd7pQvddlrd2osiTxwvwIIqu4FLSF+Sk8LIWhzHSZX5zA2w==","shasum":"1cbc834fc5581a35e81f997dfdc632644fb0084a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-config-options-rework.20180601230814.tgz","fileCount":953,"unpackedSize":1972816,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEdMfCRA9TVsSAnZWagAAd10P/3aMvtH45UCuRpPdy6kr\n1cc3PrGqglrpGJfpje+6oiNetYpwuduNsqDPoA6KNL+0LNt1VyknbdV5+ARq\npDjqW92Gcryi71W+YphpY4r7m1vP7j7OFtzD28vHHTS5p9SC/B6FUS2kcKEr\n+XAJoXxRSqXiwLQfnbZMtAte9h8eXLA8E5/FZqju0gaqc6RQBG2LPW7pNrth\nBvmsiNAj4VdIkEWWBckCo8wC9Xw4Vf8UFFlIwTEZ/ar+9sNvRqNai3OeW5TW\nElvecuVL+Kuj+VdR6kCClnxAHGXf2WuEplZ4eqiF2JKnn0btoxQm7Y8dRfK/\nkiP5XsB1hyCeJcb9h60eOhjgajwv6ezP27ah/OqUyL+G6YRKJl1GRSgoF1XX\n5cgcL1cO6IHVQsVxSzscsUqjHciJ5tDMrjXC5d40GXlEsJVxO1SkwpH5dFDx\nHVijYCgEVoNkgAQLIKuQPxlMbNYLBU5Of/rcj9Reg1AJj+5kmChgdQ9v2lgk\nQFCjUqP2zOt9ImKaN1kZ9VmCjj7ClZ3sjcKQaOomKfDFU9pZ/w17qFaTDb4I\nCJ5KJ3hX2nB5OAYlLtzZphypVy2gvWjlIKnx4Le2tqrVuVaTtAX9STDGB6YR\nP21THT2u4l3MQTHrfjW8burJoda/GrNzEdqeI8Ec0+lwR9cf/H4vvq0FhW/N\nQq7u\r\n=cA89\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB5Zw6/iL/d8DCKRs+xYMBiFs1rtcDbhHyTZGrKSoqr7AiAFkQ7/ElLLPnSYWhQxfNfpvvZmPP64vuYNpkHzRL2Reg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-config-options-rework.20180601230814_1527894813138_0.6219454379281597"},"_hasShrinkwrap":false},"0.2.1-config-options-rework.20180601231049":{"name":"@atomist/sdm","version":"0.2.1-config-options-rework.20180601231049","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9ec894cddf65386a3ea29a85f98a4c28857d9c53","_id":"@atomist/sdm@0.2.1-config-options-rework.20180601231049","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jfRcuX1TlWq3uAwGYKD/RlM1oQmdW1NBZvuO0Ky3U9XSaTpHTwcU5/8CVbciGdiQEPfGiesiy9nBlTqoAjgG5Q==","shasum":"e01968c963e705aac3ec459d6e543f24b3e8374e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-config-options-rework.20180601231049.tgz","fileCount":953,"unpackedSize":1971788,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEdO1CRA9TVsSAnZWagAAyZ8QAKRXuccZeMwOgqYnebLu\nem/w7rVXy+9RiiI/alsdlUZ6YGQ91/1le4ajm/QhqHgPah6tjEoDQVebQz4g\njR7EWVSnJGtk20JwApZaaSSXpPhAp3gz33MWbHJS0yeBMPZZcf1LtY4yePcN\nDECdcD6ScnZvuGVVhFdtR3RzOpRLcFQ7Y88vMwmzRCCKWrolEvLh9j50LamU\nLQrRLS6QhFlgQBgGtREJ5IrvLWBLSzzeojdaagUx2uIqZbs1hxGdPrQcYX3y\nVzQV0yoAgapD70h7BPj2Mhe9W5mZyCSyRQfDQLDSvqXaYpNMUXCfu7AmJL+Q\nFu4VXww+Xwjlp0bDQwTSLn1HQfhoO/Uq8d0KX5P7cDo6ocd0G6lOtlyPEN2W\nFgSsl8hgUf8rJPMMVM2OOa4gJhnkYLV6uISCopUA71dkFLTA4f8IL4rr6dQe\nP/NwigeI6q80wFH5Z2Fnt2tAJZdEGeR7qM4SGqzQFBBqDQARC1L3aUUwGXr3\n7Rz61aB9BUFura6T+Aas5kQszB4zSRMyngcDaNWdfLUOy66PsAEQNsODny6U\np2l9mDbUpqPlKa1DquTnQ2rE2TbnuwhYg3ed7a+bwgKuL2MG1y1PfzkbqNKL\n2WIRjqrtoU7lLrzaAm3H81tMvqaEsmTTJ5fwJ1GQeg5N2sP21jmgNX/6v+mX\nCg+M\r\n=uAmO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA7HDIEgdNx8FV+qm03Z/VbQ9r5HyNAuWzHSjYxVLWJGAiEAmx0oOJOc7dmtSpsmAa18ik8gLxDik1g7yE8+TmbXqIU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-config-options-rework.20180601231049_1527894963544_0.2846610081777634"},"_hasShrinkwrap":false},"0.2.1-20180601231552":{"name":"@atomist/sdm","version":"0.2.1-20180601231552","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0532c6e9a6cb36da3b4d80a7144151b5f0288f88","_id":"@atomist/sdm@0.2.1-20180601231552","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xtV53vx0MusnS0hwgS22BMgA4TTD6sdzPJVeQJ5eGbyQ2OQ10Gu5/zDLGhPsc9LGYhBJ5QjjY6QfUgwO7YXTjg==","shasum":"af9fd74fd0b58980cdb83e2339f0694c1131c2c1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180601231552.tgz","fileCount":953,"unpackedSize":1971766,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEdTNCRA9TVsSAnZWagAAKIcP/2pMu8P7skmwBwldPVgl\nE8O/OgdkGgvhxKidWccLJBQIYwarKgtPO9bLnuEpoyJlzUqCl1wu8Q8Y8GdQ\n5P7nbRpBGSnMC9IdDNn+XriLUxZb9I96kSr5GpD2LSKIGlzuHio6gwqbkFyr\nxWRWOQYLT0MtdaXnm0VkeZ44cjysCM2W8yiAMIkjQfwz8vNReC5BdOxlY9UJ\nw3jVUGZbqUWNcE/ZMK9+Ff6Ds+rBKuXhNEp8N9e5MoVhcNt/pEyDXs+FWBiH\niHuJXk2uDRnXApcE1wF3nl/SWs/I32Tpi3qWg2zx9+9SFX6JBbKnpR0a+3sm\nktC/YFl/7Q9dOP/y1GWaqmX7PQ6/LXbQ2Ugj1cHzTQcVGycnOWVmgif9bdVw\nZIKLAzyvyUtyeqshX5QmRTU3a0EtXONNUDAbQgsHfOcwv/0l1nnlHehZW41g\nhnLSipTwjXgf59+o1cs4i0rsdWCT36+8OYJ6FWgtqEKpztxvcp3fE8Pl6yQh\nIhmZEyTHD/FXsiTj1Q6OSPKfNIfbt9vMdD0KzC+nihI53d4g7/0xvoscvqC6\nDRGki6hjuHl2f6Wgj/6Y1dXTLnfF+BQlvLpnEb+IP/QkkihSXgY61MbGTAWo\nS4wzHJc+2MR7pCCN72qYg8mvrlYITr9vKCGdpJY3fFm2ox55YYAUMsdTI84r\npBRE\r\n=wYZT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFdbDvonMpOVJJJMepk5I0eKS/LTHQch920hvfzHQSdBAiEAibkvJscoVwJ6DvVKYf7c73XSvF8t50vPwBZd2tR9hYc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180601231552_1527895242646_0.4129184091681797"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180602155932":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180602155932","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJStSS4xxQ","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3ffcc1de998bd848426d652c0e26c6dba5fdbc20","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180602155932","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BDTqzYn9JHsR24YtB64C5FBDYxvAZFdylUI0p74/lxjbgMuM8bh1+orVS5STy9xw9Nwti6xeIYbi01UqjWfqYg==","shasum":"0215b039501fc1471dfb53a4d8faece793893b0e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180602155932.tgz","fileCount":947,"unpackedSize":1951054,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEr/yCRA9TVsSAnZWagAAAdcQAJpp2eAqSVpWkCYVVf5E\nlvzvfqyIa4wbZPsgH7qwJjhNvrUZrNeRdn9MaFxGcu6E/7CyUgVCTM54+0PT\n9fV3tvtC4FNHBbKKtQRHSUwJKKb3LgdlvUsTRboL1pGycD67Ug0LWqU8Gair\nZ0+aB/CwSXrPLcuft/brlQt9ivctywtlBkexcqay8kVcovEqSewmHpTOsUqi\njkwZW2zSbUS4KO5/J8MzLfHZKGvWTElWuKKpaz8De2ozi0V3VXkKdql5byyU\ndUQMiIoercN8rRJG8VE6SkVL/i4nn229ibWVPyMZtBSBBPw6IbMhkBXvc02k\nAxHEdtwaWczWxaUnk3uE9FDmFEjwaracJ3HLYXV3sSbzk+DHZcyfiLKYHUT3\nsTQy5rOtwbINff1y5lrszi+4nMBg4sNO5JFaUQsWBeOCYQTITYD9uT+wAUNe\nz0jqXAum1OOUwIoZZ6nshyk1klkc7MjVssxnbStD65NUkAsJgAJ8NAnVE5t+\nGMc0SbwG6GlRQ2w8akzIvJRFwUeTdy/9wpi6ZfIW3zYhpWxHEDlwcGwf1Zob\n/AnicIrCqOqvB5sRrYAXk8E4cP0blRUza92B3hlAi12CyRRrlc1+fEi+KCGr\n8VU51idbpbiNI9riEw1+tshpx+4V6qRFjZ8YDwO8fGlQCHEohyvBkpTi85SQ\ne4GJ\r\n=p7X8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZtjTLZ9zeczexK+R3LIyfJGvUdyffLBWkD4pLpLU7NgIhAJn47t6KFyI5toVMbk2oMYmxonE+j2W7jHbfQn4WXaVR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180602155932_1527955439058_0.41738553380359567"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180602160134":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180602160134","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJStSS4xxQ","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d5ee3be8e7792ac40273801c7537037c5cba0d5d","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180602160134","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jF/4myunBPWGywHWLPQWLDMDKSj4HurnBBuNKW8UqnBM5coTMMhorryVNiXR3fFuV1EfzWOV74wYGZkpGpXvCA==","shasum":"01bc97ad89c9b3d0d225b381ef5a39957d5513c4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180602160134.tgz","fileCount":947,"unpackedSize":1953872,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEsCUCRA9TVsSAnZWagAA4J0P/0aMeOW0peDB8uhT0Bq2\nLAGmnQCkkM+Vcz9/WRUBVBrKZ7hVKa35ZLciUZdEo2FNTNGEKzQRItr/UYdQ\nEQZn27iPO1a8Pk9OADzp22NzyYQnXG/jqRmORPZxOfWa0lsD8VAjwv5Zaf9j\nfLQ6Z7Tn52HrErSkNR1uRB7vT1XZXhbO4zSNMxV9ZexrnUH82Dm/dOR6CYw5\nKecKMJjyCiUpEqv3CHzWcqkUcPXAaZ/WKxtZw7gdPn+1HM9CgV09WDK46KMW\nKG8hPfXlmEZ0+SGjf0H14HcnklMioH6thqFPQBoYDIkXnHmoSljRtA22JZTw\nohxW9mUAmIrpGnkiWUi+IN0OTYX8UcJdMNZkm27poJO2Y06vM2HjrGpCrxws\n3yumqnJNcrxNcSYVRWuWwCGMw2SeCJJ2gwsc63j2JJE/U4Ep5TYwHhkU8Q9q\ntpzvynZknOYJA+XFXHdnkd+yM+MEU5CtvjYKZme5jP6nqDSJS3txYDOulZXn\nIhkTwfCH2cAT/JSF3S4YBcP64L6YQmrOKeuT4BrdjsnNPJVtJ4IR3TF9sjl5\nH0fsPRvixbjNY5JS26i2wirEglKo6dT3YKpcKtDf0O34r8dtSDSnGGbCA5IX\nQQUzgRju3Bq6tpiFbPngdcnAZFVFPLlk0/aks/V+IuKCO797GMf1FwjIFUl7\nOk+C\r\n=yCEY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHfaV0OU/FvkURfBKKMwY/JKOMKjeldcF8QS5LdDZaVZAiAeWtEMKZrXUZqB0wLcqq+OcOmsZ+NPGgQbndGgrGe7Rw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180602160134_1527955602469_0.7008404528035321"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180602163041":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180602163041","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJStSS4xxQ","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f4c50395c0ee657c2b82b3e1d5c2df9576aa8981","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180602163041","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eXOC6T1hgeia89IIdKk7ldgLQopK54Mi/axny5WQni1qd+N1YRlgNtFvjiu8VcepLPwR6vRDHMeP2UGdHdF/xw==","shasum":"45e069471d8de46c3e40f17469e6464b329260a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180602163041.tgz","fileCount":947,"unpackedSize":1954194,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEsdQCRA9TVsSAnZWagAAygMP/iEd9kCAZ/yOTLqJ11QX\nZ5jXWrmG4kFMnajvYm8BRSeLwT/KxTlhQqVn9pDsQ5kk0RUEjfEP5vb4wS9m\nmUn08ovziPisC7Xeo5b72FJFTyjbHYooxwda3TElGm8HuFLd2z9OkNO1FNaQ\nPejO/NSF9IMFFfvgzdfUa7OPHbhb+EdWppBD4oburTfqcQLushPNY748yVdo\nusgmf36O7IGjL4d8XMsF1em5A7EOTf02Q8eRqsHk4tCUGYSvUa/pwjLs+GkK\nifum3GyB8nNRq9uwfOSFvEkrCPf8oS6/dNT/OwDVZ6z0DRh+V/ntFmAB0LgM\ntsxRvQ8GsIzyoFRk7MZt7I7NCgs4U4EdGVeZ9O6iLOshyHD8P+aDUq4oGj5T\nvGsnPn5sG6+BztId67ZIX03TAv0QtwRybUlwp8ROohOXnMn892dklOrsUze0\nEG2Ok6hBojcn+ibPfJr0mDey25YOEdH5gJxXJrxcS1bwMdUBJwsNGBWs4Mpk\n9Ab8w9v4/M5PCkduXOhlJX749A1GoqZpx7pa4BvswiCrdtXd/R4XfVlzGEOW\nw7iVlSSMm6/NMBV106pYtwwWFJIpUNGq/1gd2StxrUs9b/C+lY4xqnr+wejj\nmKx//BA2dIylMxdwJDAhHR2SoT4R6BeTBXUs14DrEWaEXukBbe+OSXeA/0pE\nBCSo\r\n=Noa7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHFl7Hcnv0CtSQY7wjmlqyu5GVL8yZr1byAPPftYEy90AiAyQz7Vv/jKmBMUuSR3U1kWDVuHSosq/eSelDlDThVZpw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180602163041_1527957325584_0.03497053754129875"},"_hasShrinkwrap":false},"0.2.1-20180602225124":{"name":"@atomist/sdm","version":"0.2.1-20180602225124","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c0b1ebfc13545c217bca47bb026813fece85d272","_id":"@atomist/sdm@0.2.1-20180602225124","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0ZkWvMc3iwfWfymkFE7iTCZIgHF3IRu8zbQypOOobrCJ/Z6bvhGMrEVspOGAIyefcchiJ59y6udOhT84YK/kgA==","shasum":"0c4b9d1fbd5bd1d82bc98b735bf4868981c43af3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180602225124.tgz","fileCount":953,"unpackedSize":1972421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEyCZCRA9TVsSAnZWagAABXIP/AlSHEEIIdbPKD2faOEs\nygxM+QcnPh/pYAbTLNgMj5/MjFOBV5O/DPGoDN3p+6Bvd75r51ZS83hyCsFc\nFF+KINg/iKLbhtaILl/L/i0+IsAI4NpKoUFkvVnlj4xZb0ly40zYkWFcwR5O\nb7TXX2dt8TvPqnI9WUzHNSLG+LpF/yTP3b/GT6q3KiFPi+K5lSFKEHWf0WUg\nC4UpHjv6dYieLWCsWqB3+1RTyCr4OUuNlH1OLKSi+A1RCZNp/OM6YUx3f2ag\nOI82hABMLkPSfDh8a77nQK/CL0n4LNBMsZ8cpxOWWfI5/My6P9DEIOyuYwYr\njHiZp7Up/P7sUfXw/+iaMYCIIJaG9WiPERQI7Ujo1xwqlmdFmNKIn2TLTIof\nvxOEvLlQfQhciIlMMeSmmFyfLlURQdAs1av7xBv+BYRiTezhj9KeyrOuMdAL\nWjprvKnm9n9K0cH2ea42GC5toQKa9mr//b969XrUuSIlfqSCsBZp8WTEQkNU\n7yIEh1aivK0GG5ukjR9aYVshiphTDnoba6OXxMHiJAz4+Ec2zIWB0fyL4Xz5\nGvedE6dVaDIQDwEGwhkEQdyqM35IKjjdI9ej2sWEewaFCCBPdU6r9p8V5ciH\nk3nIqCNJPUojfiQbTPn7FD6ykmkF7lPMSHshrJ0NM03xXi6TeDwP1pd6AGGt\ny53+\r\n=e48A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+QzVesk+iJRbgsTqhRpGQ6qll+ikVhlotU2CoWc5CmAiEA/U7Fu1DQW/tSeKs4KO9flbwEwso34o6SoaJpEBQ+NFo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180602225124_1527980183187_0.5423668516626221"},"_hasShrinkwrap":false},"0.2.1-20180602234651":{"name":"@atomist/sdm","version":"0.2.1-20180602234651","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1bf044112fdd7768a9fdadd37bc0e793921a9bfd","_id":"@atomist/sdm@0.2.1-20180602234651","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VejOLIcO+FpEko8Sf/1l/YYV4Ds1NbmvEWqf3vlOvmm7Y87jinrxLjdbQf4dbkjS8nxbUYacA5QTt95RkBSM+A==","shasum":"00f9dd41003d530e4761bbc09b0997b89b0d84e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180602234651.tgz","fileCount":953,"unpackedSize":1972477,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEy2mCRA9TVsSAnZWagAA+JgP/0FTGowyVF2VhQf+8ndh\n9mDcXZJFeTk4Wgqx/2K3wntNEIjeRnX0+1QCcTxae8S6BBS+90pysDYanvNH\nBa7YmWzGRssC6E8pCqhy16HZs/OI1r/6ZKa6XPUuJcSCnZa21qJraS6q3nXj\nIiN5ZxHURbABtIyWe8VJ/0moXhXTSXVZHxxWlfFh1ovcQ+gfneptsGAnaKwz\nbmcIAclPmNNLP3x8cg5hETbcd6OoDjmXhmyNYQYD3SjhOzONa4H5Ve/EbdrT\ndJPf4OhjzD6P8dAgN9gjqH1wAnRDgjrxxkMtAMOs0qJWI507xelmX+U3Fr3o\nzkD5smaW/nW13o7HEBkud0KhbnNQw5p0DvmEk0pW/MJl5Q44N3o8ddRqfEB8\n2PH/eW6kDFFsSoXMdXhhi+QmQG/pSpOwJTYiSww6WmUNcbE5kucHdn9BDSUY\nP/CURbNCJhpSiDEtq5h6+QIlh5igAYdMe/Ep7vLPizPKkQER+0VTsKNWRbMb\nbzGooJTAI2GfsZQtbSpDXS/MZdUduMuEsdgHaGrijvLWe1LWrG6d0W1HFvjD\nLuy2xKCWuoI9ywDnXoEDuu/s8NVXjbNTavueyfn2m5vI/wl1Js/pPiwBYO/A\n53+IqaDPIS1My67idVdbewxzu0pQxIaaWlJEUAi8XaKtMjz3iLhQ6a91avo2\nrtq2\r\n=yqLw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG5MZYAW0zo7I8qm+W1gMwHmBXRtSf9p/jDf3uvCxgiIAiBB82iN+29vT3xGVTnHqXt7KwVyI4VKeiKYoEztsFwb8g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180602234651_1527983524246_0.07582712908997835"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180602235207":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180602235207","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJStSS4xxQ","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"730aafdf47ef3446ff52ef86c713d02137b79910","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180602235207","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sbZwJuEJFmREmFIdFK1JkJGuxAmoDkzTkpXNppZIkaasQ1q40UrAPuek8rIRVWTuGCaoeq2g65yym8SRvTYYlg==","shasum":"20870e47e853576bdca3da5ea904571d4fcc51e5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180602235207.tgz","fileCount":947,"unpackedSize":1954905,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbEy7iCRA9TVsSAnZWagAAA00P/A+bi2LGieFP5zJtfDYr\ngsBkQFCqeGIG/qCdeJU7NnQnKN7BWz0LVDm6bniDhM8cdG+7Yt6bhzGvvFyo\nN8hNhMQCsPgq8V6TN13CSG5DNeiGLjGOuwG5nJOZCISavB/zfbUC0gfKOzmk\nSczUW1D55VXMxiLCtwMp0FKq/laZU82JGSgnNaqG9xTstVE2dDbXqHvBzDkg\nL8QkfGx+jBAaxqtOgFFtlp9IxQkXVwAl5sB7+IIUMNarh0R0CsAZ66Kjucot\nSdRSXrOyPz6lhuCaVHf6gtNM2PDsmFlyurCpeIhVS5/n12s8maLcVVcYNsfB\ncWs6O6y9g8ZFcjgZ7jnVyePOazwgjY5/vdB/3tHqGPyz8qWXA3P15w39rA1i\nSDg2tDpaVbHUiDuN3B/Lo0VBpIlG270XdocoEYaiGHRaMJqitis9CEl3RAWL\nCxPflbtDbITGgblyMx4WRyF+is9E7TEfOcuC5JoUufNooCGSABt1sxd5y7c5\nkrcUthi9yLZnqKojCy4nkmKM0T+WYVLo1m6ZLJ1qn0vbHlrHraLl9Y119O9y\no89D68luofUOYmOTXxAjb+ETK3acEovWyvwMM3cuzIpTw7o2UWNbK9Kq9Num\nRB+n2TfpcgEwljzQAvgTQqkrQNOa1L/vgLCUIluNHcZPBa/i51l+U6wu0SRn\nEi7P\r\n=7b+Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC9oWnVYCENIUhr3LFxgq0Kj0PapSRUABgJZ3z/Tc7USQIhANy0XWmprGeIuv/QZTR+3PCPHWOtdAVtYQsinJ/rPMDz"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180602235207_1527983840360_0.4551950105990541"},"_hasShrinkwrap":false},"0.2.1-20180603020351":{"name":"@atomist/sdm","version":"0.2.1-20180603020351","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c3a4e2a232fd1cb079e5ab7a9ef0a5f9284068d9","_id":"@atomist/sdm@0.2.1-20180603020351","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sw/agdQwUfblbRALJY6Ki5Gp4agxbrKaFwc7Fb4xu6tArB4E4Ic0MSb+l8GUcpUr7x2t00ruXGzQG0bhI4UzNw==","shasum":"16e4e6276a993051b43164766bbf0a9d4c419183","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603020351.tgz","fileCount":953,"unpackedSize":1973332,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE02dCRA9TVsSAnZWagAAcoMP/3REHQTneEqd4m7KbcTA\nd6EmftsUc7IQjKK3UOqhQuhadsjyV8YqLApytuerq8dDYryp57uoIf3Gi5iL\n5idOSwOQfBzFspV2Aa0rq+pvxWoyLhFOUoIJl2gtKo/slJF+YJ5jgHGv1AMd\nELJNc/+8YNZ7uTg30y1Y/aptPfEODPkhN4kHXiJ6gcr17lqJ1UUYYV5RZqju\nhELco6uecpVTJRGiWMdxbmrhBC0rZMwbN6D7PqFVD/L3go/oTtID4kZJqt4u\n4n1H7i3oycqwiK1WUPQ9kzfXHPVce8EUUF1Sv1pFta09puuCdSD9Rj9eDEAn\nGa+vyCAX3PzjOzOnyidBPO7AufLql8naqe8s4hkd34kU/t2t8lMsvjjWjvbm\nddG8Qc/+qaoStC6YaCz0TOYPZqykqPlAc35WcrC3GWT9/Sams7IPDpk7KnOe\ntQtk51IH/pqHsLAy+z49yFQL/SKrdVGtcfMly8EKnZNmpFwPE4V6YvD3LHzb\nRRTd1+ElfJlMTDRWEOHGzuLLHaTHScU81FcL03e2d2iGAWl+g5PN6Vzz5jQR\nByMrbnsxwQxG8gNahw1PAatyk5a3kBMzVwvJpRb2CzHxwd4/BMEU+hHPNbNx\n66mWzXfi8uNazsRfeujP8/bcUpe2kZpdDJDh/2ith54jVCVE0/dCsjeLZaDt\nWMRK\r\n=BMD7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDucQZ1JMYx8u1ORo8e5GyL70RVHGFq0jNc/tKs+vO47AIhAKIt6fRb0GTMUFxZbn5XIH0jacLYz5io738EG5Zi+hiZ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603020351_1527991708869_0.18093443700968725"},"_hasShrinkwrap":false},"0.2.1-goal-split.20180603042648":{"name":"@atomist/sdm","version":"0.2.1-goal-split.20180603042648","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d0601a1e59bf2f96792b333ad4bdc71a53d98052","_id":"@atomist/sdm@0.2.1-goal-split.20180603042648","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/XYSki1psj8/9vMtki1xbfddcjKekhdYkqBjTtB1LNoEZ3E+nTxpo+O309lzsEno//8SiDTM+EPmPX+12fPLLQ==","shasum":"55af4cc9d9c59d16c3e2a748e1f2a7b9fbd28bab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-goal-split.20180603042648.tgz","fileCount":956,"unpackedSize":1974002,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE28aCRA9TVsSAnZWagAAGnAQAIAk0+GpGYPX8O5p3Dww\nYbL2+wi4yeXRwfBC9Fhp82SWVxaKpIFRQqnw8HaINc8KUQg7gArVYlrTJ55G\nBUXKgxfVFMExANXYTtRYhaFXMH7PoUsgEFeOAsvw79hCj4eFL3kWitNTq5S9\nReOVsap3UKG1bKuG2HOfSPYuUumXAfo35T+w+qtzUADNEo98Rv2aZ8irJWEy\nWdkw88J1c5c3L7eM/zN/u9OVg+bYQdzo3K9qaFq86bhM0B4nN5o5uj619q0O\nLqu+VJt4n4g07Jyh7WvxWsgPeoc0q5wfuERf9/64+Jlhno0DrPc3Otl3JX7P\nHPY65gcDIMKxoV+490odCKVEND0OmuD39dRZSPqfdKEZTzUMovtTuGNI5S/r\nkoWMjwWpSMcDqrZOKClP0iuk8jWPzDueZGVVoaErt4z/1yyVVaykr13v3yzX\nimq63tNfk/lLVyinfezGcdS2wi2x8U9M1EV8MVPXZsyNBHv9Qy+RIGPT0kAJ\neb2a+rgJnNIvscGXlJMhbZitFl5HPPFDZmEW7YrZnIa53VNIjNzmpcVTchuY\nvkGSajh3YjJruUYWqNejVM0b3Qyaxz7KTkP3NvyNhGS9T287eXNpqzZzMEUl\ndkITFVElB9g5xXjbv/3MQAvfTWe4dlXfKyArLh8/ZhjLrZz+aBau2oi9v9sd\n69G+\r\n=Lq8j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCG4gy4JUyPddOqmKh4l3obrcqW+VJiFyrQdHompT8blwIgVEGhKlUf1FpbE5/Vs/YxPe1yyuFDvHOcPNlarukflk0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-goal-split.20180603042648_1528000281878_0.03929654071430955"},"_hasShrinkwrap":false},"0.2.1-20180603044911":{"name":"@atomist/sdm","version":"0.2.1-20180603044911","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"050930de88700c6d5071431304d73a019c45924a","_id":"@atomist/sdm@0.2.1-20180603044911","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Xh/1oMuJewd9p4RJ14Xbc7aqVy5cWNv0Ex3qULR9LXx62HfB55IdKufwJcHI74p9sgAMbdEjEya60FXbCuZmcA==","shasum":"d45aeeb4c1f88594532162026c449711ab76b1cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603044911.tgz","fileCount":956,"unpackedSize":1973991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE3RfCRA9TVsSAnZWagAAbekP/3fpg7ys1wq5eyafyn60\n1peMgxUqFUIdbGX8MM08eJ0qG/3xKV/mKKOQ1kNM9UpqrhKYEjiX5UygXEal\n9Ato9qy/dtaQgn+FAODAg1hPeRK4olP0r/RspeZ/WfX3xGysPRXbXgP7Rk44\nL+ukPC168IyHVavd/L3QK4LuHiB04wooPEioAtgCaHOInwoSpN1hUodJA1pu\nKcVuL9EjWMzkwRz820L847JFN6r5THzFr7MJIP5Ib6u5y/SJtDJ11SVEcyyn\nWe33EIed3MkONzJi3vfo+sbWnAyWXvPxb0GnPvOnCheBsAmoFujITmLMFGZh\nR+3/iYl2H6YFK1CBEdGBA19JiSArdrGwRs/KcvUyVIJEWpa/qldiNo4sBZGn\n2i+gb7p4/NlZkrzIwvMTl821TFVjPejH/V1HrXDlmx4rDK9he+NunLiowMkC\nQyaBPPC3A+Taq7DVce+5OIilh9sPOhBAx9W5HI95TTaVYPHojmBErqy5jLj8\nh1el8f3agr4Klp7XgVsUOuWFiRaRbshKHlrooOSBUAdc26PlXYafK9lZS79x\n7tccMHEHLAdt7EIDf9SdSOnRdg/QKkpGEuXQlOLZetTQmNeKUG7orud2u+pO\nJLeRC+l4zK8ZnQyL4rQ/vT2kHq3K0BldG4CoOHcQGdGw7PJcAm/BC0cFfu3R\n/17K\r\n=S/n+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDIkH2uq4BfhAFr1J/YWtXVC1K9P6ZQh+8Pz4BkjiGDcwIgXY/79uaIa2HKY4glUg11wFUV3i8txT1boC4YbUBbylk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603044911_1528001630565_0.10300896031549267"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180603050234":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180603050234","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1BAVWBgx7","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"811540fa625ca5906dc0a715c6ec7061a8c68609","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180603050234","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tYWIV+EHO8gtpgv3HActJJ/uc24fdRUxd4B6YOPcia2mtNVm8DK4xUXsj61qUktG2Xaa0OJUgiqAHz2JvRRRUw==","shasum":"9035e4954e3ff8bf7d86ba9c6f761a466ecbcd1c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180603050234.tgz","fileCount":950,"unpackedSize":1956419,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE3eSCRA9TVsSAnZWagAA+igP/2HdkGW7fCx1lkgDSedx\n5wIk4c26nQJyk8G+uZ6aEzoQxgXijTqDDoOKUSn99dh3Q2nH2HE7uCyOZPnZ\nTw3oRTsm6lt6p2JP1jhDKMSsvh1t7NK0sXtDchTu4W35Cq5hXKfdi7zP2MEv\nFMmPSfXvXR5Z8C788cTw5cj63TCq4higCrfosiG6b29w9OhP+UbtaRY9Ip1b\nbFrSi0ODDXiVQngwdVFMCHIgCnhFoPWpXp+jr5+J3Uuw2DVzsqfON8efPFJW\n/qo43c/IlHx6qLByHPuccWKz+GnpuxREhHp7bJ9cZ6kbQ9/oUTeM9dbaL4TQ\nJkUId7ckqIYbEIwrT4HosR+u9KCM/+2qCcCibVXqhF2TDirSZ2EJ88mJi4PU\n/fZePG+lZ93AtamdFe0zkm8+sRHionftVQfyKtBY0SSBGMSUaN4sbn2I/hlQ\nBA3+neFZ0ZRi/lLnkIJizV6XgX1t8JoTT6c9rOmLkrFbpvtnU2lpyuvKZnCo\ndqHSP3bYe/hYzS7Ps5gE2n56AevfmVb/mJIDDaGWibxi9V9b4/M2lsnVb1Si\nLTYPJCFXO9fcuODRVnEE3QQJhJObStSz6fDIMmVfa083DECmnQw0IcvUesRk\nZ+CsT/B5hoicitKzW4PEhCF/TX1RxrZ+l2NKrZyMJMHW8tCn3TPm1hVj/6oA\nNSRE\r\n=IoPT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDaPcrLbLjdZOujr3em9zf6FDULKb5dBUAWJSEiAwfYIAIgfV8I3wBAeXz9GSG1rTuicurZ270bjdXzkWV96/APxYU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180603050234_1528002449579_0.9351921293916006"},"_hasShrinkwrap":false},"0.2.1-20180603060320":{"name":"@atomist/sdm","version":"0.2.1-20180603060320","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3468f529bac338c84edddbe6cb86a4b978ecfbad","_id":"@atomist/sdm@0.2.1-20180603060320","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-v3Zj5N3Fv9p1tKs55CVzbzokDZkRlAmVCpUmKKZpXy9tcZE1zoPCeoqzYf9cm2lWYvzP/p0I7pkXhAo9CPQY7g==","shasum":"014948dc3772fdd3344f47373f1acf2d39a7d390","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603060320.tgz","fileCount":956,"unpackedSize":1974041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE4XZCRA9TVsSAnZWagAApVIP/1GY26miUsrdpuhOECUO\n4jK8MAzMbjDKcVpa0eGHuq6tUC9Ur8Xb0DZ3rUpcL2JrLEM0tPJhHPFg0Ti5\nNxJqCvj8o5Ico/3/GpDyw51CJTBT9sDm3/kSsCroVrnVqwcLcHuV30sm5tfR\nhMU0WNh4gfLKy3eFJ2j3S4n8S0c7TeG4D58ld7tNN4br30VKdxMB+JSKpR82\nZhpeiYSrbmoERc5g7iw2QVrJ7MF5W1rr04lfqsBboVAOR+8HE1HXEHAPYbCJ\nbIoOmQqgmFUt9/GxhTbNwcvQsIuH2WCxbdlAQJjXvrh9H7CTphIZvl8P8+nd\nQmkpJDXJfl1xNPN2j09VqJ9qHzn5y3XWe/CjnkIv25Pg0RFZR7Sfb1Gacn4v\n9g4OTVFzCo+qBxeS5oamYy5BYTXVBF0BKlfRM/Tj0mUv73nPDT6n7i+MggrT\n9Hq1Ed3975a670jTieYnfE9a0YItXUolSbAf3eVsEBW55ShQyH424KSFGS8j\nXpk+vax+GuBcMSB4ikdrDuPfSUGiltaHYAy5eNKfBs1zjsK1NxpHaNp9LnGd\nDsrSOT6wfNvAK9Vdjl4C1uh4+XkZTxDOrBncXL5bBGAsa2jFK/a2bsTkU9M/\ncWR8LF1kcyM4li8//V7IqLzIHiwyC7/6lnt7J1C5EreJXu/Wp8rN6Fag8FcS\nAkba\r\n=jY30\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDmfgS6yBFc53KT1sH3imfnzqBq9nGBYPLyyUQM+bVMYAiBrrOJygjTdZAaewBPg7ju6E2kvUAHi39CBBxwf+BJu+g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603060320_1528006104689_0.8231786895718525"},"_hasShrinkwrap":false},"0.2.1-20180603060403":{"name":"@atomist/sdm","version":"0.2.1-20180603060403","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1f43c4e78944eae868d5306d444b1b61ba2e6e3f","_id":"@atomist/sdm@0.2.1-20180603060403","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EX6Adb2AcgWvAu79XkT7Br46FXWfFXodR2BG5OfGGAbXKjE/OVmb9BAtnqs/VjKdVn6L7Irr99gRrIxKjfIeCA==","shasum":"40fbd4562818b10d85a61d396794333488a9491d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603060403.tgz","fileCount":956,"unpackedSize":1974041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE4YQCRA9TVsSAnZWagAAPZcP/jLWMtzRI4fU2d+we9kX\nc7e/cbqiL3KYsmJGEd7kTvmOxrwgfuyNozHxnlTZOYbA80b+//rVpjLmAW2h\nAAtQoxPq5OXdRtR0AyvJMb+PyHlZWgeYMWFspZIAfqN8pJqDBeEHLDSWiOyC\nAFozYscRIY1hY/uM/knPlVmS9diVqf/tyO3cCsBAPQt+nSUENGtZ+QPpLjtZ\nm9Pwh/XLWtIEerCrDsV3du74bWgKcyiq0wJTDQLjTHrJmOSmVr/wsCHLVLmR\nvpKlgVgDacpC4sd+GexRrFTSIrT0xysHO777N1cixFqkVsb92LWhoatQPxWh\nD+RzjxHU4QS2/CVNC/iz/DDIE/DEW9AQD3HiZrjy+rzksnkIJ999kqESQBYZ\nZSUAPHgmcHuPt2vGrwhYNnTCzwPrdioOkItwlucc7AbP1oRzsXM+n0sU1Bbb\nHggMtTdhc5JlswE2/LyP2U6AOCO4Qk0J17TDTX8l6PsjRFI3yDTV3/nJT1YJ\nQawk6bZg+xca3ttbYkzcmpe8Hn5bYmCI4ZuvgdjPJhyaLhf4meNvKbgn+1/t\nI0zt7MamE0D9U862HpUpAdnoVOnVp1eAIigKH6rO2UfvhunFLIK+7oIztNIC\n8s+LVF52xgpJl+tDk2BbhRMivSVuSQejzVI8agWxX/sOjws8H6OTdG78iDmH\nvlbP\r\n=6sf2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICFmn8j+k/Qfcnod1XxsvXDDu3LqLdAbTrjICwqLhn/EAiB7Vr7mHw410cCuwpFaBIHdFI4RYEOmgJJnhG7DKhWQFQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603060403_1528006160169_0.8485950956708472"},"_hasShrinkwrap":false},"0.2.1-abstract-sdm.20180603062543":{"name":"@atomist/sdm","version":"0.2.1-abstract-sdm.20180603062543","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"18949eb995cd0c85f5e45e8f20d85af40e07c4a9","_id":"@atomist/sdm@0.2.1-abstract-sdm.20180603062543","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z3JvxSqSBfBbD1eKacTTK6zdOpHkuIokKqvXboesxlsJtmRNQpmw85jI8F5VuvfEoWTy7XcjoB3xfvFnXj7hvg==","shasum":"22e84b364a2e4eeb3e37ab7d7b32e8361d73fea6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-abstract-sdm.20180603062543.tgz","fileCount":956,"unpackedSize":1969875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE4sACRA9TVsSAnZWagAAKMwP/iAW+K/nurcOMvty8gof\nkK8f8Sc55WCxoTsgPISeU/J/DSdkhiv8wRn+LbqzRclRebSTb35i67LFTWFd\nQ6EPTkeaCW6hXXFrH+Jmkzg9bp8IrDFsnAc8uKrKREbYhDTdKMw7Re603Yz9\nj1q/4ZnKOkEMzIfCvnALkGlB9jhMvTh4rnbz/Nhyk+bQvcBhBUIRbiPZ47aI\nG9o26dinUtnjzJZ3XzIhiu7hzzbsV55pBjduVGyXMdawzH5l1N4G7nlgKRwS\n5NXX2t5osvWhjfZckloGf0+YEaBeGcHTnmpAzR8uzrrBJHbAYdxGDwwlJFjz\nAUSEQEzhHGzDX03VBfr1huOsbiUBLDK0j7FAndHXQzw2zEMtE6MzKyHY1Eni\nw/Zn6ltOJYSQ98w5dk3g0smT94kqY1v1h2G7goiPEKJss7jt3E7aDJ+uJzXE\nlJTA62xBdPRHll7l/30Q+yNxvpJmzRyeUAhDFhN9Ly7+Vki1iSoJoqVarQ0F\nxO6ukjsc4J7WurGop41NtshJqmiG49laXn2Mcx6b4BjlJQXZAgvqZrnBkihb\nLT1T/vIf7TsgHsg93IHn86EXfacwq/b1z0hrpwWgqBgn1Xgv7Infq2gXV5Yi\ndBbRbPB2TLiY5urOAocy5ll+eKaDBe8q2wxgx6WOEksMZ8RA+dC5hDcha9WS\nv0Cd\r\n=yJTi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFNFI9m+yokL2PeFkFPWxRjFwpnbrZIrgSDB1vJ7c+CwIhALLPJ1wsCT+LVQ/lbr7qCEIIzaia209A+LQKQz8fvRDV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-abstract-sdm.20180603062543_1528007423931_0.12449308849127294"},"_hasShrinkwrap":false},"0.2.1-abstract-sdm.20180603062659":{"name":"@atomist/sdm","version":"0.2.1-abstract-sdm.20180603062659","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"60d024d3a194e84acb10a1359419881075c2a4b8","_id":"@atomist/sdm@0.2.1-abstract-sdm.20180603062659","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-U891vsZZgfeapiYRLSmxr0kQobQSDdudeuXwkZRAG0YVSV622JBs7lvo6zaX/0WfwdJq3cae1zbCt+bxKeY/1w==","shasum":"dc6ab4a3afd3dfdbba6141758687715cf38a9476","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-abstract-sdm.20180603062659.tgz","fileCount":959,"unpackedSize":1977806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE4tiCRA9TVsSAnZWagAAL7cQAI+lwk3AjEe1xy5wfdph\nO8sI/v9Bn5rfe5G6hs47Aei0Raxl0fBp4Xh+tC5L+TXYNOIojLyArSFHH8OU\nw1uMT5hLg4c1e5Y+MnyMD3eutTbBLxFQPM0HGC38WngPsdvadL/mKGcBylOe\ncyrKgehq52n2jevrLb6LehVu5sev9yqofRHaB3GtzsOPVgVSi6zJMwC9Rp7H\nzbgJawqrePWyK1kP9huCxWIEumtK5sPCJjnF72MwEtXOOa2+n2pHvDW7PaUd\nXugo++9O7yvGTwkVD3xoduuIjyXXR+hOWxgEIHLHiXxRmpgHLHEdQAFCTw4j\nTGocovH46s12bVa72mVSy8GnIdcvJ8jjY33pTRhMIQsevoDOu6JDJamP8IOm\nZb64NnuCET+wXiQVoGCTdMxo80uMNSWYjG04DZdk5lBV5OfNUfJPXKtJKKUq\nIcGyclclwE24g9kPYKas/NXj5izodtZA3v0Brxj6ikcOFIHI+TTYjgUWkMcO\noUvZQ98ytiNxEGOXYPRaUROq3yjQczmpKT+bTpn3p2BStNzdmrZL1WPsjJ/5\nPViv/uXgxKMOrvZughIwBHRuXrq6uGGEKfsgaOgEFTB949FNUgvbyYEEVhfR\nI+K6j+O+CA4nQddDwx4rGW8MbVcnoMBxWe2r5+AfqMw0FC2uzrRYhRGEuJoC\n0gz5\r\n=7aks\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD7mRrqSigF60do/+2L3XvkxwHNTQ8aA5ICqx5kVuc0AQIgd2axfbv4LkfdWXpF98+UOylG+aKozLGr6M7SZneNzYU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-abstract-sdm.20180603062659_1528007522250_0.7719764650182543"},"_hasShrinkwrap":false},"0.2.1-20180603063743":{"name":"@atomist/sdm","version":"0.2.1-20180603063743","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cf4f586d4aae3e8437868efb4e96de4dc9a08d99","_id":"@atomist/sdm@0.2.1-20180603063743","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uNAP3k4lajbWR18jLkRQ8uTYAtImxIxa8Z8PHdskv1Yhwe6/7ij1CWRthzCKJYr0Hg9YSvXFEwmPD+f1I4bDKQ==","shasum":"02b987fe6eb72cab314164c4aefb1b97f3e619a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603063743.tgz","fileCount":959,"unpackedSize":1977793,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE43oCRA9TVsSAnZWagAAZVoP/2USN/TQE9EUIjCxPpUU\nGC9AC7mM3qcW+ZIQM3wpbq72jiQ6DRU5CTp7AdghWRbsN/XC+MkEjPlW3nG/\nEfStnH8qHiOysbnhZn5x1/IrHheK14C4Dn3ExNXXgJaEldS4xUKbaZMiAa8Z\nJ9yYQ+H3ruTwXyYzSevnyyEvGuMvgMwPiJee/XTgItU7oijQH5eXgwU2+2xN\nFgvW30KDhM1Nm71VsQ+alwW1GpAZqnbKJ7yDFR0hWFlP0QGiuPRwLFr3VBDV\nqqkeVheIOFcocjH1CpHKgDPcKCxrEAWeZX1giv3M1uXPSn2z4xL+oe0WrgOb\n25RE2VuTTsYg+8jM6jA9jOXL926+Ubs49YzqB2//ODQ+Tw/2D61U4wxTPpcy\nHXOBrcUlWUfiCpRCzdGSY/Z094P4NOnhTBQCYDZr/vEdsvM/yvF9+9U4EPcA\nqsk04hsYy0XWObgev/jsARRuN9VSq2nGIenkABQxLig7b/EUjm02JaKr14HE\nmmXynEv6WKxWh98wjIoyjVRtvZYKYvIzevmuf/JApLpG8ssGrePMg5gf+Lec\nvqf0ZsMvJY/G3zzJ946ItUiPQ25IEVLIGMtWKiu7hjqGyQ+tT7hghZRBgnjx\nFzX16s+Vh4pCE77YxVKHdPJ2zx8x6PbrKyDVKjCKxUawE6L6NFt7ARBZZ6DM\nY/9s\r\n=eess\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDdOt4kYekJdMApjCL9KCHRuF/hN6SId4UOj7G/0DLSiAIgHECab5PUgw4tUtRQDpwlSP+MmjZCi0MSFpMjhDn1YLI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603063743_1528008167732_0.2856023383754198"},"_hasShrinkwrap":false},"0.2.1-20180603071215":{"name":"@atomist/sdm","version":"0.2.1-20180603071215","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8b01ef2b3e3989dfe95280122be5b1573b6d4daf","_id":"@atomist/sdm@0.2.1-20180603071215","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HhFMJMzsVkosOLR13DCErxs+hbVfckxqljyG4KfAK3QWIrl9hK91pjmxSvhq/qSK2nCnzUnlO/PLEFGF2SoQGw==","shasum":"7f73d1b2d31a60f24ec8edc1b3b19cefa9894c2c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603071215.tgz","fileCount":959,"unpackedSize":1977723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE5YACRA9TVsSAnZWagAA3+QP/ROs2dSBqmTqc5H8ZXO7\nlMEnmB+SHlsrBPu/n2osausDMTdqIoA2c9AQnqoQHrIvhIoKZeLWRIcgdkwE\nG+izlxI2KYJgLiL5n1ZDHwlPqY+8w+R9lMAyr6Z+c9C13tJOtFgia8hGvTWe\nfytBs031IDbBtuQY9ybxoMLCMSw447qJdF+UrN5yLFq3i7fABCWi63Gqid5b\n6JOGAUbUTBJXlsSokrdUNmgf7hHi6LCrSWH2fxeP6NY2ItpCVvLA4LYPPcy8\nTC4eo8njLEBkw7nFIQn6A/WoW+trR+PM4MTD8rqqKNHMOoQXCU070zYVdXum\nUHPcuCVK9c7Y9+4qcBdTBxDABKSPW0HVWQJpiEqTjFM2aWxg6MwmyIJx9r5L\n7YwdzJLoEneVz9q9UntNmBJO2Ua+BuEZK1zlI8vswgYxVM/eIKvpIURHnJnc\n0aQ/txzMCcjVmYCiaPXe9jzMJ/cnvxDGjCFGWNFdv/x/c/1Q8v20+NK58gQ2\nJmbaX5LT+zqRd4zoeScRcwYTwOQT2nD3CXyb//yYzNSUpUMq+S4vdq82aS4e\nmr0oqnayoPUTFFk5qWyrF2C1H/3EawZPdqbyzdNhM/DA4SkQjeILn2KNIk7q\nE2LzJ5o92Y+BH9810Xs6iFdAYigpOIuCDSX+tp382pDwn9ssJzp1hpu5S/Ie\nB4xJ\r\n=NA1/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD6rfNCY65ydMS+hWbAM4iE5YBzrk/j+zbjEY1lSS0wngIhAJHeEgjXm6ZNVr7tczWdecBWqMDcrfE1rk48PBOAOYlk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603071215_1528010239860_0.3593701154530009"},"_hasShrinkwrap":false},"0.2.1-20180603082724":{"name":"@atomist/sdm","version":"0.2.1-20180603082724","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/Sy40CNL2ym","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"700c6d2cc8563172cc0b8e496b2c4197d5cfdd83","_id":"@atomist/sdm@0.2.1-20180603082724","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LXOgNmZrCXNqGt0t6F+fzfrJQQptZboycrjwtRmwUVjAWWGMWobD7vnXMKfnX2ClTAIdJCKRdVx8MI/Q4v6O6A==","shasum":"d8f2ee2ab341479b5764f794655840c26d5d1037","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603082724.tgz","fileCount":959,"unpackedSize":1977794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE6esCRA9TVsSAnZWagAA5g4P/0Jl+OXQhMxAY+xYxl+W\n2t6QJfalWO1RVgG7rrkXTH9TTcMstmtTb0wb0py/ZFiHEViarcggiTLOFpya\niyJn/kLIYR/+WmjTbScqxfToLn9JAyc6ZWozxj2hiAPKckwx8SYoc6aGSH0I\nQ/UwC2rjObiyVzMw+rJiv8iG19V9O9/0ULq/e7WQcNcBnLCDIa2HpxeC0p/W\nfrGI1zs6vY0oJH9yZmMFO/DRElqfxMo+x8xMytlzorlmWlebJsO/R2XUmcO2\nWKVGtu9ATxxK5Pxtrvyg1qrvWdHkB4k3qm+0wzepGjD1dH1GwHHL1+qUkthU\ncuZnjJuiptMhrV2aLNgfFxCBhhdIbxgQEVAG+8dJZyalfPgxq0GLdK3nmxbh\nYkrml+0hGZnD2RIhyg1FfHFxr2Ax3G2wVYqvBMFwB4ZsCYcVUmcwHFI3pC5s\nyVYfFug0SIpgBtptikxi/iZClvFpQYgbDwGDpXO0ujY5SAVNpM2xG+/XOMrb\n7fKG8mWltrnGl9LfCCRbNPRktWyjazg3j03XfBFRsYjbjqRA5FQb7RI3K6F1\nQEBVMTiSKGK1Uv3cYf7AS0zjogCUaoXsW+L4mOqUecM1M9dDpMM/9Vq0yZv7\naJnxBG8cN5TYMQhZfXPzNaIHa1ynRvzWjw2gQII10wVVqsBs9vcFRupfV+g6\nx6w/\r\n=4+zV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICpVmCxNT68beIyRNHFGnpsaM+JmOSzQe+SiUEQKHY4ZAiBOXuM4sqBPQkwR5TggA7/JB/ciOpRcGfymkZwy4XTP/w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603082724_1528014764638_0.5987084491845716"},"_hasShrinkwrap":false},"0.2.1-20180603084044":{"name":"@atomist/sdm","version":"0.2.1-20180603084044","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"217b788e90149da3fa74627691e993204b42adfa","_id":"@atomist/sdm@0.2.1-20180603084044","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pBW+rKzN7iBFUsScDIJHJIymsiAJ5sa65Nt5rW3JoyxYO68V3mX6jufLr+TDXq7Oy8UtfrxVkEFNRktXj83inQ==","shasum":"7433e384c1cea5932c6eba41ee1218df42cd0385","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603084044.tgz","fileCount":959,"unpackedSize":1977794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbE6q/CRA9TVsSAnZWagAAS6YP/RzyK8rarz48/ct9yMSq\n2LHz/Z3DdgjdOPKFsQdsPil/MILFGTbNyQXpUy7vr+lCOfrmiffthelmja6q\nqS8rI1TsX7DjxQn8c9LZRtgkKl4QEG/TpzDIqwZAwlZLMZlYDz1S1hfAGmYf\n3JUslR7xarsjGtfsYnZDn3c96upCkAL50R1oxRX3XhzNrqbsvAzfcl9lD+32\nDEyqJ5lU1euXGSrbJ9UzHa4wdTGZAzeS29M7q6ctGgbHGSj4BhZnc+NKjF+Z\nI3ekfwKTIYnkUn2vR9fssaPgQC7aJYyK5f+kk2M92wa3+54a1A7cYGFZYFwN\nvysitjVv6RWShMX5t6ef54VKReNDba6mB250Kg93ELb7DGNjE8zuvnVraZnG\n8wNM/S9rvmqceqFpeWH8D4AZ81wlBqeQEwrr/yR3vUVe8/Q/F0I4yFawKLMH\nZCgUL8laicADNAkUXfub98uQ4jy6GZxr5d1rFOCmuDm8WLUfKrKh6IFOR9bG\nQ04/Bf0jHh/5Kw4uqAtU0k/Vadb2EHapiaNSUpn/vpLDsSx3K1lxsMljTksw\nNqyxEe6kJ32cBz78qrOPU/UFNhWPA4c4m6/XV37YJ/eIcTp1NfFK3CtPcCvi\nuUrNrfefuo73LJUYNGJNDQJobormt/KhTnUFyhjc8DlcjV/ClSjh/cOtGl86\nhg39\r\n=ptgV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC03OgruQqvx/WOe5eL8gWwQDU/4EEJeXHcIWEkSDRluAiA6HOvzK8DWQ/sOLxJhcZcjrHcSpVvZ8Hh+zFl2nRgOCg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603084044_1528015550906_0.02628184474080353"},"_hasShrinkwrap":false},"0.2.1-goal-info-in-log.20180603154838":{"name":"@atomist/sdm","version":"0.2.1-goal-info-in-log.20180603154838","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"61d3ba94fd34a8bedbb5f538537ea88a2fd8e080","_id":"@atomist/sdm@0.2.1-goal-info-in-log.20180603154838","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5i6fV6el68X3UCFJTtvYnwU4ZGUQISXKnp+3b9XKQtI2J4x8LMtuVHq1JA3OD2MMQRR2+1u0aMhdBhoXkgURZA==","shasum":"e739985c104799b167f36759f16329f9ebb90615","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-goal-info-in-log.20180603154838.tgz","fileCount":962,"unpackedSize":1980624,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFA8MCRA9TVsSAnZWagAAs/AP/3OAmgUVOn6f7i6+2hpC\n+lWNxya/2VR+yLcqxuouEQS7wzjRXMuK4MWrtT1LdgQoDBU1lBCNwc7B94UY\nPJ30kNpOuQELay3UFD8qzGpCNgyQpd9ZI92ekPyCPCNDVdpVrlvrCP18ifKW\nYZ1ZfocGEiL9b7lSGOU22VtfBQ9jgFIofu5IXSIIS24WByzpuvslp+jB7twU\ndZUDYapE/T765R1rjYF0R/tdxob3bFQ8piTFBldHEULyUVou0TyzBGQLymQN\nCh+N1viLWvZ5Y230bb4BEg63gfUY0E84qBpctV7aBjeO7ze4nxDWCYWRyjK2\ntNnSzZB09JSIbEDAACKjfRltwur2dAkv3iVTM8aW91gGRqrRInrd65HrR3A4\nOt6RFrSyOfgZz/1LLGf4l4gQHgATj3Y8lYDaUk6Ufq5cPZ7xVBETBui5O9T8\nHE3V3BkPLdnjbO17EzY0VUolEa0k21tYM6uNns1eUHY5Pcf2ydt69AMy3VtE\nJTAb/u2lcG8Xm7kigiX6qYDhyoBDW0Ir7uDvEDgA7ezg6BETf3wlnApZeSpM\n5ga6DIwitlOcUGXtg+TPcCDNT/of5UXv3crHQFERJcc5/m5kBd84z5JCDoWk\nq2IEw6Hj5q+Wt2WHII66OEGiN2W57DZBhzkG3yQSlJuWTleh6fBNvOOAmPeK\nZjhp\r\n=LNWw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfAx2zOIcKK7nhRoO0hXA69K+RQFOUK91dRiiJTT8hjQIgbthpKEBusByitKorJc2Z0XBftr/+l6Wrn/YPBkJSiF4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-goal-info-in-log.20180603154838_1528041227893_0.7796155942118286"},"_hasShrinkwrap":false},"0.2.1-goal-info-in-log.20180603174739":{"name":"@atomist/sdm","version":"0.2.1-goal-info-in-log.20180603174739","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c7963cde8cf5e4ed5ee74e418c5102d53c1f7ec2","_id":"@atomist/sdm@0.2.1-goal-info-in-log.20180603174739","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9ZXBLxi2POlc3bHyKrXcsfSLa9B5We3tz1vAR4XQvRCS+i7zaUyIgOaj2wu8wLm0I6dv9CwQdp7if0XheGoWMw==","shasum":"83d49fe99bf5a3712f244a0b6082d1f6b023d7d1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-goal-info-in-log.20180603174739.tgz","fileCount":962,"unpackedSize":1980749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFCrWCRA9TVsSAnZWagAAOVMQAIHO3clThEh0DtCjw9Lm\n6d8n5IUqEONJGqBwH6J90oMleImW8QlMmxTiTfeEGml/+7qmUgWib556WPP2\n8zvV4LOGvgDmc5SlM8I6WLZ30M+bDVLylwzT4pEODjuV+/dKCeybRfzx+h5s\nvSJ4hjozjgtG1tO2vxXvuBZZp81DnRGJNhpJM//oY+pQdY11JFGgDOMKYdiy\nI9UIPFCitHtIcdNl4mgV8O2Py8+5eq95pMFSCPJOOuO/R5Gm/Qa5hk8VU49e\njzdoQfmsxMq0wTXcVBCC1rYRPRJtyU9n8/QNAX2MvlZkBMnHG44AE8tr8rJ2\nJgAB7cO5eYKTPQjjnMZwzhK4F1exyih50vT6sVTVVlm+Eo3DAjlvg5OrleJ5\n7MMCCGsWxRzk2l2KJtsKAaRY1EVsoJoeu7qn6dVm4k7Oz5yxEDMaIdXAGVin\n1aPotvktfsBYdl3a8nlnZjQwepy+aYdK4AdWuhgJv/ooOczaNzeWTrhFgSA1\nEHT2E0oSLBZh7iOq+rd32svMCI2JtJ2G2VVh6bFXoAR7WpbNYQTtPdOloUS1\nCjA1u1sBLnAijX/BE7cHKsNnz0nhsFjUYvEL1y76H4v8wZhCVgaLWGW/dvFy\nDmg/TsI46+hSnOfetzatmR3+UCe+iqa8iZb9g5WO3NwTApNPEiBcYI6s1/Kz\nxOqG\r\n=hsXe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBT2gqQBp06IIm7bd1oE7qdyqFcKcPHN2gWQCiqoSoCwAiEAu2ub4KOQr7a7p0Tb4p4DWG5Vgrk/NRBMHMAUmOziCnk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-goal-info-in-log.20180603174739_1528048341972_0.6737224444648218"},"_hasShrinkwrap":false},"0.2.1-goal-info-in-log.20180603174850":{"name":"@atomist/sdm","version":"0.2.1-goal-info-in-log.20180603174850","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c9ac42591cfe6bb4ad0264d1fd84071c27616043","_id":"@atomist/sdm@0.2.1-goal-info-in-log.20180603174850","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mqguhkGkduWMiZWsxdws8HpjJMeDtLJertT9dIgQ/J14hCrZh8r1uyKBDB6HcSzoMW079tht7iPAh2MIUtBxeg==","shasum":"4dd0f3c87b409b04e2ba0ddaa888688f790481e0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-goal-info-in-log.20180603174850.tgz","fileCount":962,"unpackedSize":1980749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFCsdCRA9TVsSAnZWagAAZl8QAJNmapC4oBz8p+m1gjd2\n4VplvFQMrDP390MKRyXAVNcHbAyMeR3npe1VKNBDd2bAKyvZ1DYzZ73+GrV7\nmSxq0LtmUi4+06KgIRBofni7RPEo4/ZoFTUZS7aFzJT5Lnh4GrmSx8dxo5sW\n8PPrK3pLnW03VbMbcPPnsJv8l+mkmjOJwYnwre46AfTnZ4iDfuO6QKySz1Ds\nkfaus0aXZIaoX2zpBUK4uPH28NqufzavRnrvErczGmBkeiWlv7Aw5znCejYb\nt7SCv5lJfXgO0qPiL+zCEVUaNLfVMptymb4ItGeiL1jWYAjYfyRspiTxVTwt\ngCkP4RABWCNMwKQxWsoNvo16/w03TqaPX9+YylmxVp0wbGcJpbii9vasSab7\nMk6F7lptKaN1O1oOuG2TqMNAsCHRR/St8P1PUuznMMGeTZqSLWvO+7jyvhDn\nN1bpGYzOmLDe7qzGPKJjCp0Rzqb3/U/oc8JVm988LRkIsWm5IbMZ4y8wdHn5\nSOpUZ3w9nK8UVv5y6M0Q0f03MEuVqsVC0R0TNY8VUHnD1NUS1/lD3KMqjWaL\nE5venMw1PTe64P+yG/nVVAlZi9cYP9Bk2eMhDYrUA3urZjSJoXfCllby32rJ\nBpL/pyNca0/X2oyqRgg3gtJyXbd2t1lpIR2cabyMif/f6S/4L5PM5r6i5hD9\ngMip\r\n=P+V6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCT7YEhtyiPmbRjT5of910HV4JBHerVVL2T6kMUai+7jAIgCwlm678TetcG3uoeFaO/+eLUdujYTUwdtYmp5BD+PXs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-goal-info-in-log.20180603174850_1528048413097_0.025733302243831746"},"_hasShrinkwrap":false},"0.2.1-goal-info-in-log.20180603180635":{"name":"@atomist/sdm","version":"0.2.1-goal-info-in-log.20180603180635","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8cd499debaa799d1449bff6f39d711b9b7ede6fb","_id":"@atomist/sdm@0.2.1-goal-info-in-log.20180603180635","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TZsMGDFrvy1u+VoD+aCgAZgPnypig6hjQ6FDjljvOEKwryM5jYLjQyy3MLfmaNmpKtOgoy9B+8Om30paK1ySUw==","shasum":"b301b50ca9af28c9c53267e3a9a6f610062fbf41","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-goal-info-in-log.20180603180635.tgz","fileCount":962,"unpackedSize":1981033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFC9ACRA9TVsSAnZWagAAKTUP/RKWaYkpLtre6An3MX6o\nzFj8dS3OMQO2PHdojRmwszcq9xD1nV9KSVG1i8V8RJELCaMdAZnMJURGXvBt\n42p0lq9V2oFryQXguhXJ9A5jswC+7U12HdST9Ymk9k7syounrygfVZErg9K3\nI5YjfOC68lefDAXmOVqJN8XtAGhFA53g/scFIN45BbUmUapIViosVFRDZA+O\nf+m5PP38TDPw8V+jQQPpPSwiBaA3+1mF7JomfEgwDrOqRID6NwE6k7e0/dBi\nq9yrMwWkBv8P0rRZDvYekmiOxKNARX0OvzY6T4wSFOS9MGu8RNHDM4VCSaIb\nd5TaTCwHT786crYRKChlvKr57pze7xV2/UdNVN2uaSLCHgTaRoFlGZzfCfNB\n4bxUrh5/uxgIae1o9ucWkQqTD4VLrTdfjnr/ibbB6tGAa/enFOnmm8S0EOqF\nWEYD3fM+kggQmcSuDDDtbji0Dw7L4D4GKBVJGSQtqWS4KhiNiT432c8LB86k\nA8T4IqWIY7DC5mu1W8nMOn20jqDZYN2VxA4hLVsXsukbEq6ETt3d4h6admtp\nBuNSobmqj0wzg0nR0rBwzb7GGU/IyTIFY8VVo+CUBO7e7oJnn/pdWoeNmVnh\nJqCviR/7pmS3KxqvE5bPPBXTwLoi3LzvVbIlCNCJD2gRSJ8V94hyoQ0/p5VG\ne+n0\r\n=nh5X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICw6m2dcdLacleCiqY0FEsZ03U07AaJVuHXRQuLEO7UgAiEA1edcSpLDGi1gE2LBEPlIyfZSO3Fy6eEAYsJ6b1/NVVI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-goal-info-in-log.20180603180635_1528049472176_0.3068793818953315"},"_hasShrinkwrap":false},"0.2.1-20180603222736":{"name":"@atomist/sdm","version":"0.2.1-20180603222736","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9c8bde2cb290ce38c219657125b0d2dff71ab7b4","_id":"@atomist/sdm@0.2.1-20180603222736","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KSrzRyFpxzcJlwPaETiLIJpeyJBHIZfe9T8/9qOCBkZF2IPRzKPjNF0NxFEStPXnP3VUu29yX1v4RVvWZ3aGOg==","shasum":"e60ed2d20dd9546abe16713efb00e65c599c9aa8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180603222736.tgz","fileCount":959,"unpackedSize":1977236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFGxuCRA9TVsSAnZWagAAOQoP/3InsRx3rDEV6yqZBQR7\nYB7H9dO2VDc8nuROpW1T90HmNmdTslBewqXQbkHQvoEFsRxVaou6U9efxSIA\nasw42zVYTFCzL3jwXx2iiPCgyNVYp5gnfPeTBvJ5mrkH5WrI+Gf9YIOhuxT1\nwW0JUE2w0Jpv2W8P8zOWArX58wLsdQWK3FKxKmVI49JjG/9q2OcxsTByva4t\n9/gdiZrJ6LMMzaCN7l3CnlFQyrex29J2ORcmGZy9zUtzqMDkntHY6NvR0AnK\nauzhSKVA82oUh1Y2LGt3bnVKJzN7732jVMIyd4UuyBAEdsQyb5DcI+1/W1M1\nFHv8/qh1CCYiOqVKsxVnhOLqhnFp7k0Qvu5QESYyY1d1YPPUorv8xtouVSYW\n5rGrk+lEdpy6c7Uk/IX9+zuKj25IhkOAnjQ8hX6x+ntWLA+DPaaRiFXsGuA9\nSvH1b1Z0J3G+X7tkIxwpAtbpRR8lUtrNYOETTdoAO4OO3o7nVQXP1OJ4wEf+\nnyP4fl3WPF0hvZtISpFyzBEL3UzSlo1pKjCm/9VjtfBCasV4dr2pdg21gqa/\ne/dwyRGcO1meA9JwnHoXgH6TqVORx8HiD6gcewFXupr3OLpR203NiPRbhUKc\nXYwQJg5cKTSUkAIE34HYntn/Qstv9iDEh382WJRmrR7D0+H+/qsTAKi+7F2T\nQNhr\r\n=FIN5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICONLqFFnOzP/dwcmdCPQfsD3td82hfUhma1/aQASM/EAiEA7mDItr2zf+Lxl/Cz4pDWibeaiAPAvWFI1icanMMTG9w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180603222736_1528065133792_0.6888591686614136"},"_hasShrinkwrap":false},"0.2.1-20180604015732":{"name":"@atomist/sdm","version":"0.2.1-20180604015732","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"21e46547b70566e16fb03ef0dfc100dd20ca89f0","_id":"@atomist/sdm@0.2.1-20180604015732","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XqnGYpGvF3FvK9hyHX4VUd4T2NEoIqcha7mS+gtlOVa+ki46FlpnmMAYCwOullP8h4u+OxV47wLyFoRzmC79zg==","shasum":"7e23342e1821fe3046a54796ac30223415401d00","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180604015732.tgz","fileCount":959,"unpackedSize":1976343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFJ2cCRA9TVsSAnZWagAA2U0P/i68/PTXAcfZ/ob+l7cF\nZp1r9fagAOSm3sHCqhx4hI6HvrhiFAA58rI9ptW7PvA7RVscpRho3uVeQFXm\nGN9TZJyn2CF7Od1k87XLBEemqP8/bbvC/lsKR31qXrrAn2mfVgPPMM7JdwvL\nIRau8n4KXaOh3bFrmLYTKW2n9nFsuDnXqu+detSFv6D/fvivWZrUSmg2fXE8\nl+t5nRMEbmEMBCr4wxNPs20/hliWYFYXoKYhhxWiLmUNf5glVRf5DSCpg0YE\n5GzpN479ogCk3yrc4JsUCZkhC1Y6ro/xP5uG8z80SQyu2OnGT0tbufT7aILp\n9iGli5lbzqVaFe0zUmRQ68xvHZu1UFgpcKWAZjpKnezCUnUixqrWOE0IdJ6k\nkYqHcxzAQnTLiYNL/XfUfdC3MkO77rG7aNm0nNAEJ+zmMhJ5Pw24pZuyOMbJ\nnmHfzlqd3RYvYRsDQoSXs4gaRAmR9SXUxhvnt7Be9pUsKllJdvKruXoYPwJc\nVzOoyQCTAndJjUE/0CIJwUfDNL/RTYTwU+Pyod4iDksndBTwkbRoUp4uR4A8\nGJLRw9WOBuLQC82bqJRuAv/ktytvf9OEH7JVjadT+9vhR9+LnsV0YG+hW7mH\ns6iIeR+aGUvf5Bu5Tmf7/L1MEPnCqfho+C5wPMwyaDGfepHOD32UURjSzm6T\n1iGO\r\n=bT5H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8xb3buV7XoOMPNAXnQ7PFVOhb6jFNe03a8xrELUZdrgIhAKMkf52/7kQ7ZwNCQA0yy7pOUS/h1BwrI1Zqaerne+Ce"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180604015732_1528077724399_0.8145861969524655"},"_hasShrinkwrap":false},"0.2.1-20180604023535":{"name":"@atomist/sdm","version":"0.2.1-20180604023535","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1ec5dd264e4f7af9dbe4584a169892ff0609fb68","_id":"@atomist/sdm@0.2.1-20180604023535","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-i380HCMbRDY6yyGYD1v3ywyaRDEmC9k2/tRYz6cB3Xy/Evhk+XfDBlT7Qk/AbUcDtRLROXvo63Hh3KUoP6oZbQ==","shasum":"d6202fd2cfe0f2e0c7d64a5fd114ffd4fccc2ae7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180604023535.tgz","fileCount":959,"unpackedSize":1976315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFKaRCRA9TVsSAnZWagAA/6wP/35vSHqbClcZtFkW3xHi\nVb7xYYbMWJiUwuGzCTM/YjZ8AWsfOemYvvJgpXXMYU5Qm9FpkVgm/u+pAKXZ\nWlCJ95PPsddX7wjE0j1/doZu3K0VyidBZVpa5eXcV/RbwW/6e17Grkw+k4h3\nESPWm0+Kz4PaXErwZVOfcpMtp/Vrc4Q/l/JZd/69ef/TqvaoyWV3Upjzjiq/\nVTu/nmF48MYkA7lO8/1Hfl9ihgoEBZk4jK7GapKzKc6aYYFVYz9WLi5Tobcu\nfrCGfuZc1yehu8S+yOTO5GQvXnNXnsraXfNu0PMecOF85g6cg8yYpO7xBocZ\n81HXuRvgi18/U+2OJeIRkGq8qMR/mcfSS5+WgDOBzz0J6Mu/Iink44OJ9hQX\niJKFyAe5UWnPCfBkfu0tfgIdggg6SKjGfnd2c3J++XZ7k+WiaCxBDI+DjOrj\nAtYf+MOO9+W/MhuJQfQqyE1QwaJF2r8l6yfazMpfhnplWlNOC4Ad9EszTpuC\nrENny1T/3AVOiZ9bGw7jaJ8lYR+lWOy/aDQDh/wyNBQ8SKDvYegnNYc9ajyT\nSi23/o/CkPMG8vJXMTHsOw3YwEXKz6gOpl3eMIG1DLUkeqklsacnhfREPd0F\nMYfjodS0tGrePUvlsW2yKxL4ahwF1HpRxlD526K8JKGi8zOMI49KHloAwT8N\nUXvJ\r\n=PEFf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeoPvQI1PQYzRdkoPgfYOvuEoSTQB5UQGiG+u3uo9ePAIgfoLYDNMqBX+0h0Wycew2p30+DVL7tkkFXROX3dVd4vQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180604023535_1528080016811_0.49267475391740456"},"_hasShrinkwrap":false},"0.2.1-20180604033543":{"name":"@atomist/sdm","version":"0.2.1-20180604033543","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"515a4d738314849a8c0ce76bb2cd1a634f3083b7","_id":"@atomist/sdm@0.2.1-20180604033543","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-258wZ6KWO7IXo6QyrSLCaPRBKDTdCTJR7fC6Ko1lv1ko+c4aDYMQz1aZ4F+vXNQqayE5PH2Vw837CJjFpzET0w==","shasum":"a82bd27d369f83d5f62b0cfcf00bd4f8ef88ae34","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180604033543.tgz","fileCount":959,"unpackedSize":1976351,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFLSoCRA9TVsSAnZWagAAeXEP/A250n8o3vEA6d33+uBo\nRuX/27yABoUapNInR/NYAupoDkA3Cp6TG1gtYg3yLhUaDaAqBULOKM4CDXkd\nq7UhUAFns4MvyFh7yqVChzLi00a4Rjh9LNEjg5jeFbLqLLdMA6yOG9TuijBk\nmI3+MNXZkuu9ajofUMBGFrBM91wuAj4oWs/fyWSD2hpRH+lAd3E0XirXvE29\nuxqzvXxh0uE/8iuKvs4psfNjfsGhba9NaxIxV1t9dALOra6mHRa9LAs9rRWc\nXAPq35n8UeUebe1kszNi9HzkYzGAWFYDkVGWZxL3eFrXCaWtlVQ+JlAKXK/t\ngCx4jfmB41lDyDiEplV0NrIqpNw0Dx8+eA0PN22N+UnqtDSntW/lTCHnKUob\nXFJ0EKmNBM2uCqidn+ywvFZYEa9FAPQ8OQ2Dja/txGZ9eeXDWq6CmBhEzQKJ\nrpT79gF0vtZuAMqtchhgeO0rKnDWUQw8i/p11PPcLbgalqxUNRvBNd7L47Zb\nrs7bHza+JLhh3q+qMWDcnbQwZfi0UdxMksuFSvh94KAZFzLBg3R0p3YuN8A2\nrJtifqyZYAYBC+GzAi/r6K6XmaR8egf4HHeX+nuCm1tEdjz8Kz+BVFxiB+ld\n8uBR63z9+RP3BeHeV4oJP5mFYBNriYJWEBqNhzFNbRex6rDQ1SPsGI/RiaYY\nDrav\r\n=AxNS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNr4UBrPtdcuOfxM4DUUPnCvUeKHkZezk4/HfeoXoqbgIgDVWoW6lzx2At2yEB9nzSLPIk8QHr39Vt4zz5+dPaDgI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180604033543_1528083624558_0.17812399433663972"},"_hasShrinkwrap":false},"0.2.1-20180604055111":{"name":"@atomist/sdm","version":"0.2.1-20180604055111","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"76e11e5dad5633298446ac29eb88d1d15b8f9f43","_id":"@atomist/sdm@0.2.1-20180604055111","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0sas3lx/LYiBvqrJsOgiDhT18JA9PhGIay7QVlftEuKTgoV+RRr54g31xMWAYxyWqeGO/mF8AKNADyizgXze5g==","shasum":"4217ec0062aa899cf8c17916e092d5113a4ff410","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180604055111.tgz","fileCount":959,"unpackedSize":1976400,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFNRzCRA9TVsSAnZWagAApyEP/ivEbzLWkEwbw9RnxOZi\nNlnBssMDC/kvUNJJlxb0RKwRltpTxWQO0rTWtbKnY5Zs0io+tdNU/ZWNnVec\nQ9zBGe+KRA44VWBJXq/9GZ3g9gz/O8U2eqn1dKP56GDXNv9ISKmmHA8xiATP\nhHflmgjuz3tLaXfiQNwQA5dk8Adg9T33I+EZq67LmYc86O9FXBJ7uIeTHxLO\nQBWtj/ZGwdk8XUVjUy1wEb6oIx/S8BUJvs75bmMv7OMvXpQVMqLMRP//zEY9\nL5AqlwcgOwgs8QbTOAom5rH04TiYU3b+4pS791FjYPomObOzvI+OlQvQNLKA\nBoZLRfgupUS6F3umm1lm3fmuFGaV6AdaHVylt2Jzk6Lul9M1yyDRGqqz5dfr\ngsaNHzttBzZkKg1ldroLoRsWenkWTAVRCRmik9kRwVtcI196FrNgjN9WN7R9\ntWYnpVpFPmylgimErMWHqJH7Kf8RmAF09oDpQZKUS8HXPeeiN0w/FPONNA6k\nVndkhNms/sVTzqhLMPmUwLgjhEarjV7QDg592/E1NiYLQe1E5khTQVxH/nCp\n2tMkJUk1pA0zh5HTvGBv5AsdcHUpItiDwfTK/Ic8Ms2MhWNMtBzldaMZN4Xb\nA/sX5i+ygaWaPeAeNI1j51GjdzE8XETbLAkKceAfMx0zSW7fvXWB3uiz8xiB\njr3d\r\n=FWqV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBbau1tNS6N7ke4pyBnsoQEgPjAU/93Dny8DNWj4LV3/AiBKnMB4/iC0o/SrQL1AiN+vZflPxBc1R0RTUIzYbVFgBw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180604055111_1528091763674_0.3579138761650573"},"_hasShrinkwrap":false},"0.2.1-20180604074226":{"name":"@atomist/sdm","version":"0.2.1-20180604074226","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2ab40a91a9ab0f4a8dddb109ba652acf338bf009","_id":"@atomist/sdm@0.2.1-20180604074226","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MK7DLcpvuAWCg5Qb7v4Mf2m51BDBX+i4hu6qzhi1LBm2/FSiOphJFfRzLXLilabZpiyC6TZkKI/WBjBkIvL22w==","shasum":"52352705a8039bf7e03422943a51d44cd9f8bf6d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180604074226.tgz","fileCount":962,"unpackedSize":1979622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFO6LCRA9TVsSAnZWagAAakgP/2dWPp6s3AA6lcdWMwGU\nlRR7tW0ZOiZ5EzvCoWMqBjsDnqddvo/EOp/rFNPTDPNHFO83ISiHvbENwjOe\nYWxLDbvUDvRLGVfFwqAj6ua/ee0bRyWRP2/e9uQqJuvqUJ4ruaAJr+BEGjAZ\nPYSds0MUY/ijcLo0Mh4IEmEY0QGbzLsxT8US4afzEA02zh3o40/mjmqXp/U5\nMnBzOEdKqOWHVKrfCs9s6jOa8Thpv8dk/RGk6GhE8SsSIgIyrtTj9ISjDn65\nvIv87rH2yc4h5wfz1rXWXS+7BbAGqUvAxe2kM1DtiKhE4cD9navV5zQAhsfg\n35mh0r3rA5Natfi7d/ZdvWGruHVYC3ypSIfs9nJWerwE6B5NSXKf6pVcisj+\nrsUE5Mymq37aKFPD/uHa2AzbAgeb/6Y7abDoPlqXzwPF3ia90eLx7eZPHm5k\nJuyxEz0+Q1f+O/fQUgfqKq9/LUjobgSySsxhE5Kq2ukWV0gzptERKazSQoND\n4q/Wwi+H0coLjPk+JVxKkEecoYls7nT2bhzMPeIVugQcs4uQiCKqBbLqZSdG\nWZDkiB6yhVj5i1ECbx32I1hPY8RNfXR3BKqqEcIICXxgvO8nuPNNYHOuyAaZ\nr+mBSthi3qizjP1JTGEOmyYE7EPex3XFYP4uT+p8WiuyKjLngURW/3ojtW1y\nFA5H\r\n=vXyj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkchpo2nbDHfF9NMtr3X9ZaN9vzn5iLW7EessneCsbYQIhAIGGSa44cd2jc+suxZVXOPKe3m5SY7fp7yMgskwN/Lrz"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180604074226_1528098443793_0.3341343919536499"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180604155743":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180604155743","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1BAVWBgx7","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8123aaaec25fa5597e96b149a5c1e2bb73a9ae11","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180604155743","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-X+N7wYj8iNxW4C5nWflLQ+YQ6JYXekMeqe7sP0r9ZERW8huGYpV9+YBfB/PdNAk+zP4ZfX9QEBwvXVo4xso2JA==","shasum":"f34ec08c93cbbfcacc16212cf629952f2d296e82","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180604155743.tgz","fileCount":953,"unpackedSize":1958143,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFWKVCRA9TVsSAnZWagAA4YkQAIgLjedTIEIYpXMBJs92\nbGzRHcrQfEygBMDmiTqRT5i1Pit5/3hSSKeg6s+JAl9gsNpBtBmfqNemuZBL\nPeKHmmUdM6Ffxrz4iXwqycHMogxQ2MzidePAp+Za1UOmkxBlYnwjOvcJ+FRq\nrUP1bqHADxDta3E3fG9spbxZ25JYtG13bCq/TRsU94t03oTOqsca7MdIFUQN\nGzaluN4ngdSap36pWvOOI3kEgFsUVJKQU8qXszWeYnoHEQs4PjFyNmygTE3o\nobdX7ZRApTM/DkXIF0+sKgvgnw/qnKZuyFtdvENTrAsbk+S0hoOgDtNMr/4C\nRh1bRkbTrus5JOR7aaIFChifSvkllue8mY0qePytqdL8Dpz0uVPxodIR1GKT\nx/5axFEpvjsGKfCaBINyKJyPu32ALBZcFTDZKsqFU5h6UefXrGziMzJcXEn7\npZ4PWSSkLtqanDdmMsnxI46l5wu9X7CrNGxnwL1MbsikT78Xt59PsrUaEB+0\nh0WFWVgtrUkqUSmJSvy8QmaNO+0q6Ot7+7FQ/T+JHBoaXpPM/ow9iNAHtfXM\nYQRJ5CLGCBspf5q4UHjHhGMVP3Cl9Teg6Vq/pvnFGpNmDG1bm0qNXhg8GZfk\nGL9TNKpaZXydTDDo+LP7F7xvYBVw5dUb0sj7kvwYS2BnLd96G3J8izmoyAXE\n+U9S\r\n=uBfG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5ZU7Aa4YCbW2jRRSC2bRVe5v9ruCT/QPwpKHCVLIIJAIhANczuGnQyNxsWFJqMV6INNPx3StrqyIbjtkGP5sv+954"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180604155743_1528128149502_0.5565115674443075"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180604212634":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180604212634","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-nortissej.move-remote-repo-refs-down.20180604211000","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cbc525a45ad4741a2f165931576debd9ea5a074a","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180604212634","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OUumuv1smZ7lZSC2jd27Xa9m9dLuj90HAxiWynIUA4UJQwyPLjH11KTemnTXQXnGIsMy6+56LYQBaflF3feSIQ==","shasum":"e5a52190c5c908ee3dd2a9e8b82db8a2db93acca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180604212634.tgz","fileCount":959,"unpackedSize":1963800,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFa+2CRA9TVsSAnZWagAAuE8QAJ2G0+3HKc3vH2M07IJy\nE2gzngrup/dPj/JgaOI26lf25Yy7Vinrr5ELwTxS5g7NAt4P5OV5vtMqEsBu\nnhMPXaA+dtxS2bhJpLOTDi2lwT5FvuMu1/Ojrxuy3ytBoxOVCDe2f5q1P/dE\n+x8Lmyekd7xGROsir3MAEsnmkCkKwH7n0u+9r5HhvqD0muE0ui0LtkFM7nfa\nn16gj7QH7eoh8ESXXdcBj/Zy1/3zb/8HbZ9/fl2+HE1SIU3UMd8U5X6H/MGa\nzS3vqRUHIewIhQbhwMa6Kv097gzX7m3sfVvT27q2Gx3N7VE+i4cNUxppFFBZ\nvRD4YQau0YslJE0h01ur/eVbY/mgSnHti4W0lifZScWyyFDVUs1znKYwNetl\nQ5y6C5pez/uW6nLSRslUDQ/9Uro/GDEpSdL6ul0mPE/pdehNK3ReOsfw53iQ\n+mcCgF3JlaOdX6sOf2QZyRjtulXSxt2A1zkpUWrXcOJSGxwJfmJth8enKsKQ\nSMMNRyJOITj3fK2mWMZtMW3gmnLMBZJYMmkC2b/GNzyTwv4UAyNoUvJlS3ij\nhKtKbpBNn4twK+yCHMYmmyL5q6s+ZgKcfZusUCHZzFoolg+nGxpl2hVXTEbb\nRR4p5jpq3BOitTuG8e7bXifr1MTiS9ZrJUdd9JghWCzITx8jX7TyuD17u5On\nC89g\r\n=We65\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9Uh9f2jTrUqOl8+RosgIkYcL9zwwqDmSx13o19vf3JQIgFd2A6mnPkydwgxFoNpdDYmi5aB5VlwzkypHwkdtMKLA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180604212634_1528147894302_0.8400723699079189"},"_hasShrinkwrap":false},"0.2.1-20180605033211":{"name":"@atomist/sdm","version":"0.2.1-20180605033211","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/S1EGATByxm","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"356c2cdb6f25c1d43909de38d0af8b5cd120423d","_id":"@atomist/sdm@0.2.1-20180605033211","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tuHrluWxzsfb2cE4vS5J+X3DefCH65+FE/xCf8p6Vodf5y4AYzThGgleCZR3kAptwE6qMAm8b0eNxlB1F7WmXg==","shasum":"6f01b6df6e45f39c5fe8c584887bac10d1afb970","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605033211.tgz","fileCount":962,"unpackedSize":1980568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFgVZCRA9TVsSAnZWagAAP30P/R+VTsaw2AfbPWTEyU0m\nw2QWJUVGU/GXgn8D3J5wb3wv0Io+J9u01HpQiZ02QDKqyJ7DhHlnkrw//1ZL\nm1/2MnwMt/X15/FlUL5EffRyCw5qrbdgg7E8Jxe2i33olGK4xkO2MmSlTV6n\nwhw/UuhISzWaOgaJBdmq3FtgPO+zOI6csYa0e/PGZzTXEFR5sfJBMeVZbX9p\noH65dt5rSfN9ywuKXMnpuTRVMqivrn9GLwdZZXKs8j2dZ1rEfEJPNBHzuCF4\nLGW4Hamr465+KQ4O964erGSAMnSRH939+RndBNTKiYQR+78Wpi3SNJ8vklRs\nSYtxD5WF8dGlxMqBVgxQddBCndwzN3+Q05C0TzZMuxmcOqJLwKpwZmEPnXD2\ncqvI5SENgya3LYs0J1iq+Adqg5WnJklbEN6Tf44eJOxxUFvC+046auY4Vs9K\nFMTlcwNuSj6pmWq0HeUqBqBAAHABktQBdMSdRjzt4D+Ckjgh4EAxklZVpDfz\neWTnQmYBwwrys6HPWCoO/EwJrgKpUiDYIR/zrZ0tankAwM5sSJOrAFbS1UTp\nxxCytUH2bqFNT3Em9V+LQmo+mjbcnnE+mCEr0WrpxSPPHD4LLArInuk2Y7vP\nsTZ7aud2kk+j2J+QyubKFwGXIr8UY//ln54f/qz3FtxI3WRxtJhTwQm881o/\nwcb8\r\n=W7iZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCs5cxYDkGNdq7sQviKS9slk9/DsJJEQfe1jzG64fhfAIhAIL05apv6kZueh1cvRSlO0gBnsuEFq5mZEsd38gWEWKD"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605033211_1528169817755_0.2590697626029177"},"_hasShrinkwrap":false},"0.2.1-nortissej.move-remote-repo-refs-down.20180605033552":{"name":"@atomist/sdm","version":"0.2.1-nortissej.move-remote-repo-refs-down.20180605033552","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d0a29f3ab8c750c01894fdf09b2423388d1cea6e","_id":"@atomist/sdm@0.2.1-nortissej.move-remote-repo-refs-down.20180605033552","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KNAzzEYjQxtpATBKTVVy90AHlB8Gs0TXJeh7H+bgzAQrDLLtCtsixrEU2vus9PPWbwRcSvbEq8XTLU4raKbQrA==","shasum":"6c6d30f1d5229d6a08f9f93fc92d79a4a596457e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.move-remote-repo-refs-down.20180605033552.tgz","fileCount":959,"unpackedSize":1963763,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFgZDCRA9TVsSAnZWagAA01kQAJXmOEWqVse2jT3Z5I4j\nf2qHGW6Sa30Yo+kIw0TzXQV13tjFwVEd/L7JjOOwDUjR7auYLfQP3po0dGDx\nqVSBmVLNzETQQVScP2PD/ZvSdrF3Aw/bd2/4JUlmzNm2z2xU7Mo/thHROCvW\ncM8VhgX8hCN9q3SzqxhhoWB0+wAqtyIkjzPG8sWceZ9LbVK0CqTOLefJdqyl\nyVS7tnp7XAaJ5CokhCjGBjyxpmdf6m4rHTpspLgQJF5nEEmEcnCWfA1qbw+R\nuWQLzKwQcFHZBE5U0viVUsrgqRJIvnEHQquI26P/Cr2hC1De0r3bEO9vIT3i\nMCNMupJ6t9x5hHVuhTcCTBSKdr+9H2b0VmxvbxftiBob03vyo06VWNhbZrF7\n9fZTjirn8HDbp3DR5YnMKBuTS6eMPy5FhkTzWM2dhm1WVF+0ULWAdW3XIaQq\n7K1riaealnzlDfvISySrhNg/QA1GjxEcm+1MzyuBa5Ivcrc/VUu+CoLEfCwL\nn/e+TEfZh8tFje/TEJVceN/JRsTa4D0aJ9jMp3j/7yJy/z6nKnJaOuCGkfLq\nAH55Ii/uKCn127uFwC8ZAaiIy1YuJMT33R2qQPAQFYKj7wm4tPOkgwx3fSyv\nrI5vkV0UttwY3sD1ikY/8/ywpPyxwIvVe6DX0UCxy+7g1FNmV1iZcX4Z+a/d\n3QNK\r\n=uacZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB21CAEcKGkLxO1/bxA9FTo01UwIo3qKI1OGdKuzwMqvAiAs9pRjPDD6wM6OzkjgaceV/kNO0CDrIuGwI1pH6wtALg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.move-remote-repo-refs-down.20180605033552_1528170051491_0.17363468599123055"},"_hasShrinkwrap":false},"0.2.1-20180605033754":{"name":"@atomist/sdm","version":"0.2.1-20180605033754","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ed19610981237b9eaf1ed429265adb8fe4eb5b51","_id":"@atomist/sdm@0.2.1-20180605033754","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qwgCYpNBSZA7KFVhNukFoXEtIBnJIEV6wqp5LSuAynTEUE+4FL2QnfwdDKA7nH1hLgquTiByiVIIwXEAjiJTYA==","shasum":"029a3087777276d628a11a5c18ab6b7d2f17abdb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605033754.tgz","fileCount":959,"unpackedSize":1964672,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFgbZCRA9TVsSAnZWagAATIcQAIFAgERUMJ1LsDfHQ1UF\nHBeidodO0t8380xEtq9oopylMtuKMbBZbnfHu2joovgqo8kuDnLSlxjtMOtp\nhSrTad3L6aql5WTReqnrtdcjcMR/SYwXXovss5KNU1mg+9kv+5PI7ZcWr1se\nNcqibc3EIEO5dHKzAFfLSI29GPv1n3S0h4J39jT6M9d+1AP2Li7hpDtS78+K\nRFZFemJQcqudsQKiI3SHRnr1hlZlbTFf2pny4N1nH5NehqvpB/z2JdCp1mzg\netKGRI1ZEcw9jaFVagY3TQS6JmOPZPmvB4HDKGN+y7JPby4q6dluHj/fqKuj\n+oIbbvPBd5C0vur4d/fW7HRmXVElpUhGQ3zbjxQ2q2augddaAi6rDJB0YxhS\nO2ybR0ZL1/oTuVCBBHJrK6ir/iXCBvM6aCujDCvXBrWPhTn3c48Btv0Cc3W8\nssmRgFomuc5vBIWlBoZKo4OUgs6QsaQ6Iz63OK8X7myjz6x8xcscTgs4k1ot\nbTI4R/xfKJSGhIyjPqLl+XCAkjU6OahwKadurp/CqBQWxWa2jCi6+nVj/QIF\n+tgW873htkm4TIDFtzHSGv1LYBtlPs39wBCjCRf+y67TcGPvNXRdyDoAGRIS\nScKawT/umB4idgy1CuXFFWakoaL128ZaPL+SMCvCsWrEoXrlKGNJ70R0NY/E\nWX5J\r\n=8VTT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID7GBXni+Xx3x7lvIHG+bnCZMu5sfSftU4AJKvEDe66FAiEAvT4tKOGjl7wbBWXfVwYnbnV4mL+XqPTBq3RqRLdXeX8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605033754_1528170201589_0.36832667699782573"},"_hasShrinkwrap":false},"0.2.1-errorReportingFromGoals.20180605034944":{"name":"@atomist/sdm","version":"0.2.1-errorReportingFromGoals.20180605034944","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"32cff491292c66ae4838a7da2d54f5b08869d310","_id":"@atomist/sdm@0.2.1-errorReportingFromGoals.20180605034944","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-CuQeNLJ3cnXwNtQb0IcSywl5YjodOWHiemOU/DtajulLrcDHpF2IVNScKRha3xre97atYOJi52x0YL0uHtvNKw==","shasum":"238906f0510a001f3ed49a0915719258743412db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-errorReportingFromGoals.20180605034944.tgz","fileCount":959,"unpackedSize":1971073,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFgmDCRA9TVsSAnZWagAAUEoQAJdeezpE/EpEns7ndAhA\npmGn1YEUm3QMyF3gYnKpohmX1xBKrDN2MAdZwnw0T1xJFar18LBn9KxxK8Ec\nGcKEK37feWM5Zf78DQ25uPgL9Qle3e1sp+1iYmKSIXh7YAIjIE26HY7ilXYf\nhc7SqoJsOAJ0AUMzBzegR7yueOAFNPx7v/ZEJIBG9/JGZqkeJ61kA4K4CUGH\nYmj4GDGWfcgiAqha885xEsFQc4XoWNYPBjL3JB/E6dvibJ1IhqZBK7Zp2sEb\n2SbOzNGtWc946/AvBG8Tpm5y3+jLzmmAkcyOafKrWqawyBKGIMBcf4gUV4g7\nfs0CrjfX8LqunDZjkwRh5el198DFpDf71yYeuHu9ws6vUiC2mQxihkLaLY2r\nGlJI7PQ7qkBMOr5G3NzW0DvRqwYprpReCPW/2iyyeoBRdygqq9WhlGvk65mk\nz7EK49WfSRTdchUgsqKL1f3/eV/+0l1q8vz3lTXgwS5ZEuP5mmHaXEfUO7bm\nNWDMMIGoYCYDJK9V5T+0VcHnrS9uptev4BDpWihekbYcp9xggBV9o5VB/iBL\nPA9i5/0FZFwYCwF86ot0XgXkJoOxihQqRCswZKTrYvujrr7e2R55puAGLlrE\nDcAR4VN+dLxJY89ylN/UG8HKi4YM3CD3WtgExkxaeSOH/K5Dfg0WZ+/WqPxj\nYZkg\r\n=Fa9B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE6NPAZkeLeUx889iA348oa4JGZNXrTV1zHJwEqJBIa9AiA5FrWLuZCl+MN+fF4h1DE3NiVVPiXQPwSUbrxGxqXjaQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-errorReportingFromGoals.20180605034944_1528170883170_0.010984746840215776"},"_hasShrinkwrap":false},"0.2.1-20180605045818":{"name":"@atomist/sdm","version":"0.2.1-20180605045818","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0bdca1e0667876e78d3c1df6807d5c30cd228ed7","_id":"@atomist/sdm@0.2.1-20180605045818","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MH+i4RE1pzZi6o44eoCOqO0+29fP7fUO0v9zKINEw11obHPCbJckxiQ7DHxynz9ZHzaEUWQ0qlz/UQP6KU+q+Q==","shasum":"927ae8a95c4b520c08f88ccee931999284c3d850","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605045818.tgz","fileCount":959,"unpackedSize":1971049,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFhmUCRA9TVsSAnZWagAAZcgP/1VFn7aWm4bB5oBhem6A\nWc71b1Uum5ApOW5k9AHxuwSs9p/hduaqU2+WtYdBkhFGqeAnHo9wvxxri855\nTzOOCHvSPaOekYUJhGft3YCtjYGdAkCkuGJA82OZDXcG/PrIhpubOr/NuTDM\nNhlBJGYTB6N4UJVuPcjJ3LublGk2DzjVSaPnS+k1LyHfidWD7s9BV4ctEceE\nTOtqS+ebFOMQWt77snfw3KPNg3pncXk/hRFxqeKy8UzMtpuxZ6m/lkchIVyp\n9zTdtINacKRCjnbE1/wsh5rvGgdjC9455s5fVkD4Zg6B99AzKe5jo4d+0OzG\ncTsf56MI60UleSS0+z+7yPb4wRA7zhdLJQDxWGl4QEANURz9UyPl8ecszG68\nuFqAOUOo3ufzvJCgMsVUtRpNtBTFNC290+P0h4EC0kRZJjs3IJrPnkKMJkDs\noWCoDySz9NvfPjOYUKrFVBqDGuDbjY/7fTFGMjRZYhViA3FfcS3mirsDsIAa\nK4R5DkwtuZYV0HbwRxebK8Kd/57+XQJjxK1GxUg2rRedwZdga4NXOSBw82Ob\nvCHwN7BDZaQnHuA/h0dfnyvOtGXUfXvXOdfkG4AuDn0sdv2jb6x5w+tWjj9f\n3VgDjgxbMwjGyeAfx1q2z2heMeaQlmv5ao0BdMw7mVta7MO7JB0leGinwm/d\nR/IP\r\n=bLlZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICgRy85VUxv5QvECSWAcdAyVaYrhEkdSEc0CyJXgIB02AiEAuPUVAluI6oDTcnq50GTF3Wc8dvi+lteeuo5+s0oJ4+I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605045818_1528174996677_0.3455115211049131"},"_hasShrinkwrap":false},"0.2.1-20180605062502":{"name":"@atomist/sdm","version":"0.2.1-20180605062502","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6c281a52568d0a7d7f82841bdee59d32fc78067a","_id":"@atomist/sdm@0.2.1-20180605062502","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JOt6xd05R1c/ozFQPNd5YEHhyQ9nG6mEHo5sb0aY7c9pEIv85b3Oep0cTYRCh5gwJ7rsJoWs+YPEhqIElvjRUw==","shasum":"e7e0dfdfc4b74be966c2653af720f7d9c8861438","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605062502.tgz","fileCount":959,"unpackedSize":1971128,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFi3lCRA9TVsSAnZWagAAOlMP/2hUXzhUqVvNwbMuraQI\nV1P0SV15qvUO/kfCPHAwwwp2a0kd/QL2NLhamGlrAuwWjDcL/LcEjJBOForX\nFh4PiDLRvqdP5W+HLbQqYJfU/jWsKseIFCGSzIOANDF4VpKsJR06ziNmhX41\ncIdbq3abxhL+XtDoat0Dmn8IWk1ZZUUBGfc4sqKggB3RrQy+Q+dbVGXzG0RZ\nAw3EOVPGemgPb7rItWVkcb4gWD38aOHcEroDWbriJPQP95T0R/zv4UAQI8U5\n9hggR/NjtMj3EhF3kLfklUYG4H9VnRulBgA0aIXd+392GpeDSFNZhtZkPaxp\n/3sXfCAMTKi+6J0an+poBA3fg5SRc0AELLBkKHdY3arrqex2UWp4YtllkNYM\nqgwOCvn5DHhf8Sn9n99m8Q0OSWyWZu8vmb7vDVNJfnHTYkFS6S6x1HtQzvoS\nMhdYzgmJy7sHYKKAGbbYDEXzYqXlquiXsa+OAKK/WooWUolRAGvuJ2ete3Zh\nKig+SlOxf0+uH1PAq8AfAcsW5pW5SfUuGcJoKBIVyLEA2fx3zugrgnYn1dIX\nQ92ELPnGPrKBmYlNCUhLbcRvCH3q9TqU5b1lIrI8A+hLkVTiPcbqU6UxDRp0\nY/1A1NDj4amHvuOOFwZydsZPWT2ZFW0PiAphnjusl5XKI16ITDNxxL3+hUrZ\nnQmj\r\n=qcZI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4pugMwaTzGDlpKgfZ8WzyAWANdt8THEICOfzkkMazbQIgJh2RTknsKGNmj0nQz3GBvJr3Gj7vED6HyQ0VKl4WbE0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605062502_1528180197158_0.9025201728328993"},"_hasShrinkwrap":false},"0.2.1-20180605065906":{"name":"@atomist/sdm","version":"0.2.1-20180605065906","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db3cca26b60037f457304d2f1be2602d693b8074","_id":"@atomist/sdm@0.2.1-20180605065906","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9UEfb7NkXoUmuLeoDdF0PxtepA3BjfvCTsKTdqovx/LrDx3OAm0MQR62BFAezKgtZQD3TWhkDppmPW8BvolZ2Q==","shasum":"9d0d3fb4cec1bbc6097c28e2c10f1c1ac7df5c68","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605065906.tgz","fileCount":959,"unpackedSize":1971178,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFjX7CRA9TVsSAnZWagAAQ7UP/iMrC+7WARHWm+z4f/3U\nXCqWv8iCWm5+4c8hPPgzO8rWro8bgoYuFOGfV45yEAVmo/OB8wFtMqJJw8tg\n6e0o89VmaDJOxnhXxHVAZfjiGAG0lsLWP98GFCokqwNiD4tP6G30b2+WbMr3\ncEfoE//M1OYVqfzbXt61q2mQjlPLBQn6MSYrhF52Iikk0qPtaotdgPBWP5cT\nEOrWEYivH8MMd/E4A7qgqvMel1SkjHHAoy2+btf70Q7fzxnUFHeuo64MqlMu\n/RLfxQQxMTVKY2vos2+HAkxtWUy9q9PoYgDGdwrE9hIRCQOyL7uLUrpjttek\niKFevIB32VQ9jR5Mu3nu3WFGVmb0vy//fK6Q5BSGyjRlRUwBM2vDd8b5AXax\nTpVyBmvSM40oBUkGJ5pjxsUuADxiWvqvX4oHavbLfqMuNTa5po0jVMunF56b\npg3Iuz+NvHSjlJfEn0kmNibKNDVt6lv33fhpURdgoSsmS5Z5OboFTLOEGecD\n3xQvBPqcD96WBWjOq0clyt+0mKpUclTohxQ/YC2DEKv6ML+SrhJ2oWloLXWA\nnaoeMYTpN8DHye/EJtygRT3ErBYCxevGBlk9zSRHWT32o96pbmCfIWFFMgrp\nl8JNlqHnf9bS+of51YGVrm5pLS7rPMbbwkspCw+CuUQMHp7sFmAzXxHH7IYt\nR1he\r\n=OtHT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEHbeH5jsGg6uC6x3d1NrAvM7j0ZGAdL/UoZZ0y/qSJ9AiEA8YKr/ZdrvBl3ybFgOrEKra336Oixvr4Fh3mC8Pzp0ac="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605065906_1528182266361_0.3493996118203131"},"_hasShrinkwrap":false},"0.2.1-npm-publish-tag-404.20180605171537":{"name":"@atomist/sdm","version":"0.2.1-npm-publish-tag-404.20180605171537","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f16f067a7a5570ff593f9da8d3b00b024983ed1a","_id":"@atomist/sdm@0.2.1-npm-publish-tag-404.20180605171537","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-E5zOd0kIzvGY2BmtPGaueWiQWaYbTiQXPwCAHEjkUHX+2gaVzGr8TVRnCHo1ISM9vX/PhmnesnnVf4rwwuvFNQ==","shasum":"08c352bf72a7fe12a4c9715373cc2d8bdbf72975","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-npm-publish-tag-404.20180605171537.tgz","fileCount":959,"unpackedSize":1970983,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFsaOCRA9TVsSAnZWagAAFngP+wYHslkeTv2YbQ44CrAn\ncV5bkMPnFUE9lzyVsTW6CIDMehr3XS+hKL1xXoZFm8qQEoRh0uAoQJnKN8aw\nlTSbTpKQum1OL7J0JIJPtnG0L9hkzTCrOc8yZVuMeswK9H9dMDNLbOZLc5wd\n+FvU2rpNafTnG6hKtNEXJ6EohEtV5RWEZlTlFd4R4E4dDdzZIAiYgbmWfLsH\nLjKIXw+8KrO4S42smkaJAK+0CjS2dNp7CI9T2qFeo8yt1+T6i+lxOeaiL1gB\nHBefRcOwH2d+nHjhidBZIMOh68+JgfA2AypeREVPYXVsh2THxuZH+M/EQT3t\nrQoSBk1KgXpMOlfvmyNP3U2zgTPRdgtpNNdQliqxF1meJ28ANsp9gueCPow/\nEvFzIeXu8DPXkr3q8huzw5emOjFCnuWCqrn4cq25zlZn6u3yU1VlYIEYkL+m\nVntW+x41xwGEewrSTAcv0VMGolIRKRuphXg5h3064XJeGiY6mVFVV+LirC1S\nSGuJc2kp2BTcbkHyeT8Lsc3LOH1T0EeGzER84gV3f5IZjdc2q9Hj+/UhvGFd\nPUTMWxjdML/gjnhYSyrf74St/sFLXtlOdlAT5q13zQLkcBQnhIl1opf4mQGg\neZ9S1nASS2jOi3EMVfi9xXubJxF4756Sm97lQBKBW0ZAc5+BkxAJgwmosyDq\nG/tt\r\n=XS6f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCG8X4FdiHsde5RNpyJcW8xbFSoIvsDSvKuoNbH5++4awIhAPEj07mihpQEOIVz61OE43pAgPE3AgzY+K6i2BXF11b3"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-npm-publish-tag-404.20180605171537_1528219278554_0.49880833267122315"},"_hasShrinkwrap":false},"0.2.1-20180605180909":{"name":"@atomist/sdm","version":"0.2.1-20180605180909","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eee33b89c4412950504e213e8aab72cd04964995","_id":"@atomist/sdm@0.2.1-20180605180909","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WUKqtm6sUlQeE1TOOHO7c/PhCkCyoXYjqj50tQqHHsN8NcUhVlooks53ieQ9y/2wJloEXnGWGqDZb/oyogwMNg==","shasum":"69742d79a21f4b3c6e7a5ceb45ea537f803fab62","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180605180909.tgz","fileCount":959,"unpackedSize":1970963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFtLgCRA9TVsSAnZWagAAdK4P/2P482KTULGLIA/AXq6c\nEvCr73lvf2UiFAeydVbcVXjy8vDae+2DpXQQBIwqBGhrS8D70ZFiXwhSBboZ\nnMmJ85gJgd4PugzI+tJdq0Y6RK9CN1dnC2LT4OgUO6ALvKlwHeY7d8orGm22\nRY60vQqZXuQJUqtXF71T977ScHHeVtQ+2M4IO9AwQC02DfjhpdwEnz5vp7In\n1kt+QKLOCcY0gZVl8o347OFnIDXb/1IMQcxnUK6UhsZHqN/oWRBAgYfujpj8\nIKn+SrIq0YidOFlnzKOnHzPa74bO4Vd4O96E4SCubyOW02uTEe5K3Lr2R8n/\n9HQVaMiZwGQZDpKQ8+H/yktDmpyeOo/xYTNjhGxTkgLUoB2Q2vPYJlBIxyHD\ndCh4HP8P/rf5G8KSixwdCSrbZHkxUZWT3PrMaTP3WB3wVLm7b+Mhp8oZZKOJ\n8CE2jlduqaE90MLI3zowvnyNZ116TjzfEM/meYzCMEODT9vvsywCGE28Fdd0\nRUcqI10fkklBVKIZ5GO5MFrRs+bxwt0963iqzqvU/K2CxFIkdr5VmpkfCE66\nwZB5fI3+tcEm6aaQJ7idCngw6oN9AMEcHGFYfOLmJ+jliZv+nqrTYz1Sor1i\nyaDQ67EKBMeuDKB3oevnnK5cATp1qebKl4YnstGa/F7ij+guwGl5+bqNulXP\nbxMD\r\n=q1T9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID5Q6OFYDwBIFj51wYiUcoaQQQpkFx1T1eafHYxTDj7dAiAaMoHNFXAjWRrCjJw1ae5T74DHy0Q93mWgPU9J/Lhfjw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180605180909_1528222431983_0.1340442072987027"},"_hasShrinkwrap":false},"0.2.1-cred-res.20180606003341":{"name":"@atomist/sdm","version":"0.2.1-cred-res.20180606003341","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1d3e44ef38e1fe6287f0b17a0c2d68ba25c7f0d1","_id":"@atomist/sdm@0.2.1-cred-res.20180606003341","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-O1rV1wrObu8G2hxqq0JgQMKfZjX427qkNVfZrZ4zynEyg/hNOqtH5HyfHOtYSQGZZwF7Wq7a96Li7+GkvOA+GQ==","shasum":"f8aceab79463f1cba68e121eb37d63f2d81574ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-cred-res.20180606003341.tgz","fileCount":959,"unpackedSize":1971306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbFy0QCRA9TVsSAnZWagAAnsAQAKJeSvuK6un4OQRknWVF\n+Xje2Ys33FkLPxziNt8l7TRM+bTVoMdxJ5hIOF+TyEZryEdmoS/NZF00qM2K\nn/WJXlMrLiiUGuK7bJgDSSHiTSgSLpiBSLSxaD2mn+z1q0mFidZUW3MxhTrg\nplHdqUYpfeg068njHgZ1iboUCtlAkWsYA1VQ+fcLgIZkpS5i9O4bI/zVcLv+\nsMLQTHK/AjarZgk20SJqHpBLLXICBsTnVrXyIcraEQyuQ4Qo8446b3HQBoiu\n9B7HVW98UreXl6WGZCmXaIBXUQ3hsvpjHs2aGb07UW/o8A/gZqHP459YWNGc\nHnTa4p2UaU1bRT5oGJmR4tifeakePzSVTIildoH+aUt3ZmpSK5qYkDkeIC2A\nqjlMuql/ZB3vHC26Z4ARsxYdkjhBo9t/Ta2aM71rsEURfbWtWLulihQgNG5B\nQMPH+oHCxEWa+hDOksLxKeuM3zk0Kf+W4/cWvkutBaamoYqCIDG5JkgDrTQK\nduvDPNSVM2RI3urqiecKOKjPISNf8eMnNBAXrD26eLNr7jCLHEVnXCf5KlXG\nSakDvcQSnlLhuQREO9bfoU0wv2pM81vD0wzJ6FkXeZJF2s4Y6VEiqll4BerN\nsrJGKVJDHzRx58f2Jy8+PGpfkA22au9zu6EIwXdmFLyAhd4k30rDfrTjEu5p\nqJe5\r\n=Dsxu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwQoCGmDDlh52YUjY7Sa+9LBzl1QVHxcVOvGDYnnxjCQIhAL8TSja+alwc3Unq/djYfiLUzHZxIYXeVVfIq8AK7xIa"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-cred-res.20180606003341_1528245520047_0.9898549361177977"},"_hasShrinkwrap":false},"0.2.1-20180606044140":{"name":"@atomist/sdm","version":"0.2.1-20180606044140","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ae17939b4e1987121d22aca71da7d607c6cf7c9a","_id":"@atomist/sdm@0.2.1-20180606044140","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rmRXRl6ZbaCJDUC1Zczad4KgIE+XbD1F7Sus0SSJkIAEEWC2zox2JQtr4vyTkKatkS6NA8G72bNVi+rscyhb+A==","shasum":"ffcefb5a4d6665c0c2527514d6d485e7c7b14a53","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180606044140.tgz","fileCount":959,"unpackedSize":1971606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbF2cjCRA9TVsSAnZWagAActoP/2jlgiV7AECe7t0y5D8b\nWlqQ7KYW7btFSnL0OhurJadERFLivcvJbKm5+bDSLZ1IWlRBeWlb0IA2VfM7\nT4VvttGzqJ1DTalTh4ZEUeOiW2ipEdGE5BOCZDWoIk1ejJaQ8wzfGC/Qfzcs\nIeuFkozrPsiunzdc5FoQNRpXZDPM4HO2kn4AomCR7KzN8YPyPbe/uZq4qowA\ndCUGIGi7lshkTquXpxh01f5axvcDaGQODbxBlhJwfOltX67s1Qy8vq4K/XDl\nKlksyBUHdOcx7Wqn0wDOy0uUTUdOCfzS7dOifL0l9oilup8VnTM9doNDbsZA\nPXH9IUDrtdB+jb8JouwP3nPi4j7VdIAxvT4R20uijbPOGEdgAuaaZVEkhOWz\n9wBq2rUGPAQ76VaFakwYIsDtBBU/aANXny+pbXxh7I9Frrus26j5c0D8btYS\nwd+/FAUZIChPUrpoLTLeG7Lp75wI84wDoiZ2I06EcH6CJSbbnsCnYxfCmnUB\nr23WCPXhV39vmn+Qqhza4G+WcO7p9PHo6egQLPYTaSpPffN/fqBtGmZrcuZh\nkR7fWf/AIMUfYU4Gn1Xo8XmtReCcfBW0e9xuJE2gWcvP2Mzi4DTM+xS7PoUw\nZJPIJnYX6Ksc9FDeHZIvIYdPtKaBIFCTrVghI7p3DDUck30G0q/Cl9ooFw/g\nBDjn\r\n=9hHo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIALOZyZcDVyxStQtVANXQj6Ifc/soUnF/C+xRTukwLVsAiBiGMVACFy1eisTbdiinXpUbZqXV7pz1h1A25byJTLK7A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180606044140_1528260387317_0.3481775585165787"},"_hasShrinkwrap":false},"0.2.1-20180606054624":{"name":"@atomist/sdm","version":"0.2.1-20180606054624","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"97594817b26d17a13f5e23c47dbc055b2b29c04e","_id":"@atomist/sdm@0.2.1-20180606054624","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DrFWzCFLl7eJmM4Ex9bg8jo91xZ8PvJFDfvevPzMT1NJ0zK6/pDSRK/FM+5eW8mRv4B0rKY/WJtYghtCbPHI5w==","shasum":"5cdedd75a125822c8c75a95360690fe5ce41672a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180606054624.tgz","fileCount":959,"unpackedSize":1972209,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbF3ZJCRA9TVsSAnZWagAAWcIP/19t7ViMp7ru2UjPpt+E\nAJ936IpVtFrwg06XRv0JqAKhw75hJaOxY750k7IRU1RvCLgMkCz0GKmStW+R\nZhIpv4zbREsCTWNmozEEz/ZIiNRurCajAQJmdTtToU02RbLZBjTAdM4nrDHz\nJW3THCZGfK/8OC02Ek78v0i3cXZGLYOaNv17jaqZuWrWbDr/g70kfJGQlqPJ\nK60a8JWdatZrIX3Vob9Yns8HESJuyWO3udZTKv0/SeJ/1Cd1lfTbVpvq53XR\ndOgSJrK2g8825XseNGuih18rK5oHa5w3/uBeZANgSfcIYpgEpnpoB64IXHcZ\neCqb/oj2uOYTsm3H3LaHk2w/eni9CfR0/Y86VDW23xm4v+yuJbOsJ4I+PhEs\n8w69qpHN63vLDtHg4JCy8t92EIZcDsQQuabVS9FpbmNRWPqgEZUxLFkC45BR\nVyxkPPd0k6zIcQ66g1m8YCwrkYq5HOx+qejINyzW+ODgDWwbEgipm1Du5hZD\nguzSv7gek2FtcJrZJckkn5HHz/ET4FPC/4THOGeWd4Bt5f0JSVWdtPUS71xF\neKfUmReLmKrdcRWhxVYKunQN62diATXZA7LX6XMoUQBjC58q5mSxb12dqUWM\nLHn+XPOo3eS/zqjBs+LeMJaaBWlkp6T/KK2e4A95L6uUY8P+tULHskLF8tn4\nvbGC\r\n=F7na\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDtQ6x180zxYVQ0DNlr2AxpxPO3ON51of41igE/ZLdoFAiBW66plHoIY2i8f5ZVCEhU7Q6n4TTDhp6ZAPZ9fHTojNg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180606054624_1528264265502_0.5024693203523429"},"_hasShrinkwrap":false},"0.2.1-nortissej.tag-as-branch.20180606180606":{"name":"@atomist/sdm","version":"0.2.1-nortissej.tag-as-branch.20180606180606","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ef9faf37100a8b4e9e9e48ba29b59432292aeab9","_id":"@atomist/sdm@0.2.1-nortissej.tag-as-branch.20180606180606","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-venYGJNBBOnCOBu7U8ror3CCujbGih+AR8kTP9hKHCLgNcg+rA86oeFNS9uDTD3RlnP3ANPa26hPVvJR1JqQqQ==","shasum":"f4a3fbdb20f15e85c79901a01b1b7cc205a9d4d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-nortissej.tag-as-branch.20180606180606.tgz","fileCount":959,"unpackedSize":1972693,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGCOfCRA9TVsSAnZWagAACZUP/isUtxcz+tr6gB2rn5mh\nbgJzfyjhDWFyWvN9FhvaWUuq5/uxJlswir7XtLYsE53WJRCU7L7MQk3ps9WL\n709AaEBh3XNNr4Wh6exH5xYbL4eOkLwkVsMF0LUHQdCJrXF/eRkj/f0f+kkI\n1bbmHXmwcqSm3BRIzuxEnIS9jGJv6h+ZmG1Y151qTAerNnq2gHp/YfH4TmC9\niC8yZyQmbNo+NgBf+Eb2uSN2JETLwXekjkl/PAqs68X04wDaPgc9s71tF9a1\nrP+jOLJWdaf62whlaFzxDkY2IMo9kRW9JElY1DMjjptGuvVekIo5bDj6CLyv\nWx2S5dC9vqTPtfYeeODNA8vzvy96ChXftJXkt/k509pLhRjQT+6oRbheiDVS\nFhDz1fwqLGd9VmYL4DWE6qp0Zu22Aph4Aq5yLPjXUNd6u7ysmxx+HxB6N1Te\nJ3bRJN8WvGRk/gLrGtlS3ILhQ/byOeJ8jrZ5jVzJH31cn+C9IUc/f+bVCVg3\nnUsPY3NiDDSHUDkRlR9u122qlvbJGNRH30d+JyS9j2LBGBtpsBQTWK+NbU7d\nXIQqDXkkHA9repC+tCvd4DhsgH9rmHshrUFAiU8fAzZ+lMKBPDTQmTO5pf7O\n2fYeK1Awy9OvxfKuytStGljsUH/IpZ8mhJaVrfaa02ScwalyXI1GHeRfIJ8h\n8cVU\r\n=S5oI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTcOAz5PL0t1BMYJnFnmOCaCeH8lEE4P4Huf11RQbUCQIgfvEml0xxwlmB/gRmtLW2wmOHFKf4fPNSKAuw88A/7Is="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-nortissej.tag-as-branch.20180606180606_1528308638939_0.9959701445081706"},"_hasShrinkwrap":false},"0.2.1-20180606233508":{"name":"@atomist/sdm","version":"0.2.1-20180606233508","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a0f2966f9004866f2218c864f6318d885e3ec68e","_id":"@atomist/sdm@0.2.1-20180606233508","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-p0uRAZzG0VjPNlDTeMQ3MhNztDzw9lHrROj+OQ5zdR9HeuOBrejGwVyq5khqkks9D2fYDHX7KxKjfH0kvybiPA==","shasum":"87a65ba7465ab343f0d8b671e9900b2449b3974d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180606233508.tgz","fileCount":959,"unpackedSize":1972308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGHC+CRA9TVsSAnZWagAAk2YP/1JrRb7rGSI1lhhPX3oo\ne1FRQeblMAVtw5mBUueD908mMVr1y7UCDTQzbn+FNc5zvmvuB27L81rmGYXI\nrJl64G4o3JU2RZSwcCtNdsg6vYPe3XunGjP5DIJjD/IeDphdLtCRl3oVk5VN\nuFJOr0r6h2QbAG0cnD4rha0MeZWF6QvYTeJlanykZLuj8OHbymnBgzi/8gZT\nNa/sn5YyDkQIhJ6XrwaI4ZEDKgiHSx2UFzkWHjveKXq2hfizUlZhGzvZa6E4\neDfJTX5Ta82A+2wbxWMx8ffUEc/4frU5YxTVzUwk/kUQv3eT01NGhmOmEAgl\ng13sdUw/qup+XdiZfOJkPQwmslO3lM94HN1Tpau/VC8p3+jMy8ETZX6IPD8L\nrzH8vt+jput/EJ1QgJxBHgek+FR3qRi8CCSr/ivIf4XfmpDIw6CsSUmGl12j\nwRFuh42+xw1QrTxUI7agzfZsKrSw4o23gKri5U20114C/YlaR01St1zgCLJe\nESb6I0kG9zYQCGAzNZfMaC+ZUqsexFuFxYtyzCr26FofAFY56fy6xzOev32C\nn0RAngo2kHdkN7U9JW/nksVHULtvuoJ4cs8DBIA/jY9Xl8koDHNDjxcSG/dz\nB9c11XEDKkaNuhh/942daeO9O32jbeuT6nErmQlWeEanpkDH/XQWSidR50Lc\n0pbU\r\n=9Ze7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzbYgkgS6z2ACsMszBxPXhVDI+iR6MH9UJoukCBlEZoAIgeJJ59NXYWpyTjDswSmLu0jJpveodkT5dAwJjEZaTmG4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180606233508_1528328382364_0.77016107605626"},"_hasShrinkwrap":false},"0.2.1-20180607000642":{"name":"@atomist/sdm","version":"0.2.1-20180607000642","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"319918cc01a005c5d47e646f9df9e504fc31d370","_id":"@atomist/sdm@0.2.1-20180607000642","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6EMMKhJv+FvYHeIK0vZosgWTymZQesDSJE7Ast7CgHa5rIP9xtrxnRY6H+i5rL/iFa/cYuEhurkPedT4Vq8eNg==","shasum":"eaa0156789f5bd3669793c8089fdce58aa00d1a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180607000642.tgz","fileCount":959,"unpackedSize":1972308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGHhOCRA9TVsSAnZWagAAGHUP/3OQa/ZjcPj6DUkV9Kk5\nFxov42i0d/6p/XYkj+sdARZW59NTGwmj5B/xOzCUfpD4K33qV5f7p1QkvhTy\neKd/t105YXW/69gVglDpT0P721eRqTjbMDvSdmJEu/kUUrVlIeHEk1OW/ceB\nxmDlPlKrPaRuPlyxopQ+XfLTdb8KNUhhAjW55n41TgpnjUZlHoyMXc4CuvtK\nc21kpSup69puUJKEW8Rhb1JfwDMCusvvRIoiBVVeQdId0Zcg7EjZjc2V4Rmr\nzwz4TbFq0CsB8tsFnSxwB8jNB+g/Whs/hgBHo7JJn/2x+6DYHn8lNDzmFMYL\nk/qjKyRaaFnBy2Mb4R6pM54SgvTZo4KbkWTUo0k73qZhl/MzpyqIwXlmoB7v\nDupKh+mHGG4/SFhcX+WJWP8DAz4pC2x8G7MzQb4jx6/w+7nzGNJ/aWnpd6/p\ngSVOmZ3LT1jJ9zKAb+FMzsvZgxVFWiBvzMXh1GfCits5CSSVratIj8ALcBjn\n7GeBxLby45WSHukrd3E9ErixIueE+VnUyOCslACTt7Y2GA49B6Fuj2+YPjRa\nfw6l11EZ7vqx1Q0dR0GhEU8e+OXUKJzkTD5lbj37DCtjvVGyQZ3u5BGMZNU8\nTLJylwE4k3xR92vsDytITnLTZ6JS7d/a8xSN1M14CxqjBwR45vwua5GkeR+H\nqKwE\r\n=eDz8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrj0OPJiE57J1J0tg/UqJn0yzT9IkpthhqB13M6HJJ0QIgUlQ+mQjrbnGD3j6TMtPimAssU6WaoU3UfndC3KI2Jlo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180607000642_1528330317924_0.41255391421031296"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607004549":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607004549","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"78ce746b529cec8c4db1717ed27c63a1905a334f","_id":"@atomist/sdm@0.2.1-local-sdm.20180607004549","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aYLYLNB2yzvPzNNIj1ktrIykE3kJSbnFjdeLkzyaHEddXJ9+agz7bCyOy79z7YTFaGX2AP4hd9/T7lsh4bnhOw==","shasum":"d15e831d27bcf26a1d11cd5db91d540602bde423","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607004549.tgz","fileCount":1034,"unpackedSize":2040294,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGIFkCRA9TVsSAnZWagAAawkP/1ldiAVrtAYFGKnfGweo\nHJxOfz6i7EgUvy3Vd3c35HW4TKnsJvs9FNTal3Zh7m1b60tvfkEHxqZwFuvP\ni5dkXEt7aVwoxP611bzOOq/Wra9x1UMK9DUY3ivMx5mfaCwTPJJIPG5e5xDz\n9gw8ykQO+NDgRk0sufkYW6KQrb8e96bRtY4PX+EepiXHAVCPdkqraoot07LW\nPT2digC5ncQx97a/lXg5RewbM7j2t+EITCjU91iA26qlqipVtBD7IQQsFa3s\n1h010yRQ4GY7SMknaFBDPl7XcsfAcfPYojsqUhB2d6/SmuQbKEUR4HoicIAx\nn4HpQCxFOUeH7nXu7t9bJNgwqyHrDw+X1GbnwGX6fbKs/ZfaNJWHUVvgXNxf\n03CwILXRAQSxtUspkmNKqAKhkQSonfXRKDrUkWogq5cjbvTuC6sdsEUPNsq1\n3EbEmU1EGNcFGpcbYmw5g9ybkpOqTZ/frT3Jc1kNqy+l6dJGhuDmY0yK4F8E\nQohSrSMvRfKSi4C+ce0pVsEnBhsdnS/245tIvZ7ZocnhGpXbobL/YzGAeKAg\nrcCtVLO/hrFjvQzE/47Z6twWmaudPWqAcUqsJO1rwCkLEs7Fl95OJU+gd+Qe\nBkvljjta38sF0Se5uPm1qrqSN7seHNB0Tm63LN9HyDUpuifQekWJK8HKLXFG\ndyrz\r\n=ci3p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICzO39uZnEYpGxTj/1wbi691LIDsaJUCL9/pzBc+QWTIAiB1Zv+Wn1wIVQRJM+BZBCHuztIp3ZKycj1VO5F7PETDrA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607004549_1528332644513_0.8703612348575118"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607043659":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607043659","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5439a4846411daf30f854b38e96ae25084b77249","_id":"@atomist/sdm@0.2.1-local-sdm.20180607043659","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-f8EeKoWIzcTqeXXlnqolRr/FpRMCGn3RMx6eMX0xrRhh0I6PIRtEbPD+xoufdcYL15FLEojjeMLO0e26UyXNIQ==","shasum":"5caec1cc001971c64ac81840386f3d812a558508","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607043659.tgz","fileCount":1043,"unpackedSize":2055381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGLefCRA9TVsSAnZWagAAY30P/39PiSvBRGpedZQ6KXva\nQs9phz/1f854pPBlW/jaf/usiBUFOUMY1UIEUxQzURv0rDcroLMDNbVd/n8Y\nYa4Aso/zbHwGKoL0B+5aYGQRr5nIuDbsOQ+LdAYJ8SWDwZc32YjXTXdsmtKh\ntG/6XT1oHrBbdlHED+K59cNv5k9Neg5LR22RkuxJQNHCH4T62qjz/K+2O4Xv\nl0+gl03Gec65TbkswiVUl88mlJ6hfS2Lzh4t6xfxJlhXfw/YTPeRYbOulLU8\ndhijNGFXwYsgTaOGkLerfHwZpKzl1Lotzh0Xt6nnef0NmDAI/r+2MAwJbaQ9\n5C48pkzRRZn0zgMCSt6HXIGpc75V1up0gYjFCOEZBppoxYq+rCnoB+bR9Qwn\nk2pYx4pDaPYPO1R4G8KUxepRzi0CI+elRO/XBdXJ8hwRAUOHvKx2bEukIi4r\n5BkEt3381fCXjv9FYkv0JPts9Tz/wvmbokRz1AlLyUvKkF0PjzymM12gjgUh\nFqw4yja3ryvnC+p0OF+BnGSPbjydpiqMRWTKmgciesH1ObAIjWTQcdc2UFs5\nVYm2Mn2BjYfmR1NCyXrAtl3QPBAlrcm9y2YW+3omiIQGIRS7CUzsisumWczE\ntbIXfET/Rc2KKN6HC0VrsXy5bXNdGNF77X89oguNUjKXJyYcyF0+rYPIyYIS\nMuAX\r\n=xJ/j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH5gi2IiJaphRQDAAHvWTZTp0p9tMZ3VZoqHl75eOcGYAiEAp95RuEUOOQWq+ra0SW1hiTLAUv49qY6j6ggxg6ItSfk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607043659_1528346527246_0.7704150948031054"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607052512":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607052512","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1fa5dc67ffb76b19652cf0f4f52a7eaec3417c2e","_id":"@atomist/sdm@0.2.1-local-sdm.20180607052512","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZDoRPUuoja4QkRn5fuZ0/fmTiYxW5KQGH8LWfpQhwjGUKfwfR12DewqBR+mL6ozIEl6nSYMMuEb4yTo/7ClDYg==","shasum":"894ad40f99c31fdb9d72fdc63104b92ac566f737","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607052512.tgz","fileCount":1043,"unpackedSize":2055563,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGMLpCRA9TVsSAnZWagAAcVAP/iVz3ruFOd8PB3KQnlwE\n7mOzfL/rCXuXJ56E7Z05jNjBoIEzMz2/DA6/Vl7Ye45Zx1rs6q5m4JSEIWsC\nqlhpdEvOuC6AbNjcePzj94ep0FMn6oDq9npsI2UO5xk9vwdIOnzitmEhAGjS\ncPFlNW4+7UDDHrI+hSk5RMSuN+GXCAdpdnZqGRWAtegXMDh23LOBlBegVxOj\neQZj0bE7RHV/vDtS2TmBFJEIcBumx98QKwsdV+nsYhwopVPXuWAEo86wq+wG\nQ6pxN9mQDF4CYpF8NL0bo1LpSqn0W1v/sTEDLdevhVB2BPjFamhwbzx4kZ/A\nAgL45DyWVny8d9t/2bF4vpIGHLK99Ad0vP4CbqZ7nKPWgBcy8IFq7NAc8P25\nLBGG9mHk8LvrwoGgjTn9pJCl56KmqZ+bFguFIonSzzqzMcPyBYuDSqzjS2LP\nMbABAmcXdQ2JrMVMvYB8SB/njKQ0A9WQE11QbHC0Sh0o9tibOMGawvwaWwSY\nXsYcIiUHgopgx7FUMOnwzYGW3A2ncfuys6sqTG8ON6wThq59DiovUbktXNh7\nBOU3z2bibu3KeimVlR6E01yaMQFi2Kd2IHra7OufisetQxSpbbmQrh2CSHDW\nNflMlY4VUIfyntjn5oUujiZi/UG8HF48bQDQ6Z5wChPWS8B4Y96150Lh93/o\ntn+G\r\n=594j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDIhOJVXJstRUEZBaMiGQQYDziEYIV1S87a9HLzRahekwIgcnHPYhJxKKnplj8nUjdTHZWhmzx2AwFxqgzpPHuhTcs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607052512_1528349417614_0.12435862457026814"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607062909":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607062909","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3f22fcffb62fdcfc8c537e7e1b8fac3f0b02a557","_id":"@atomist/sdm@0.2.1-local-sdm.20180607062909","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QkJ230MQqspYdUlq0DlQ6of8Btw0W/LcnlkA/XBQp+qgeJXPdThML/IDwCrr+tpgJiNsApddjXyU0znKcjnISw==","shasum":"7c154b712ef5dcae632b87ff78fcddd585bfb9bd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607062909.tgz","fileCount":1043,"unpackedSize":2056256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGNHnCRA9TVsSAnZWagAAWfEQAIUNz0rQ5AMOj/DvizqE\nWowKgqGUaySss4s76AOh26zJDobViQ/kJum7aJFXdRojxYFwdtQB4XnxIhiW\nHBaFE5nF6lxKHlhJwgtAG0EC2eMLSOmrInUrFpN0u4MoMJXTPn82R2cNUfwh\nnggIHGzpBfg5BYLuOpSsAbgiIkqEvFMa1zY4EMe/4M5yupx8EtZYdLBMQrvW\nmMTSNRiZNMi2YlBLI1VN8kFdXI/5BXS87r0qtI6N2uVu+GTDYBta81CQdxqM\nn1uGSNpRGSPs862WY+Q+xo9cViV+CtAXu3nuGsp4kwpSZw2twbrwZ39LV2+m\nKXU/RSiYTJQyL/8YFVeACictrjPfXGgvbblj4SPprWxyfbDQ3CBJxc+yRlg/\nts3DGdIO7Za0IOc08LCXGNwJKRRn2s3mFBAmdylFbT9sfVxlJgzw9EXwsllY\nGP3bqQ6td/dpLOzQvpco+HhuzZHwaoeIgVQiraQvGXAViOokwexLm05RZTDe\ntOWhPn03NDiQLgBHEH2iBkoEkKjMzLcbF2khfE8i73o9C1jvT+mUubQ9sn54\n3SmynPbuSx+nyVyttxVH3Svh9e1TBM0VlrFM1cTBOCY4ZLYsUfkqbPiivgeM\nuAaJAzmZ4FOObdMkF8/rKsUO4hlR5FbI1jTqvc8g5Ao9HcEMN27FEipIksh8\nV8aR\r\n=3ZUF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG2CfxnAvIKkUc+aZxBcfsWRyIn295AHVY0ghLSDChbSAiBcM3qy5EJuecur8e/wq1wBmVRkldwDjUVzUdzvadWCww=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607062909_1528353255388_0.6165127187361457"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607065849":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607065849","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5a2f6eb87bc3ce3bc34de62280c02627ef00ff64","_id":"@atomist/sdm@0.2.1-local-sdm.20180607065849","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9LFscZ7bVXCxTxXHD76pprvFvLFMuYb7zEPOhg6s1H6/aHVyywTCsZEyBQX+AGmmf1NgK+O/WXQcAcV/kEo9zw==","shasum":"00bdde6a53fe6836ade41eafaeb20f53ed32b74a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607065849.tgz","fileCount":1040,"unpackedSize":2054178,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGNjYCRA9TVsSAnZWagAAfeEP/jQDViQJIB/Jm0LbQRXw\nEfxYjZIgWFBa5adatipVzSdJ3kDt/aFVdWjJLdgQGZ7RDkNHdelX9uagvjXY\nhJSI2oDRP7Qfo58+DuwY0jD4nwYE7O3UmUC9L3Ifh72US/b8SlBzJgZMzXaP\nMY1o/8a/+o4opshXTXbukAaOpPOpJRiFpe+UODOwsj8lXgzdWtXY/OTegiYo\npIRrFKaVi6VyZN9GNt3RwT/5evNPQZKOWqp8GM5TE3jNeUIZiYRMBQytlWS1\ndLBar0VsO1qjFE+KvBt45+0xxTRAsdELfoZwRanOQ7ZRvFLdgAnZL7YrvPF4\nV5peSBHT8OBr/img6Iktk7dhnsE19VSCgwHfDkAb6GP2xgbjORz9X4ItA7A/\n4i/PuelAyEKq4CPUtVZSYrXsA7Llfy2DCGR8SrwyCFUCy2rUeI3kdAilGU9g\n6hGbdX48pY20mYfQMRd0ltNjkVyArVi4lBBVx6q+BCzzc4uYLmeIjKot36U0\ntRjmmpumMDNfONM2vgaQI3iXboIMERd9m2qAbjpsv7g9TzXvX3yCAhjuAcrg\noCRt8pBJtjmNLDq8GSEPDcwevGRl3KzC48TN2xnDZgxgCvcrios5n9USpVXE\nLalLUUJEPYi0Zr4UiDtcbHxsgyj7bjkHDH5e45ndSarLqCInkVOKLYQH8Su3\n8uA/\r\n=rlKV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD+cNiLTwle6n/xTori196VqyxGUZSwDollojRe22qDrAIgCZNGH28L07TNso9KVFxx3eWknwauCM7GppK8xcILhKQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607065849_1528355031462_0.9975659543491524"},"_hasShrinkwrap":false},"0.2.1-20180607073348":{"name":"@atomist/sdm","version":"0.2.1-20180607073348","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"10dfaff246c086d66defdfec7f645e9fdfc361e6","_id":"@atomist/sdm@0.2.1-20180607073348","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RwY1KI7mLxfqEeQFvtkhUCi6T6yTe+DHESDp2jOYFwTpNRLeBHvdyMJO6DH8uoDhOIeiexdzK+VJXVjNCGFFKQ==","shasum":"e9f9a423cab819afdf176be81bd8eba18fd2b32c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180607073348.tgz","fileCount":959,"unpackedSize":1973382,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGODzCRA9TVsSAnZWagAAM8UP/0vr6SaMg5j3sUrupIkf\nuit+6T7LXsoh0aAVBk1BDDht/2iXQxETcESnPTv+c4gKjO+I/7cB22t9m9eL\nHHRw4s35uM6HkPQADGrUaQaHKRUHTuWHTDAnGCuy95PdJ+yRuHPXRIVnO+al\nROtPuE4nMWMqUOlyh0txExooQgSEPcveeqaRt5zflqBAxYibzvV+wVRZAXmt\nu7peII/498te06M8kaTU17gzk6fWJBSwEF4ab1ztZEOkAbQziXxPFM1fJ/rZ\nF5x+Y1wNtD6+uneFmxKCOaS4U4aVtKbEwDPfdgNxaaDoU6qRbZh/q/6dcj1U\nqpDU1uT/u5DnvLhJmkSaSpF2HBgnr67eT2S6pZHMtQhVWXi3WSEomfj8+QML\nvqUvU9KgmFVdIMgqPMh9q4B5XHMeort+cRbMWn9TSPHSvTNkTliS8mX8JBPO\nEJcIs7AugCsvn+yRf70neDcz/m+0o5zDxDOFQknsiTNYmMChAE0ztz5080f7\nOdOHw8ErEc4CSmcXB8p71KJuIxUBlmAXU+q7DZfZPMVnwFx8hU0jV3TQ7DUQ\nWA1MWg3N2EwM+mqpisVn9WgK29U7dlODBr/rEIpEPnw15noar4tUw/+wMK+J\n3JooVuTeNTfUtt/xG0+Nh3Yfn7cmr6U7ajqZrw754X05ozXfXljfLJ9OlXba\nbkQC\r\n=Ly7+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICL90XOWMeAlHsEC3G8KlHv3i9IMoqEeoTDFmEGrlTpZAiEAxXIWd/jS/BFD+BGpJ7At+BbvWe7P7a9Z9TRv4PWeVUw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180607073348_1528357107155_0.2235020268042509"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180607074133":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180607074133","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0f08163ecdc8a950987fc9165440b1ec054def49","_id":"@atomist/sdm@0.2.1-local-sdm.20180607074133","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-44ueYPDQ1GZdE7Q5RXkwtGMz7uh1sdyv8qo1rhAIv0j/ZW+5Ie3nD/539IGDuXW++KrYcJemtpyASrO0L8xeiw==","shasum":"cf5f3f399418b6561f7c0830c706d784079b3687","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180607074133.tgz","fileCount":1043,"unpackedSize":2056866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGOLWCRA9TVsSAnZWagAACDwP/0t0gdR+6jORbe45Ex/f\n99MLkU94kSPQGbMnIJTLOHg+ewYVKoPQ8aBeJSKPjTBZ5XiT1JRhW4ObnSwN\nsV+P1B2dNfDZBtVmMEAqZ1DCljAitrgVVNIeMPLIpyPUZ3v+IHYCP4FpcQcI\nQXMy4qQniPVjikCv8KQ6J+KApOqQwK8DkUzT4c1IJUGt8KhS68Tb4KZZwb8E\ncBMF50vh1Z0tzfFpe7qdnjcMJX3S7le1wLwa5E+QshBox89RxCbIoJ4lPyfq\nCMaA4+Z3k+8YLKkGuskcE9JabixfGrXoWOajN8VXgEQaDqH/v3oxpSI8+4AL\nSb7EjUuwPihv9ZwMS5aB4+ZTewgD4mp6Ao2eUuMtkZ/HyXoDdEuCQNWBvrH3\ntLmOGi81SM6n7o7DQ2K5h69sMA1PaFLh5Nrvn1hKH8IVEZ1ZRuTL1vfSkLUb\nBIoXHlBgQz6FspcufbOhQ5xjyWDGGGDIi955ZxHALyL1A/fAmmaIauXXAOSS\nYq46I5ngGMLvUOD/cLbJulw9WY0Li6+X1RDXM3sJT2Bq+TnHEXoAe+QF6E4E\nbcl+G9Fw2FJvK2A/uecGQJW4KUictxHuP/0ojCEmMj5KPmT5Tg53egaObL8p\nNaTf01Ghr7bwj9nx6t8Hro7Tis10mJk2woupUs/0bm7oq/5WDDCkhF+h1jTv\nbItt\r\n=bf+a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFqlTDIT0PCJCuqiQux/YpGx1rT7h/kMfbxMaKqSvO9MAiEA3Nxds/QLtPp+Z4/H4SEGu9ExO6a8rSYjP9tFFk+vQzE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180607074133_1528357590183_0.9576233083276826"},"_hasShrinkwrap":false},"0.2.1-20180607135915":{"name":"@atomist/sdm","version":"0.2.1-20180607135915","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"aeb9e531d607aabf7d849d20f6c48e7d163b2b93","_id":"@atomist/sdm@0.2.1-20180607135915","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-spk6yarynVume9k8a9880sp9KAQdQpSFTpxOyo9w0Ruj9UTc0uK4MSzE/IYPuRLVruInEqidHZkf8NxJAdoQCw==","shasum":"019d6c667bdae1ded3cfb366e0e080c4901c3764","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180607135915.tgz","fileCount":959,"unpackedSize":1973842,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGTvXCRA9TVsSAnZWagAA8bEQAJZsPdhwX3UiIG/BUA/4\nhemqv5w+ETGnHipdHMNGdp45UOyRzkkUABCTvFF+jencTQEYsGATva9RIWnM\nq/Vqsy914cvmuSoxvQYfPm/Z08RzhF3R7/VaC/47/EW0/qKyUDheFffXFbew\nmhtinHHSMDtIMAGW+7Hv72rVylIknrJQuV0OZBz4RUDmWRkGuJIhdGjJKtCj\n/r1rR6v2jXO6dRGGakX6xenqEa5wINb5YFrS1dTH8FtJUazJ4ImQ+s0d/l0W\n5R3KCONMBAjArC23BvYzD6TMN/VHSyBtqij+8taIDMOyKxL1/WePnXvmQv46\nJDslkKhgEe8dFNdKL2l/HPOxNyGx33lfnTbRmsOwiIX0rw+tLlZp1XgwsOR5\niIXgC+9x2ylQ+2aydyBvCbhF24OdoH+2LYZ+Ow3lAbvcdhgepq9FlEWTvxO0\nfs8lnJ8uIuQss7cKEyiMTLM4IxE5SyLeJ0+GM0wz5xJpH3HOSz1x598N+i85\nuEwVHWAj7SsBuChzQFzq3RnXI1hFIjtQefawodnY1JdzvKRq75xo/ThPUWHN\nkXynr96kTo/3Bfwek2SNIiIajyLBA24E9snpqEWKkipP9w8nLZ8i5X98rqoE\nPbYxSOD00VHg8Eq4bEdkDhPd+CQ4caDvN/Pfnd81u5w86ayOAJiGHwIK3z7O\nWOXH\r\n=9KRx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG7dUCbOKUeoXotCGE/9nESxuaUhoaEnHSzq7eEx8lXtAiEAqAWryhI5vydmtoJKaIF9J531WA1c0TDJ6q/R4YfhT/I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180607135915_1528380375551_0.5113923524598183"},"_hasShrinkwrap":false},"0.2.1-20180607235138":{"name":"@atomist/sdm","version":"0.2.1-20180607235138","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f7eb2e9b26251093bff3a103bf168217a8ba63fa","_id":"@atomist/sdm@0.2.1-20180607235138","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7ukIMhx4gnKjRQC3L20UznfdZqlHM+hMo7/OrGyGoMDlskT67hDV2VzzhsyIlIIJ41S/zhwcFPJB+wzmqLehug==","shasum":"b6b1fc017e3b0cb08c3a10c8eee727d54c39800b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180607235138.tgz","fileCount":959,"unpackedSize":1974218,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGcYrCRA9TVsSAnZWagAA/08QAJJRE3y2zrrNiEdcdrJj\ntsj4BX07lS319UjciVavGOnvfCUYPBBxRlPOAAO2Xc5YIOcP50euMMJayCHp\nfOy5G8y1qd31g3bqhbXcLio8pPPLwPYVKUvHnYTi+8LZov6rK7PHcGiQD/6k\ntfcthrnXGYgpLQDAYb/OPPrRF4OXnLJ9aEI/3EpgZA1Jg7+kPywFD4ypO+re\nbv6yfNIbOPEfksQkbC66d7bsgiTlIWHD110un7323HPOXdzVkvMJ06TV0Rkg\nxCPDOY+UfgJ/AL9Ig4VR3Jw33u5koNeulqx3khdQsJ+WqnhP5A0s5srGTkmE\nX6jQi4CVNEzlgsWVVb8hfGzBHndiuP5hnSlf0JtsKNRU1+0CcX9Qk23eKu3C\nM2tkBwDBYO5lZFAIA+N05D3sLoi4NwKfNMQdoDRRC7wLjpQXJIeqbk62Opb8\nrGUxc6CuDAL33Zvjlz1jFeQ1F7FzKNUJwt7NczETOBs4kn0YMQ0RuNIwa8ZF\nWas6OyAZfkgERbnfIlxQPVu/VtFMrGYllS4NmoSbk2yl/1NjU6rBYM9ueQl/\n7eFnPnrkql3bfHrnqi1Pa+h0UaHvkvcQlbRwKS8ZFONL/4Ila07KNrV8Jccy\nP9ztrOqSNjCxA0KR1Mu6Hv10QrrpikhRSudgLPdxD1NDyMhbmSTjaCFFUYcC\nC9n5\r\n=pbZI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOoKtoBOqgwfyDAgOTr8U7l6guaqqWrhyMNvds3lLxMwIhAP6aQLv1Bc3Y7oa42bjnPJw5VrEtgMtf2K/vJ5EgkcjH"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180607235138_1528415787005_0.31965530219653737"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180608022719":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180608022719","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJv2LGwwx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","marked-terminal":"^3.0.0","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9ba88dc4371cfc9478203ec0f9db633b8437b54f","_id":"@atomist/sdm@0.2.1-local-sdm.20180608022719","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8two0aCcpSqXzE1Ntx8THpoOl0cMORtVHivikuz3TUkUL0WpHkPwpdlePlSVTItbpUnnGbGHMYpArhShjogMVA==","shasum":"e1eeefeb60e5f07921f57b6a30e25f23286ca753","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180608022719.tgz","fileCount":1043,"unpackedSize":2062704,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGeqyCRA9TVsSAnZWagAA7eYP/2fEMfXWCUGYdSpFmfHR\n+DZxbGUOeByedWkjJqkkdQlyGOcc1voUzPlJDM4F5AbOpv+/28vFEAKJQFBu\nwKeZsiL0u+mdL18B4zBTwBw3xerhg5bHt9fk12Z2eFOkXXLKyZ7v18UeO3yk\njICsj+fCdj+l0SE8qlRYmR6gNJdCukR0wySwNPmhJiF8Kq/4tur5J8A/DTss\njM8euGEUc0envnyo7pIpm0w/OmGoVW59Ms65pGk1/oqQgCVOKYjNdqGlqHLi\nOI8f0YdKjINoaJwH519B2TSmpFnfHH6Y6f+X9DdRC9UGWyyzaAD8lpLsiGUj\nmGgkRBUvM1XLGLlUDvSfk4KbZ3Ldht74F1cpMWSv/0bqWlMlQkan7M23wmSl\nX5jpgHZQX+IgeIhpFq77yptS4wx7UnwkTF8vxLjBfa+tQu3+CXo2HpEmGR/7\nQJN4edPhkdxI+sl//i73RfYjKdTQbF+2tsOXXazLU2QSKHx6tEMXlEOtDzRY\nms330ULxG+UXHmrjSiVfnsulltmNz4re3fTra4934p4NoPb8x97xjohtvUYQ\nmWMj10OD1/wcsnQAOtXgMqCfZtIEn3n5OzRaPXDflqd83KKAh5xOdv2IJEzP\n0B061iWniL/PgB3gxWWWXE5qLtiM2/b2s59UfUfZqrehDdShdLKS0j5IPwPS\nKRUM\r\n=mnqy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG39nEVPI4Srirq0eLlMViJ3vL5Ge7oXSfP9BhnywRR3AiEAnZon5fqjjM0bDoLUjK96k27a6PB5mUU9O4FH3SLlows="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180608022719_1528425137923_0.9253523056460558"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180608025633":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180608025633","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJv2LGwwx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","express":"^4.16.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","marked-terminal":"^3.0.0","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dd6d05a35ee78e649689e07ea944f32c7d831cb5","_id":"@atomist/sdm@0.2.1-local-sdm.20180608025633","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6wqGShrY+d9iwRQXimUekzxnJPTK3QeVFdPzA1tbzqOZbDA4hZQ4MW1UY2sGrxLxDcBT8PDmkI+I0SJ48KOb9w==","shasum":"b7e38d89e2fa29f687e84f9f3992c9ca292ec889","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180608025633.tgz","fileCount":1049,"unpackedSize":2065979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGfGbCRA9TVsSAnZWagAAFOsP/2Hm8jAa1+irOSFLHsqL\njUQds59RzxsNc6sv+lLwm4mlpc8lLjhaTx+lgcyTHVypKlytYrRTKvUwXqIi\n/dIeFimTa39328/XYDgM1+gJl5AQV1H4SGc47xrqShgmT972U5YnSwLSDbRd\n/vUH/Ea7sdmWJqtbbqWP0elTzhX91tKpD7JUysAFswYHt2YR3w3yxKhgRBKs\n0a8bopYTVTzALh8dB04t6km9bVqv3Q4svHRbADwtYNsRigH7daeEtA+8w5UM\nAzXCoWpD2LFW7Hp5eJ/ZZgrPqkM3U046cdyJT4ivW71Nw9WotWDp8IB5fOX2\n1kwkW39EMfjkgzZ4r8fNDWuItdgCIdnopS1DvY7WJf1yX4vv3+gK/UIg+Z5a\nZN/Rwmkwsjt8K3zJEY1z6RQ76u1iqYa9Offz2QhqLY4H/KmbJXXzOSAugEhC\nZWyctZ/WMjijW5HfJqbfSc66aZjqY2Eo/S5HcWTxuxXk9BaSfXlj1vlLnVki\nU4CmF3N9bAkJEhIg+u1iXZWPsVIV6tUOqqkdd+2nMX2Lg5qK1uLaXodIp7V0\naZYRuVxOlr3jfGXbcMCtX2S2n0zP5cTVN6uMlXgXb6VTvjVvBQfOPGFqZZ7x\nLhRprtstM3zxKTT+wCG1DRVbHWV81/e8YF7PqoA4Nyq7HeJaIV61TakaLIwJ\nG4sP\r\n=FUAK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHo5fRZoX+mjUzuCSq9v7rzIhnX7eNYBmFO7n0vj8fazAiEAmpmmwHDZRM0A9yuHkCPX3k1mgkRkgW90gL7l86tO0o8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180608025633_1528426906718_0.10414189463525347"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180608031417":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180608031417","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJv2LGwwx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","express":"^4.16.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","marked-terminal":"^3.0.0","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e43210a110e598e81f2e764230d808c8666c7b51","_id":"@atomist/sdm@0.2.1-local-sdm.20180608031417","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-j049oEQcS5hRhTeDKO83iGxRJha5Zi/nnG3qqwyvy8ArrBgkZ/+w0XiOkTasmmLG24vh8JTtqrbZeyrlB/dRWg==","shasum":"9ade01c062cc5e3bf38d387454de82da4f127090","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180608031417.tgz","fileCount":1049,"unpackedSize":2066119,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGfW3CRA9TVsSAnZWagAAQbIP/RJ1iQbMGWDpPf5iGySa\nyVNdcD3khE4LVTcDV2q1UeZNX30nBDK3JegRBbrdtDJ+l5lLfBgsAI7rU3fm\nyTd+ocCue/n8bpnNTCdCJRJpAg4+V0Tl3Qw9hoArDT99+vvqPlujOT0F9Bhg\npOPBQf5qwNhmGycvILpiKlAOVFMPbEKv1MGct0k+zZEe2iErU54MI7aYuH92\ntVbC2tYZ1XtLmmE5IMlplsZGutCZcJEKFEHL/ZdkjkRZyIyfDbWAqj4tb81E\n2kfr7JCcZCJBy6yxiQ90cCEltff2Ki+b7Y87pVNoBU1wLQhWO/YWJAPvUzfi\nNldzErpyyWWavR/4zpwzzhFQTiCUqbAm2KT6I7y6p9H2POpjtJpw16lPLDrk\nMA/EORH29u5VTrJN+kc1v5e+C6aDSqVHckh+Y+a6cN6AxOXrZz+UnXn8FikW\nL+/178owacperjQZEvQbWg5InguLMGk/eMdx+ZqOZrhrduSE2ahg7cE/0Yd5\nlbJttq/ihpzzxd1aWBYUuvIwa145eHa1fWRVP91UNiBV6+JEHTnUN/qX8Ql/\nK3aQGGJmY5+WhFu0qTGCvsj9fKB9WO99HycDzep0DJjbi4hUB8EHal2o89Nk\nmlYhDiNF2c4OwgyyD91mtrjn6wn+kLWzvw2xUf4X+8ZHR6bHmsLRlDbx6x6n\n1nCl\r\n=8Cxi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB0/HnGKIhBwbOZQU/K6CqY6kk0bbditM3i91biQFu8WAiEAnLJUuXWLYfSenrmwZML29ZcLYEzoySn/jeSZ0MVz8+s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180608031417_1528427958695_0.2679421915306144"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180608032949":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180608032949","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJv2LGwwx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","express":"^4.16.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","marked-terminal":"^3.0.0","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"499dda7e2e762937c9b75af82ba489110e757f94","_id":"@atomist/sdm@0.2.1-local-sdm.20180608032949","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-llPRj1mAbr1yceUmVGS4SO09SH9aIBRUfCU32a3dyiZcU1V0waiecdXkpZedlVc7cBJpeWLtv3RHgUx1nf1l6A==","shasum":"52159e5f10f3ab72e0fc6b2bf3cc0cd9f2226e69","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180608032949.tgz","fileCount":1049,"unpackedSize":2066656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGflSCRA9TVsSAnZWagAAgUYP/RmC89XxibQWJyI1FWyn\nKwTFyYLz1cFkaZoTfAb3Qfxx3LC1w8sg6Ll/Sp6aGbkkZ2fY1+ld5q2GJZm+\nbM3cxLicF2Hcz/o3S5+k8ODzf8N98h0EC65P32lM/uPASoYCDMUk3DB04gLO\nD7/HnG77kySG2tUbk6+ags3Q5lop9JnwWijA+JOv/ZlxUBchb/1CozVHuR0p\nooBlisR+Tdua5no/PtPcnkeiN9XOgGfeJjSvtFAzvJgJNa6lNFbsLZs8ho25\nAiXqRgG16NXZ2LKMjgYAbM00DKW50rbXRCtN04ESRnFMa55ge76tokY6VGpT\n8UTMiwZj7JXN6+vZ15iVjDrzDCeoVdNQa9vXonUHoKIIwntII/2SGP6n32ch\ne7JPMSeZl3XBLZhZrwvhszcfy9Ec7YFjZMDdiRaYwnZep76CCz+smEEr4IXO\n6cD5vo1amUQcEkHytvR5xIAWSB4fQLuMFnojHKd2Qrc45lisgCTAd5vejUxA\nw2DxVYGiJqFICv41tgivy44nAGWuHVoaHkEGumr3d6oQyypiJeXeNG+aZn36\napTYBPfK0DASts40eEileVucRjwLvN/sPdqZaq99HgbpXhI8Vqclrp0p+QqV\nfKfo3HHu2c9o7z4+hr2GDotNNwEoABMVBbJSlUL07JJQv/gGqTWIXT2hj8KG\nEpLz\r\n=oUlp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDVL0diIWFmeJ8DqJ7GhlkzdeenT8VxYAPbTSMx4ShV+AIhAMeYUpOJhQNA7wNiEd9FLm4vqQSF2sIHVx5n2uTe4+Hx"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180608032949_1528428882313_0.7278359015480169"},"_hasShrinkwrap":false},"0.2.1-local-sdm.20180608054758":{"name":"@atomist/sdm","version":"0.2.1-local-sdm.20180608054758","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"bin":{"slalom":"./build/src/local/invocation/cli/cliEntry.js"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJv2LGwwx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","@types/yargs":"^11.0.0","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","express":"^4.16.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","marked-terminal":"^3.0.0","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0c5c157e96eaf725085324f57c54253195825a3b","_id":"@atomist/sdm@0.2.1-local-sdm.20180608054758","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GrrHkwDGLDsl/1IG9MM2bu5mG5EFCBmTTaJMiTb/Oo/8EisR+kOIkXjqfHnSFY0X6hUE9QIO3djgzZFTU0pzKg==","shasum":"14810e5dff90895597861045c7eacf71a818aa7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-local-sdm.20180608054758.tgz","fileCount":1049,"unpackedSize":2073258,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGhm/CRA9TVsSAnZWagAAdDYP/3942UjsCUByfZ4/3W36\nKJae8DP1eUsvEriIrB3ZnVKx1Qt8H9PNHzxmq0NKe+faG0JxALPUkhHOm1zU\nYBFjUSDV31h/hGSq6zX7tZGrxhXrbS3W1/j0WFmadl7XOVNHZS5QnSCfu9gn\no6/3wrvOFnf2HxK5I0YRQ+ZtCeYUjN3JCRAN5VAGfemm8f0g1xyTx52BuebB\nbN/WPaLgCKCDmoE/Q6/9fhZF8R8Bsc683+zQuITS8geN1T6w+nU8vMMr0WZz\nLvgee2deIyNUXjgQGPhbMRe5VgNkFB/i7/MePab+ewP+RJGReQWOG7XRYsns\nS3EzWLk8tqL4dsy3TPOeQL22jhLk++scThCl1X5HmnBWz3PIskscO5uE31za\niCxpq0vQuVk1HGfgFI5GO0EHSkDQ92zpCxYlRqHs9O/GYid2PTnYETgclXr5\noPJ3ZTRuqBYroMdWCQpU+xJQYMVFhCta7cAK3fmMBWaNfQCdBv/poRPdfDOU\nyXDE6KHUQqB2PYt903CAulA3h5T8ZNoaxqqGIikCDGx2DRVB1QzgOp5VGl8C\nBYWEFsMi37cuN26eBsURzwvlJKZ3YXgddSt5Cmor0+HcjYL8NgsRpKtKTeO3\nvJ2usg+uKMZCWHUJ3vpLCRD4PS5IYis7jdJ6B/jwqYHLUMgvX+t5qrun5CfP\np9xU\r\n=Upnb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsp4KXnnBriSmq9KuJlx5zIjv1VXguoLjErFPBWDF0FQIhAM2HoVkQdABf1WMAlG/rNUwJU/Kn2v6K3i4u5gCu3XWA"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-local-sdm.20180608054758_1528437183160_0.697190819173833"},"_hasShrinkwrap":false},"0.2.1-extract-packs.20180608092040":{"name":"@atomist/sdm","version":"0.2.1-extract-packs.20180608092040","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fcc0ff0c5abdadf859337ad8dfd6aff35d535c90","_id":"@atomist/sdm@0.2.1-extract-packs.20180608092040","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a+MRa5hM2oQhG7lfLD/KE89g2Nx+CeitNkfb1UzSZmB8N9cCoDxzfpDcLl73fpHS51aBqH6bCF/I2jphFzq7PQ==","shasum":"3e22b9c3213e3ea716336483212ecca2f452c73b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-extract-packs.20180608092040.tgz","fileCount":947,"unpackedSize":1959804,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGkuBCRA9TVsSAnZWagAA5S0P/01SIkdpKadFebd4meTy\n9DFCl1kBhsl1nj0WECAIyP/DUjlAppG4MtL6NO2rrRI2L9S/tPOoHXFqOqPr\no8KtQmGb8XfQh3c3DJa5iWRP34F/jEpL8Jo62rpwiZjSPTekDJkcsPwDcjtQ\na/x5SMVfxpk5HFhykKQXMx8I+iOaUbOwMgUC891pQkc0sH86xmRHrw4piOJe\nqWot6MQr6U5gEDq1wswazCdEiw7NNGT3eNT/dunZLzSHNMgNHNOPu2xihK+k\nZGElz7kq5lP5etqxpSL67QYStxm3C1CEWLk8tXSCx7G1ZwMXxkhna8ALfaaP\nvNCQ+UtMSOHQKpwSHjdGYDB+abujjYYcDS9WGDrwW5NqOgA4zQbIGEzrCXxj\n8bd+5jnls6COS1KqmgWe4KS2X4SgXgeuxfLtm1fdWTjoz0Sh8F3jmnKg38uO\nXaFB9P0ZhuiwFhBJhJLpgSTl3qKSf3PPhMpzJh5ivPc8N6G6VPxhoz5HxQ56\nQnBM22Xqfwh1rV1PuZhmY8oGHGhdC1t+8TYzEfJFLuInbqVOEGh4fTx/qyv0\nBBpPY3bPqF21QfQ/nS4jpssBlWF5CBGh/gYr+WiCPMcZ9kZFRbFXCAjSUPaW\nogIDLylf3P8JiUUa52OQmbSyIRLP2Z2CXls8YJr4ZubyrhG4xYcjV9jdDLXh\n4ONY\r\n=zN7w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7/SJALmgUgE6z0eNHhoEKuLBz9BwwQ/gh8XH6iddJlAIhAKMiK5uCmn+jQ+wp+Nb378N1MPhz2SuvtkDGRgskmvJf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-extract-packs.20180608092040_1528449921477_0.8148816265059182"},"_hasShrinkwrap":false},"0.2.1-extract-packs.20180608111718":{"name":"@atomist/sdm","version":"0.2.1-extract-packs.20180608111718","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","github"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"abd8eee3945fe5e1e95e0c4db9e0fc95ca6f533f","_id":"@atomist/sdm@0.2.1-extract-packs.20180608111718","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pm9n2PqIhi7uACsS5wsBSmxWf0FxDYrUL3q0SDboIeYyWAUv2Vla1njM9v75nvnPbWlIPlUOFmAWq/vXXKYf4w==","shasum":"c9e8648a5c931ba448c359e8b8f660b02f00aaa1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-extract-packs.20180608111718.tgz","fileCount":938,"unpackedSize":1944113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGmbOCRA9TVsSAnZWagAAQxkQAKDiwtMezYqjOgWoeYYI\nAFiFGuB4yYPhZRP1KwASYfU+9P897SBm7zXiu/JuL14jt7oDWeUuR002v87m\n+cyC4+qGwO8VT5glBrl2lClVDxbKxtDYb8jt6CHuEH7tcDjGBUlmcDPiF9o4\nSx1iW3rnbRvCfV5UX35g3aogBK1q5XZal3j0YPsk45okCNsfbMjBgXzjPHiu\nVxOFABQZpWMOPYzjhZ2hkH4b4aOs+hNdwYImLqOQ/lMEWU+4S7e5Fvc2kGGs\nUcfCMAZXn1VzSqmzKf5m8+VLXtPpKVBV9TKBeQ70ixaaL/IAGCjprZfUZ4kG\nKP15kBkcFafmjtgDmbaF86F4f5EEp6t16rponWv+QpYU20WFFEeDIW5hU34f\n26WPO116XEvwlVd0LS8NObfs8WGEI3J8W4Z/DMxjGGyeJSvEZEHZGKVyxl6P\nzOZrb73I3Bomfmc4YOEUAFmRyHkkr9TdHJWiJfaId2w7MwqYfzIJkzoyjlMu\nTyjkmhLt9VXkUO8EZTU2qrsisDjOPMUNn512hqqeSjv8MEyNfof+jHdUmqPn\nNBfvkbzFqIU+bgceROywb5Mf5tHqLFy4bGWsT1w+cCdHtV8JjTQci5geM4qS\nSwiBKEZxAcXFqAHAncXZwojxtIetfQ4fj7lB2xD6TfLcAL5/30RFLOrqhxzt\nB6Gu\r\n=FMXO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEYz2FNvtSgp5GZiKFsVxLy9QnlBwJ9LA2tuGKA7EIA4AiBvnfTdnJCwCUrgnRMAN+bvpT+RMtyENwCVZmkpuJGG3Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-extract-packs.20180608111718_1528456910613_0.7755179300702031"},"_hasShrinkwrap":false},"0.2.1-20180608130242":{"name":"@atomist/sdm","version":"0.2.1-20180608130242","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"80cb0431ea890bb5beb99b1433a60352bbc01637","_id":"@atomist/sdm@0.2.1-20180608130242","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tQL5xTIGra/HAoGvMIoomj3n+gZwPPhw6mx1/5+Rag0goLmyNaD+fhA598V8uUpJs+mynOnjlXMyt/bRRzA4qA==","shasum":"33fe4ed02595d1c6eb428e1b73496d4168c9b545","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180608130242.tgz","fileCount":959,"unpackedSize":1997891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGn+RCRA9TVsSAnZWagAA+/IQAIOW7IC+wXxMyh3plhO0\n/dOoj4cXkraWMQeE7LYK6mMqmo85eMOHMzwIONyXEtTQyG47wp6z0qJQqolc\nvJJ8r/Df6j02lH5eDJW7z1q/6LYFD4DrdP9rPpKk9sCBTl9JtNCrvTT+Xs4e\ndn+cW/sk5yKHqfwbAsbF4nBXLBa3oRGVnZ63g3QTHQ5ovlTgpQ/PE7QFFjs4\nYwe8Dp4p088n9T0afxyKsEH/yGv5HPhK6AOADj+QBzd6Lv/gM26Kotm64IU3\nyirQ+/ezy3ONNVpDvqnY6+0Qe3IXedbdzDNKkepb+SdrQy9dBB9xGpIqbpw/\n4nLyTEwNdWW4mlGVPR2k18ly/nZ6fFH8KM0eyCX/kbsckozq2BmIPQ/itgn9\nuSbYHhtmITYxQ+9OuVgyKFI78trxtYyMjacEy7+xYSc8/qhJx34PSh5mb41J\ntezNNHZlRmmPVW+1s8Bzd8Y2PqiSNEfdmG50kSmHbJzesUfNkK7OekNc08m7\nOQ8KZEkpyb2jwVyTGxN+mzvBEGV5dgzi7nwoqoxEL+a2vgIqdS+1ipHBYXJI\nDLNHtyzbpKxOhvASOkNwkLEw4VbqXgtwkP2FvOR7yaDTVD11VP5LOv76BVgp\n2Dfd+kIzAnBKeKA1uOTM4DF1HPLDxetJXhnjpsxcgReAsoxyIfANOWHIL4cW\nbd6d\r\n=NtnA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDXWxX4xnNY/UJwPvralHUlmAehef9zq1j3aRqCeCmhjQIhAMnpUW6pP7SQDvsowAfDymdHpQ34Khj63OjQJEQ8/Ogo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180608130242_1528463249335_0.896254552619022"},"_hasShrinkwrap":false},"0.2.1-20180608143410":{"name":"@atomist/sdm","version":"0.2.1-20180608143410","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0a3f4b83cc6c4d5665b372958080bd512a3f1759","_id":"@atomist/sdm@0.2.1-20180608143410","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-skwZWvq3cSKYJiId0/eqD1SKe8cSnC36e83LilFIWF0L9O49eFYdMRDbbFPG9hl8p6xJlLEw5WfHkNr5j+iKcQ==","shasum":"dd7567a3eb3c47a5543c8b0b49bebb1efe313771","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1-20180608143410.tgz","fileCount":959,"unpackedSize":1998125,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGpWHCRA9TVsSAnZWagAAOOYP/2S2RtnPL8VBmm6d8P2J\nhZxiA91YMMIzxxYzzA7BFgUfWH7zwZ7bUTBZr4yE2lVtk/Zx4Oj51b2f6Bum\nUtIaP/YSVmjdUFQnyMQs1WLmuVsxebGA2LAawoglyIiKxQpxdmedcqdHcUP1\nSEk3oHaleo7Is5Ri90gZM+yPwLQpTMpeVrzk1STwxpB64kDCXyBNZkvmBEN6\nnk5aTJW5jJ/QQCAKTRiisz+l7iQl/6T2UGR+NCEBgtvc8OYrJRxz4gXA43nP\nmgtTN5zdPHsf2CLJ3WuuLU60+cUJ0+kWCRZMcjWS2lBFpIjzfrrZPNta9nUI\n6YMFNcv/P6I5w2blIXqUIyR2AncWUGfrorVZKRqbwysoAhVdEJCfjVjqduRa\nz1mybiFU29Ztit8LuV9rHJfr7STzNIUdQV0KAgyShIXhHITirK7TLKRcvKgS\nIP9PB+6y69Re/o2Y74kDCNt5swDB08o0KR6cyXL8kWdAug5mTBIWyvu3g+L4\nXr7owUKB61K/ztJ4FLidd8bwmr6HQ2Ox7tynLPHvhhx1K2WvPMy6h0s/whEU\nGxQVQWj3xgx4Ei0r92JaxyiWaw32Z8Vj8a0HYxX4QKQ99yZ+E29jYfs78qoQ\nSR20m2/h0b4NnMCgpsD9SmKU0m1Xd5oxOvpV30t6ChhybZw6nwreuy2Hzeua\nMJI6\r\n=KPLP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDroE5zxgLtCmgZNooAgl7k9pnAhPQjWi7kpA20t4dQcAIhAKlEzXbmpvOZsua0ks2aE/jLz0jG/4QBKGEfH10XhLsk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1-20180608143410_1528468871753_0.5140892046105903"},"_hasShrinkwrap":false},"0.2.1":{"name":"@atomist/sdm","version":"0.2.1","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"0.17.2-20180605032022","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0a3f4b83cc6c4d5665b372958080bd512a3f1759","_id":"@atomist/sdm@0.2.1","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OHdbWGDDdYcm185TFKsDh+NjesL3zt8NZN5FTsN57HLzeyuUZTD8qvyZLz9n9jbWu3pf4s1lo7w3+M0cXtr3eg==","shasum":"75415ae812c907aa5b38168c8d4bccc22a34b7fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.1.tgz","fileCount":959,"unpackedSize":1998110,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbGpWtCRA9TVsSAnZWagAAwjUQAJtDrDfgn0ASiraiN7GE\nDSTD129xLOY/hFJrRbIJi3GhlCiw0q/WbnqhVNIYXbXYWgeJohujycz4FuRN\nvL2I/BkITJKxSJuPlY766fwvr7Z98jYT8lUbL7s6HHUvG2kb9d2KwBJXDbae\nReHTz/4hoQZsjmaPdA2IAYj4giipkHRRRaGqqJj4R54ct0mVxSKm3+iskxqo\n+mJV5iIeHcVbGkNq7h5U4Y0DVg4arNVP52xF0DM4o20XOANYqwVJpXbZvknU\nNjKFHuXqlyCpmxIT3pBQxacjgNkYNu+3TL2gHM2JKB741bIjIM9rYJ6J/3ML\nuLO2tCciPamXCc4jADoWYg5GUxvbmQPrsr6QzCh7oT9bRasCqwxVhuxKLX87\n9/a2NHJlobubvaoprAnWVSNZiAn1CDm3jAf82tEEwjl72JCifg89EfTwIyvD\nLtBKOcUgGqmmvAXc9YSXhIUk/R1YDAP78fBN3mj8qoCAxh5HNIWq+NyPq7Fy\nMgH/hq9egrD21s605mRsOq5qjAKeY5tEwnBxP6u6+sNgIGoDYdZicl0PJHTS\nbybJQxlauQF0qaotNCf2oQWjX+/+iyKlFRuB0QwlYGPxbef92GabGFzGiYla\nJZHpf0+KIW99IhWurLe/uP0fLpBMzYEF8MLcIeYmi3W0wBeOp+K7iiaXMEGs\n4zGD\r\n=VKkm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEzRbtTaYVCb3y98AZ/u6bUg3Nsy0BCnMdx3Tmb4DyJNAiEAtt5ENSzIAAq4jXnk3SOHC7NwW1Xd2EAnfX6LKcuYXD8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.1_1528468908998_0.08133703780631651"},"_hasShrinkwrap":false},"0.2.2-20180609080246":{"name":"@atomist/sdm","version":"0.2.2-20180609080246","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"29b5c57c80bea482951144fa7f07bea566bbfeac","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180609080246","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-88Hjqwrhgs+De8cyT7Y+LLGYgiCjoTSi2tOca8Y39QORbQCnyoj3WrD/mUXGF4Ly5bZoEAS/9MB7hq2OMB0hqQ==","shasum":"458fc3c000e67f30d125acf9388ff3fdc954c182","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180609080246.tgz","fileCount":959,"unpackedSize":1998137,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbG4rtCRA9TVsSAnZWagAAhlgP/jhq6DRzGok+YIM64oKk\nZPIv4iRosxM6A9YXhVUaYwtzQQSa24OvaINXZTSwFhCNGzJfCte0jfPuHGVe\nDo3QMoTgdH3l4Q/q7MA+cMCYsXT4A43wD3ARX8z8IvJTFlrgiziUWdMypvHp\nUpICgh3M1LaSh+bryBzDIzww+rheR2Wt66YuwTavx1Ymics3LNYnVC2WJ9Qr\nhPq6qD6JktXjQk9aDjsGwgK/IKlTt/7OHxJn6XTeHfAwjRTiFvh0fyoRmFBP\nCHq5shgU81446C6wE4bpVZM8+nTckmt4hKPo/+E5wzeuU6kq7r++5xroxsqA\nyEvW0PE1ib2YIqiU94MlSj6WeBXbxpkalCWT2VtePQWfpz+ZJiiwCZigtiHL\nZpaTEe1swA8OpW5tVYxuj45LZOKFsG+gNjs/5WTA4ouPmfBVivhMfJ4n+txz\nnl3CF6g1LcwzGWoA0e7fhvcuNnK/hmxLGi9F4kBUo9KL4BDxd2sGqz6kChyq\nLf9hSfjfS10qYJBI46fjd28F/4G04v7FRwC+0dkzWEE9xk6anIIT9x5DeGUH\n/tgJg9HRv+cfD+BrhEoP7FeAQtTnMnbi+fBbsx2u3NDSKXG95sVIao48f0DL\ntOmaZ3LWefKtXyh2UucVw4GeqOmFCQxvp6yOCHJYT1F6lQdioF6C8h7Xc0hC\nrgRx\r\n=usFc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCriyzOeDLZJtLjdLxv8uRc9F62dtc8cdDcJHWs7mTqWwIgNxJlTDuCVVsJrHrLBYnbapC/1kXSmj2O5g58TaRlc8w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180609080246_1528531693568_0.07885546103079699"},"_hasShrinkwrap":false},"0.2.2-20180610013638":{"name":"@atomist/sdm","version":"0.2.2-20180610013638","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5c264226050044726ddfff38c33c7e3434ebeff8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180610013638","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fY2iuhHnT7CA+9U39cTPCy2K5Pm7s36tPDIuhOQMPFrOr6Ch6hjis3r/004jB023JvCTnMFNSYtHoGuUasAmeA==","shasum":"0e4245ebad504fdc01f32338aa947a29cf88778f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180610013638.tgz","fileCount":959,"unpackedSize":1998585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHIHACRA9TVsSAnZWagAAVxAQAJQKK3V2XFX+MDHsd0jj\nSAr+X3m/7Y0zWmf/t2wrf5aaceSjbcEsLYLjTEe/mMlP20hfwjhJR5W7R6N8\nfMI6VtU9zPZt9UglRiFmG5DOXcQdbP6J8M1p4lzlHSWA9DLvcwdgS7f7LUQZ\nJB3icAHtvpu0fuSTUNc6f9WXm1ZV3D5/wkeg9Vpilt2P5pZ2iU05Ez2fDz5a\ndeN/z26niGk1jPIqCubPhGXPdbpn+6G1WDSKGdoPXVLqwyJWP7KJ4PYQ0Mnr\nIfr0UzbRWPIc89gRSWiqPPqf7wu4LKxH19NiN4cNNWQSm/q7Ml5t1TkgEWW7\n93xFakBRjZZdd/HJUOxw7cXICojUAZCNl6aCiCnIT2WkUsQABQ3rjLTBR3eL\nRVOMeY/5tgwG5P4QTiuJAqhqayoECcCwAjqcMD3DCUpMbiCearv/9g4C7De1\nHMlvfsSMZiVkZoIaFJZtbGicS5+7PKXPAtJUPzBLr4gVZlSB74+FUhdoHtaO\n08KCIdakIjMXw+o3zXRVTjhhw6mti0Lzcfd7L5UqzUB5+ueM/zRMixazSZ5N\nOSak+hejhZsDDtjJIViNzUjUl7hiXxKWuW2Moy8hbVRaWX0Jp2iVqzzcIm60\nIFMM6SWqKGetzGtZJyHJvpnQSBauYg9aSYPxLsDeJvwl8GMtXvrtdgW6j5Y4\n82eh\r\n=Gbqw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID2PcRkMkEu35LInGhYKe/zaEtnaHAaYEcOukKaRU996AiEA6dwHofbQeEb3YAJhDXCmqnY7sSFKPYL0vNT2bMhEWlM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180610013638_1528594879522_0.007970311909620387"},"_hasShrinkwrap":false},"0.2.2-select-own-goals.20180611045118":{"name":"@atomist/sdm","version":"0.2.2-select-own-goals.20180611045118","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5c0917299ed50e5be9fd26d39643795082346eee","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-select-own-goals.20180611045118","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Rv+HkmHXnVd7JFlPe2fmFinZc20c6V8CAYenRTm7u8KCmK/OSYoLS/BIXWA+ntCMPTjnE3eJ14ssirz5KKgaww==","shasum":"c11d11fd51bfbebe295ad0a2d456d6e456ddf9dd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-select-own-goals.20180611045118.tgz","fileCount":962,"unpackedSize":2002536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHgDfCRA9TVsSAnZWagAAIuYP/3bin+zBsV3y3OZXOvNV\n+9l4zw6Ohy10668svgR3Io8L/vCThXdMtTuiLGMZvc7l9saW8ZHrgfT14pg0\nnaI/qlpagD03TzfgJWmaBm+nqSgNkUcGRsAb6RPM4/AyGokxOVVNQVnrtGr2\n7W9l3wgq2qHTlVnArjN11GxJG8xkc15C4bXmftRuG0WBV5e7d4xlJxSvj8dA\n8TbtNg/yhAO/1BodIVQ+HbduEqjKAPsOqIHY/3SkTdFk4JUJ1NzZ5iJySGxY\nBsOHxRGqrslzUFLZB9Aj/G9jiXSEvSGHQBi9kWj6jakbie62YnXEyfpJSxAL\nOfs5zYFLTR10yZ6fL/QYU5K0Bgb7CKEO8Bgt5PfP25k1L1Z/zgrZLPt9MCW5\ntXW+oLrShtDkGZ7maUuaTdcKUepd9XMdii7X7knFqzd/dztV/dv89vD2C4Tq\nIMofjqUQW+32GE0X3xQUxakl0rH/vSsBD2d5AKvhmd8mN/Qx82bZkpgIf/Yo\n4+WuhKK+38lgnuS+dj6xFxIguTAIFQJQ9qw/bjI4nlAfvG9xPyBnVnbfN6wk\npbBYND+94YaK5GJJdjF4EtUgFRSZJJKtbUPA2KKG/WD7XAtJZATH9WQaSiE6\nt11vMyzSAin3Hw53O8zyLWP/Yk32C3vIryhVTwl8nE50KtMefYeEGBkiD/FL\nBPYS\r\n=Eq2U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8vULTsXOa/yMjE8YbTL4uSC/rstZ+vgzX1VLj9HkEcwIhAM7YDcoLt7ET3hSt2pqIg2HqTPktEPubCdnk/HHzyfD2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-select-own-goals.20180611045118_1528692959083_0.5422130341319209"},"_hasShrinkwrap":false},"0.2.2-select-own-goals.20180611045205":{"name":"@atomist/sdm","version":"0.2.2-select-own-goals.20180611045205","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"786d695acf76279114884b820e38ba5ce53e1a7e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-select-own-goals.20180611045205","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bHbyusTv8COvSSwZSwfmdQz5CqVgUsd32hAJpEt3c3toJU0BZehlTEAegYIF/ULlRncEXLS8D/nhFdxTPNqOcw==","shasum":"5145c633b728748a2ec802b225754cf69f82da50","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-select-own-goals.20180611045205.tgz","fileCount":962,"unpackedSize":2002536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHgEfCRA9TVsSAnZWagAAtwIQAJg72kmpQw83s72ZHEc7\nlmkrfaP06xW8lVEVTXchMyxLk7uSgP/EC3t5ePg901xQLFGazgR1vfLjVUTw\nlqA5PBrD5VuIRsBvEMBz3AuUzXis5d9FiFXPUGGx9JtxJZT38vD09zmPu+id\nnlWavezp1qhR44qnZ3o7Xa89kk4kap4ndodr/i6bldOg9L6NFQefVVhXftAw\nFUXRPBn6X+HN9W8K41yPqYdN8Q1TEiibNJUow+TBQUfPj/Lxi5sYXpi2m985\nA+tWhfMYmpqsMo6e5IgaVo/kANvXVHhLnsj8+tcbRPmaMNNN56sC22BRpd+r\nHLNSPB4B27bSpXUAcW+BO3N23thoq0UDvkSch7aJwZXZMsQaWgb5r88VLHRz\nvcIa59sy6zcv+Rul2PSvC0dZy7Xet3GCOkjMLWz4s6WILa9a8H506jIz3gRJ\nFzqknXe8nCCqLtg/fAOvdZkI+956vAdzpmtIE7jT3GuI/WSnzL0VhSsdZzB2\nwyrPlG4n4nnVKceHQ5TaZznsHf5nAwwLaU4voLaXrl61M1mW88U9bFV3iyxD\nP8TrkxsF6GFi/BwmGiigCg9cVoLJI41/g1Hq0P10NKJ2w3QtKOq2//azaflq\nYQ2FjTibKdDD6Ci7MoQjqoBIq5XXcvIEOuJ7PDsGP33kj3IPiNrh4D8lDGh6\narRT\r\n=CV5L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAR7wJdFg8xyq61zmHdr3/Eruraz7kxSgrrP9jT8+/pTAiEAgeQtdH5CRRnKHOlf5xAL6NpJgrbPhvqrqqK5MdATePo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-select-own-goals.20180611045205_1528693022775_0.28477608195733395"},"_hasShrinkwrap":false},"0.2.2-20180611112048":{"name":"@atomist/sdm","version":"0.2.2-20180611112048","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"37db69e44bbc696d2dfad8222243d29cc6d282f9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180611112048","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yQ/L0AxYGR1uKRibNUS5u7Cw+lYPT+RfdyFLBgCskyTmFnD+IKAT4ufvyK/EaPGLSvZQr1zwkA/EHnyAs22Adw==","shasum":"b919488d1581affd01f29895a59602bafb7d1dc4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180611112048.tgz","fileCount":962,"unpackedSize":2002519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHlwqCRA9TVsSAnZWagAA2GEQAKDCshRBgbyfrPo2Yrhe\ntToAwzKc+K+nQ5p+tS1WJEX/aU9f31OyG2iSMsnHOo9aHETBqqGR6DJ2BG5T\nEPB5frxZO5OYNatVGmqO4aKaho7DzCPLWBmO5draYlQYPpiwWg9MpX0IyPYk\ns0443QKSCqgUGlzKeonAKEUpKRR7hAXnmvDnbY3YY/OIxPPKHxQlKyVy2Opx\nvOorr20joOtWImDk8XY17HfTHuwix+/0ewXDnFC0jkO7niuPhCxQ3f5X2t3S\nMrgw7aP4uFrfxhRJW0QwJG5cDibXICg37DchuH/z3t+uLDhLRVZpvUslxc1Q\n4cXvb2tBERzKiL1OeGB7NbGeEtAS552IAME0QlOVTeS76syzI+XMO/9S8HzV\nrheTB/tuviSb6Zdc356CPSog/dJ/67zGUKhhtLcpHSvsqVEWOkqJLi53TH6Y\n91JOsXrXyk8YDCcsfRPtQYNLRATezk5SyITQzG5U023HDujJVpPUSFmhCR5Q\nHWZZtp27mWCi4+cFJ74KmlN8lE//DpeiIqE43IrYaBuL3TCE4WDiEI8Mu7QC\no97H35NhvOBzhDvby6iwY7QjKhHpMJNIU2pqDDTYLGLU7HRkqgukXF0Q/1Yj\ncy8kd+23Rf+ibZtDjbz2bad7G4o+czvHDNqRuI/X5PtiQC1XH9Z8WR8Y1O1P\npn6p\r\n=O9FE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDu+Xu8IU8iIQqsNx0BmipDILNDxcvIyzAyqQq1LmAs4AiAqPrtkQIfLV9SnueeokTz9m5v7r62+kbKOFrqetKFrPQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180611112048_1528716330584_0.14869057233771676"},"_hasShrinkwrap":false},"0.2.2-extract-packs.20180612003823":{"name":"@atomist/sdm","version":"0.2.2-extract-packs.20180612003823","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2518584f7ebfa252174acc7602a74dab0b57a6e4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-extract-packs.20180612003823","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4ArK47LpBTnJULhAIsNZpJmfU+4YYqIfQp8GW2YTkNvF1sosF2Lc2617WCHfW2U+10szCk2K/9+p8jCfJdQteA==","shasum":"7c552eca8d65bd721f938427f7038de2c334123d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-extract-packs.20180612003823.tgz","fileCount":908,"unpackedSize":1910288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHxb5CRA9TVsSAnZWagAA59cQAJX8RC/+co4i43IM5xel\nzULBKFfkCtEJLLJa7ZH90nlztXKLc3nddrU/74kf0pL2dkwK3iabTa47HBX7\n4y3lyxdd1Q+OOnbdNEL2caPBJvP9Fcov5yvQtypOs1PwBNUVTSeBPEIk/jxu\nD0D5P4Fr1FwNUwTTDgTb1q4kTWe9PSPanCIExzXKfwZEEfaK3x25nkSDbUSz\nM6psmmr1u/34crJtvl8GekduwbOHNsKT6fPW188g/1WO3pRWCv9ea2NSJrYL\n2PhqQ76H13H13nwjY1gIz+J7U8eafxAQqewdvdSbhtNHuvW9D2QHxRC7jO55\nbWRHg96ELLN7dFhqWHjgp5JGGl/ZdfAG06buNBBMz5Ax6+QHPSnkSx/NJeDz\n+eodG0lRveyWygdOdjy1qu5g8kaKIlaA2lWjYLvs+2mkrq+ka7E+4/uvZjpF\n6tFR4Mt8sE+OgE18sVpEFQLtJr2Ryp+U0xxUUTvj1e3Wmg+8EmPJjBKSEFmU\nC5U6iPnviOpmZRvJFnMK8+0M//lbl6HrhH4+smRilmwSYgIIgaExG+EHzSy+\nUc0VbD08KqMU5fA3c8D4eQS+F2RZCeLmRnQdgfkzfGmTOpGhr0rmSvOysCv1\n+vcY5EMwv7/0CD/wxLzngp78eeBqMxLRrUt0P0xfCs0tJa0Nv//mG/elStdE\ngQjk\r\n=h0n9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFsjAzGQbFwsfDhUVFmLnLBORPvFk+i6C7HHjbL23onvAiATzeSFS07Fn7ghbVjpcxdV83yTBhyoTSQOBQ2CotouSQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-extract-packs.20180612003823_1528764153319_0.9456774519376729"},"_hasShrinkwrap":false},"0.2.2-extract-packs.20180612004641":{"name":"@atomist/sdm","version":"0.2.2-extract-packs.20180612004641","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"https://r.atomist.com/HJV-CGrbYx7","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2df74c7ce350b9e195a7a6c420dce089a610cb85","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-extract-packs.20180612004641","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dE1vxAKHydUtG+jeBCm0wmoMafB27uiqiqG0BQ5XeQOCO9aXzzWgEP/fOkG3NhUHHtac0Zx2lSIyraJV9rMFhw==","shasum":"c833953cc21d6de70277839033d0072f4a1ebeb0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-extract-packs.20180612004641.tgz","fileCount":908,"unpackedSize":1911224,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbHxjqCRA9TVsSAnZWagAADqsQAJc56aXgK8s0Gy7SeS3s\nvXEu7GqVf2HsJOMfrHBQNHsHRAZZ/IjU3UCoNA6M8mrrPVsDGusgfzunRBtn\n6UZxhNiQYqARetiWhV6TYGldw7rMwa/nHdJeBmLAcKpcVRzjBcX1qhjJiv9q\n1I1UOSu4wrDa6fXG31X7K9w2qifsIwge7RnF25xADO99GRo2vR7fIo6cLhb7\nQzlkBdyrKcuaGi/Drum40BfOE7QtV7yffWR0C9XibGkkQ2ubxb8GwiTTq1Pr\nWsFKTf9lZ+htBBBL7xIwVnHO2IsDlRwk/Spq0G+0NyK8xV2iMiSb4XCrAT4Y\n8/yVJ9+mUZ1URBqdj81Zn4paJLjXWnFj7KD7sNpyiPIoxTLsIA2wMKb02Pf1\nSVQ+eaOx3HFvg3GYqChZ/zsNtkrYf4R7PymXWepEnUEtBGyzGqgNHMQI8SDy\n95ytj1PseTmXSQPZcH8ZzBJICTSO4BgNZX+zIy1Ru6VGbJ8sh1qSph9JCmNR\nLSkehbptMAQ6bo2bbVRZWXyhWWctw3+w/oWi0LzMLshy4b0yGmqnW+bpJcS0\n6WFVS5w/iNy+Cfmqkopl1NV2ROg0SQsCfhwfguU7l05zThDpHrFiSVvpIlaz\nWtfFLLGWoSv546A23QHApwiW5n1z2Q9JlRNVUzGDWloJUCvUD66qFpvh6r5E\nfx6T\r\n=QE2a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICYzqADeTOe2t4YXe5Ampz3CokoFeIYCwMStsL+X3dHBAiEA2nIFtmb4XMZkEtZmXtKU9zMPUAurp5SGF5i5uUpnvjY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-extract-packs.20180612004641_1528764650605_0.6169642048743869"},"_hasShrinkwrap":false},"0.2.2-20180613171031":{"name":"@atomist/sdm","version":"0.2.2-20180613171031","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"11c2c5807cff4696e345b22a5c10854986daf8f2","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180613171031","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l2iQPTsOKv6mgTfkE2PysUG1d5ovj2axPg0ekjA88xvDZAXyG/EgryMpWZL0faASBdOk0dZHTQOJh23u8uDMiA==","shasum":"a4dee8f25120297d7dbdbb8c3f4d29a91990c44d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180613171031.tgz","fileCount":962,"unpackedSize":2002508,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIVEsCRA9TVsSAnZWagAAhf4P/35qzUqobQfOgp9ElXYB\nWnkldt/Fnvu/cP0JA02v7R+O+HNda3L72FwW+IM2x58uRSEwy239pjWBDYCn\nDTxDkPJ8ReX99RYJWW7DdPz2zSBu994d8+T3m/yiKdT27AjejAyhudipba9O\nPfwEURGbT+UVcQvAn5SC9ROMlRuOF1bOhwBvF8ukjcp7Wu3eWFcKWJuSCBiB\nj6j5DKuyZXPOMobSg8wBb8sYNAFAuf36mG6zvXXeGumxE4crt6VrznU5YgRT\npGOTF30x/58vcsKCxQ6R1BeUAvYtcepxC0PrSPPeoaGK0czUAa4Q3/5CM15n\nlIAzyDcNm+YsS2CKWQJVdVY7QIrh+/hGyCgzRylD9r89i5JBM2lXnBxS3A7X\nWcIJnwEE20bqYP+iXoP703//hM0emNKfHQFdiFOOEtfF1y6sUH4HuE38xmqL\nG0xc8Tbrfw5AzpAJgKt7jjrWP2tLhjpR7yp+Y1KsfKZHaA5yiPkc0OcZ2wSj\nNt0y+a3azGQzUK7DwR9anXV7iYUbXdngtfwiLF5BAu6RT9OA4VS91uUfAyJm\nlIGafYiqn8onV6xzkbZKjGZsN/qFOqLRkevhmgl/dL3zfHX/qxh2WnwqGMVB\nB/8CojnLPZDygiPKkH197Bj0gDrUor1LWqP2qMqFMoTMbOl/uWxEzucfLkb3\nXqNy\r\n=cQMZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDwhYZhzUoFyknKA3Ybyq6GHZ+qOwgC6WUY+FqmZN5RoAiEA00tM92eh0q+QpM8ElPKUJJRX7ylkoeJI3s18qUsJmuU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180613171031_1528910123984_0.8675986309535422"},"_hasShrinkwrap":false},"0.2.2-20180613210712":{"name":"@atomist/sdm","version":"0.2.2-20180613210712","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"81d24c802bfbd8847045c869e6011ac74cdd635b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180613210712","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HkAkR7mNtROIuAynz3irVNAIVsk/IZzsF8nkaK9nMHDLdpIrRVT8+LNYuwvza7UawS2hWsv8XTOb8qJlcZVe1Q==","shasum":"f25c7d91330fd9119cd279675864d02175158a35","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180613210712.tgz","fileCount":908,"unpackedSize":1911199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIYiACRA9TVsSAnZWagAAZSEQAKUi8O3JKK4g1FVBbKXK\nHDjU/LSslerozsYEbhCAsdAhpJs7EZ6XyOz37LZdSkmJw9RL615GZvwoG0cm\n8eS5ZAgQbchCnpy5OgFESl14ve/abmS95THU/0sWIceymdNrE8eZX3q8MrTv\nOgniBmFdW+CCmS3x8nwSB5pyDIcx9JdsQPbw7TMxzbW5mKFtpJ6HTF81ycZE\n7oqs3WyriHifPVIJ6sn1nOfWXrq62NOLKAKijnXF1AgfNhcjaF2uCe5xuhyJ\nWF3IvnnWC4ClhQkbtA+wr9EemMGRyfPQZlcy2YiC+uriCz/LsM03RQduixX/\n3ympDtUcLB3LATiZ+aBkDkuDd8/8mmBtPYe2NWBL/BTUIJuacO7rRbGjtsoC\nozzv5XQIkATF1ypOD3tP1JAOrr7aopd+HDUQvs6iirROY/4G32Xk2blvd2Rt\n1fCC1APRbnfjjJ+BzkzvPWQJ+3pcaAsfnCl+vyaZDHx45u2zgnGQWQ87tagC\nZxW06kH8AtIHcWxg+ccGl6Xx9mbkERBPmjreAl7hxUhgEXmrMppEz5gdO9tX\n2l4zLwmL/t5yBl0TJyamjAp/D34u+ULGu1YRhH+1ejxJ+2tHLY6y4wW12b2E\noxvXHqkwHo8fkjyNAsRHPA5yk0rUHXgMDm1Fh4RG4W0kMFRC+9bc6izXu6NR\nxGWU\r\n=/rOL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmzMRA8lkCPUXbSN4Mb/z0lToQEd1sMywTdqLJhsLbxQIgdcnXgdLDauNeK/GMuEczLDpIF8bV3jYNCxHHqcvkkqg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180613210712_1528924288396_0.8235259240534469"},"_hasShrinkwrap":false},"0.2.2-20180613231852":{"name":"@atomist/sdm","version":"0.2.2-20180613231852","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"530b292a77f9f9f7ebac92b1a2bfd6d9c05a901b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180613231852","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZVGyM7Esk8NaOVHU9OkWEpgYRTSYm2f23jkG5FZrIBvJWWJrbm+umKdroPhXb9ZI/DvqFzPMCuTlk4hp2hMuXw==","shasum":"8115aa02da849c83e4f5c6dc3632f9f738fbe1ca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180613231852.tgz","fileCount":908,"unpackedSize":1912172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIadbCRA9TVsSAnZWagAAhysP/0ou0JzCor0j16my5mVw\n71cO0NOi2Pzsns/pmBr42hsVabC5SVR97O28sNGg0JJ1rrS4CpU5rvjVqn4P\nv5a0olYGurKIKgD1GMpBzgbNzq5G5nj6o1kfw7idDrZmTLy+A796abbdmUd8\nzh6wN+R1wskeTc6SjEtU//cgx5KiH7K4OBGBNbEI4tUL2gpMaPcNcHSK/41y\nLZTlTqVkiGJjkPxw0pimMWKvziNQgeU4c3vzLbzJk8gsdc+1U1OmN8p1mEPw\ni+rKY1hUInCjHY5a0XpTwHbF7LWmDA75LDtzIjHkxuIwyRyzddf9IVa7jLQX\nnpv9tC0pbqDpQfJTYOnijHmC/q0sI/O72h10UV8Fj477tt8NVXSFvyYA/Ifl\nW+735EOmtKdYNF9bf2LzsrjAT62ZauguedAcBEZ6TmpvZv7DwwwdqsOUGJQ3\nSmJXHZ0S4MMpsfkl2GAbyeF+rQcKDkPkGMD8wSovbxIiuXd5ETytaHZJ7pHm\nQ/4+rQLZnUDN8t/FVgWtQNGm4FsoZsYFUpij24hSTspGFi9Cye0Vn2Ntc5Zm\n+PwGFW+6WM4AqjBIymbHEe3RafCOmPJ21B5GxiWSPjj3ASl0EZ5siPSsYkKs\ng4aZJhRG/Ow+i+5MsnZVkNbhbLRtIM1JQ0wjYKzQwDT9+rmVpXQLuMlSouRv\nwjkI\r\n=msqT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBvhBVW3mO7TKMvpF4i0TbqDru6Ol1BrSdzGP8pECq5lAiEAxjDMOU+Yy4VxnJJsgY9+YYFsdDYlPdh4gG/qEQseHSg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180613231852_1528932187640_0.6370157140799093"},"_hasShrinkwrap":false},"0.2.2-20180614004626":{"name":"@atomist/sdm","version":"0.2.2-20180614004626","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"434ca64308bd2fe3ee6a8d7c1935fa81b666d081","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614004626","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-32HKnrGmddfd5OzvhmqHxjwiqKCOgpsqVcC/b77DBvwWY+XDKJbCQ6XUO9l0KwkPtBuEkByXt1bP1KTCFAFQWg==","shasum":"546feef5d34ab0b86954b01313285955bf6268bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614004626.tgz","fileCount":911,"unpackedSize":1913344,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIbvjCRA9TVsSAnZWagAACY4P/0jAguK5/Pt3Wi0YqVrz\nHUHdokDhFtI53IY5n5kTexCErAScyw5QvTgV3VmGra1u4oOFo2RBVkTkwB1V\nZiYPkLvZdXKYnyWqCi3mbtv5oNDz1kaco3Mka+u1YO2yuUZHvZ/MZOxZ5NQD\nkNntwaj0oUuQHi/by9LYeCkMZuDZhpJOQ9zrXTGZ9V/+QWvypIiKc8F4Al5L\nPlVubP8tqJKP+TjiOYon8h8Kam+65eSFYhhQ0upH6hVVVrI6HXEDJeoSzfpB\nny9OGhrj7mG+K57wGnCdusnMvWU5dJ48bfXALB/E5vaV2W9GJAEz+UaNrjAM\nGCK7ibEeU9hR6FWOEcBLdxi7loc+Us+iVACiudMEUkmyylDldXXGqWKN2Yd0\nekbWLT1dmRMY8j0bBy18OPj2vb21y2Bi8t733UCR2lNHWJRqaYBKAn3nufwd\nEbigdkafgzWYl9/B9KUMyQrnozyISxMLPUiW/PuOiMyf3WKf0Fg74/RDlzkU\n7Ma/rPWkF3SD1nvpC3MVbrySPHGh5+i0KGx09tWwLjHEllLJc+DCY3REZE92\nEDNPnc9IdcVmnEyxw2BTnVszRV4yP9gxJeU0slISDzqZn5miwaQ3gFCjoqMI\nwvgSD27ZGL9QDp1rNk9pxhesgfyB/1GDRJSnHpm+fUQA8zOd29vo/ID6ZV2o\n8qwo\r\n=5VOl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDYKINQqmrMutEgnbnL66g/NHrNAs8Rj/oxO8vTNl789AiB+hdQBXz5Z1UF5i0ywCb+nAJMnrKxE3xa+nTk309jRww=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614004626_1528937443045_0.3711316441219841"},"_hasShrinkwrap":false},"0.2.2-nortissej.client-as-peer.20180614023122":{"name":"@atomist/sdm","version":"0.2.2-nortissej.client-as-peer.20180614023122","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"peerDependencies":{"@atomist/automation-client":">= 0.17.1"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"045311a64c43eb44a59db2920ba29e1c5223e220","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-nortissej.client-as-peer.20180614023122","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4tFQpkYmGhxe+cVPUjLukoLbVEaZ0DoJci+88U4BEfzMx4EfPUcCjemu499HXPgXJRKo8iKuPvd57u5zf9zqhw==","shasum":"c32a89731a4f0bd42ebfa868b48392d557430c40","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-nortissej.client-as-peer.20180614023122.tgz","fileCount":911,"unpackedSize":1913444,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIdSACRA9TVsSAnZWagAAs+IP/RszHz7o8hsgQ2r/7J/P\nd2reXBZUH+QFfla0p6bwiNQSnZ5eLabwiEmZaDsIpa7pHJZ0uCVQznxpVk8w\nOyEWox+0ObIb4DmXGL68PU0WueWD7tvgNzmN1raEIJlRFczZtZVc48HnwuKO\nRUaitDpQNai+vEchq5ItG3iFmMmja1fV0e5WnfSFTinY39Sawy7qy7rxQO3Y\nAheWJVz9i+MHLX5MDwMGuAJ1gahrp0vaqQXMFUdfGaeoSpthQ03Rv8dgYuoM\nC1X/n7ixoZ5eLBAuk65HMkxiokSXi25F9znYpAukGaGcP9fcL/sVyl5xed2y\n25zWhu349BgW6zO96sEL+im3zF9dSk+HkiwrpX9ipKrR0zUr6pyIOx1ZMFHz\nY+a3X1qxuW4ntCj/Um7z3/Ye1z5lk6eM+JjGR8htu6FolhATdQcWGrh3WJ10\n4wbsMiDN/QuDEh3sJa8p5J/VrBQesBX7hJQacTHxy/VZdD2XV3rRc10MchQM\nECPc0+3adMza+GPiqf54R/cLz6ErXxPY2fCYaapidNOjHo3pqEfszJKToEQV\nmx15OgEqzU6ABBFLmOuCYkVe2MqapGfEbCWQJULNbu9lU+LScq26s5wKjkjK\n0ZwJljVlEVBF1eyARpfJD/1hFq4swn3yEvYz59UIqS3xn3q+tk3Obcvutp8a\nL+MJ\r\n=NfFP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbDNi9wKm83fB21xNHW4FFsJ/9KsPLaDQJ4QmlqAkNeQIgYOafW4+EyolHiscS4hOW5exNPnJ0JTNCltI3idu/9Xk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-nortissej.client-as-peer.20180614023122_1528943744281_0.3921986558162325"},"_hasShrinkwrap":false},"0.2.2-20180614082404":{"name":"@atomist/sdm","version":"0.2.2-20180614082404","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"66fad8209a38eeaca80a0dd1180344e5a706b370","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614082404","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ierh7UX5OsW8AuZoS8jYRKzLPDsk3qJmKzG+/i/25epm9K1VvaL4fKwrn6o/sk/MPmlYpkanUaE91IgrhwDaEQ==","shasum":"d71b38aff26246738e3b493184e0132e8075ded6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614082404.tgz","fileCount":911,"unpackedSize":1913536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIic8CRA9TVsSAnZWagAArNMP/0Ti723lvS2uqBpQkpWo\n+3rbVZET/0Dq+/Mwly3M0/ThZhUOoBgRkw5JXXV2aN0mR9OdRod2nTg7FL3q\nbqylqKOcmxNa55tea1aQsDjf48IiL+iAgfi2AmoOErGHbS1lxITxfCm9ts6Q\nTYwBCD6hMcywxgRNeCcEmJVE78tXYSTyKK407wGwJ/Nhg8p7v/vNFOfukL8s\nFlh21RYV0IY2zJ6VC1nH0hdnbdieO7vMFqS0Hdck8vnCXsOnwnEWhVCa+rpZ\nC4EVGIeFWv5uVeXyhZMCEFWxje9p/M51nnoCBKPZBIJIpdeRNb4/KwyCEJ2V\nHmzJkb5xhziHF8ibFRY8ZPVjVNKT7/yMdIQ7bMSfS6VZv++hkt0HFPtV8H8y\nVA59KxsuWlJivHtO3daelje03dnYvGTn6Jj7Uqgy5tEzhv0Amezwu4NA9leb\nFKdav/YzSCIpq9w69hsEcWXDZoVPinpbc8d6ciMYBzNm453TRjLza0ERFHSz\nYKN8lsuIZPgP7NK6EUh1l/hPkY17wLJt2i6CNF7F1w/OQAHVJ8MpkucoYYzN\nAG7dsUjX0X3Hrd755lNnCpSnRxldcstLWljU3NgbiTWECJn1c1LcSaXscyOY\n8r3hIC05wYQk8nAXSL2t574YCOlHtCVu7wdtu0Q8WKZSbQ7SSxIT3N9AfLYp\n0HxY\r\n=sk70\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDkOoNka1otaWQ9eH6iyssNn0OrN//1TBZ4CgEzzjlbFQIgFQdwruN3lQiSiugaEjSzMnhwJJo3bKCe5HGyl2HYhgM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614082404_1528964924194_0.43226126448476343"},"_hasShrinkwrap":false},"0.2.2-20180614151507":{"name":"@atomist/sdm","version":"0.2.2-20180614151507","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c3f1772221996f0427417ea37c885c1ee43d8b21","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614151507","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-X5ylMbjGMSvHNXH7cYzdGer+g+alRFYO4vZBoNsfu10SFS/AO7jrPDRyMhLAIi88ilyw5g/6OlBzeTZwo/apfA==","shasum":"7f97cc3659223d002bdd96d5e639c0517f6f102d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614151507.tgz","fileCount":911,"unpackedSize":1913607,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIoeBCRA9TVsSAnZWagAAifYP/iy4m1XxpLO4Oi8wm7uQ\nuvqLQ3FGROMJP7Fkstj+JHpIgkZrZlDzCe8HfmF72Of8eyCwkxRR8gl9V5OP\nMX4O12m4sJNW4z1nUAej8yGo1d/VTyK8+SGmYnRD6AYzxmF93QA9zyCi/Lwl\nwqahZj6vvxUg/cO1omhVNdjnbvik4UaLjgpswOsHe5N96IJQ5fmxwt33eUPy\ngmZdngnqEmoagOOMsE3NNDcYljUTev2UJU9LwmzXeXozgH8KQMDC0Z14REMt\nCTOqeR/3nqQkTqDFZK4T5nySxRu1wmItZjFTulMUY9o25zKOcAe1N/BpINj6\n1BXxA1P4lUbi7uMi61W/kw1kkGjEvW4XZh4Zf6fz0S1jPTjY6quibNOknsRg\n9fZNwF3Y18symwZW/6gYZVXICtJOb1bAWeIbqTcPwsDKkeCLpYdyj1b246ks\ndDPXlGEpsQKiVIEpqYkPCa964BAfsUd4R5iFli4QV1MsOg11srhhFfyICCXT\nGFDgCbbL8RJcfH5GLxCaVBJ1AuQzQ7hwyvFLYKxwOiZcV/Eqs2INZgzHhKps\nT7hPXN129bKA6W9HUQo8by4W40IN1eBxBsSDzUgpKkpa/mJCK0ZvcH1NTimI\n+h/AdZ/YXjUThCPUpGC8NNZyzLG4EM4/8BL4Q2JI2vndLQ2Q5TzLR1DmTLs+\nKH5I\r\n=S+Ss\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBUjnif+imUMWYnRBMSk40SVoon75NILegU/jZT8c40SAiEA4FBygT4MGlBfl/FegIz48jCSc7G9m8CjBtfRZ2LTh2g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614151507_1528989569728_0.42423997768899846"},"_hasShrinkwrap":false},"0.2.2-20180614153700":{"name":"@atomist/sdm","version":"0.2.2-20180614153700","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ac47f9c15f7a44692a5b6079f34cc6401e49912","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614153700","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yh/B8CkHSa+1rhmzUs9sOzNjoI+VsJWbKn02kOgq60SLlHOZ0BnRLBZQ3KMpnqHEYHvP91FU7MH0rnEUQc/W3g==","shasum":"ee66a7497bfd3d97022ec38c03f1c37f3d6c5d88","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614153700.tgz","fileCount":911,"unpackedSize":1913671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIoylCRA9TVsSAnZWagAAECwP/2zeW0ntUKrC1KfElVht\n1KLlskfb1fVNEMYO+GAnyL+QCOGhMpN7xr+jvPsxM4NjkOUV+VP0M6lD9FpA\n5v3N4/ty7SVl8aNAdKL/Gn7S+sYSEt4qEiTRe7tThvFPybY0FLYp12qwBzGi\nBZY6cK8ttp0EeTQK5WO8GHASsMl2XNig2ZgfQXItmdcfSwl/rmxlKJduxlV0\n8s7RdjJl87z3gMdvzwHgVINKrOVzFpdgdvaQ+LzDvD/yiRSYZMJimN1auSgd\n/pWOmY9jHiN04FLNWrQcpRi4uigsqouK6iDeNPzTDvSHZmGOqUjYKqOyPUdq\nAY6yRvYKFR69ivE3qpByTeTlY0AFKh2HCB31y6/pQWXwiDq7XW/OZ5tsCwT0\nZNPPPu1bKYT+4/qc0dRHdDV9gf84UTQvDKuiRjJcYPWNzXW7PGTPW/4m+PfK\n/T+TOJJ3CKqcaAGSs+0uWIOvRYPfKUoHSxF/0NkngbJtpoyxUP01+GKlCX/J\nwfsnPym2F+YIWeHzJ8UELpaKj/9LdXWvbsBYEoTLxwnyE+OJDwnMmL66yPrL\n1moLb5dChOdASAG998PU1M3M30A+itTnpPwsCwbIBfrJDXmy+YgQ4v+6k5aO\nuLJ9aL2Vu3Wr7iV4LAU+dZp5GQcVjW9srTNsC7Ijkl6AWTYx0yYH+OfOpvN9\nRUHu\r\n=vgut\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2o9749fZ32VAnAVDueoUB+Nd4dMo/8zBzrEOf7xp84QIgfNaMRTCVCcGZxWp7cLgs6yETr/yEdGJoKNgAYm0ZAbU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614153700_1528990885539_0.17901728750109047"},"_hasShrinkwrap":false},"0.2.2-20180614162156":{"name":"@atomist/sdm","version":"0.2.2-20180614162156","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9d5dfd123bcfc99447f0b98db5cda7ee2b24a7b8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614162156","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OfKeY67+wF/VvVoMxKhCS/ZkU9aOMAg4zrBhf/j8JjXkZLeaMMVWTEocCkxmI33GI2UCOuPjSnqI8Od8akrQMw==","shasum":"322f823d9052a64ded3966af1682a93c583dfb66","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614162156.tgz","fileCount":911,"unpackedSize":1914155,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbIpcnCRA9TVsSAnZWagAAGJoQAI8ePY1f/C2PhjumVKBq\nNclTlpXU6S9Tl9NmvTqX2C9KYhMwaa92s63gqOoUY32SMTcpgKHYzDAJd2s+\n00VoAfhakVp/0aEw/xcXCHBKS67skILB9OBSvyhBRytQPHzDGMIF/7wfvtF8\nz76rKKy2cyW633On8365Sm7T2+FICqB2g5A0GZT6yLmnbG0fHQnUOs6LLNoh\ntCeqAkIFqUi4L20Gn7e95ViX7SRplq0D64iTiNJiaYuyqXNHCtV5aBO/rmTc\ncgDoo+GrgoBWXjtz74mBy4Pa8qICEUnFotZe6t3fImw37taP3uFI94YDV1wr\nYCy+/zK7syVbI68apo6D07agK+/Xy6h76Ug8OU1Q4YUfYcnn6p/AB6/SiUSK\nsLECl349SzsOTkRDnBLW+F1r49hObm6HivP3ZEhhIAWIuwG6N5zz2kyU+dlE\nMxy1+natxzGTO74R9Dl1kRmcPF1C7V4O3F7Mx+wUCRL8Au3bvAzF3pNhr5V5\nHAFWj0SE2PC5rzPpJmSOrfyErDy6CLdxubhlvKEarYe/9WIs+iHyYIBGvGnS\nlKzEyM3P5Igyn60Ox7XVLgDmE+HFMvO09A0j+QD/14KdeWujr3xXnoUh/uxX\neYxbT6k3AUknDtxhNG1/Ar4CU3ei5UlYrmW5VdhwjbaOPX73xsdRVzL4GMBV\n5wIL\r\n=OLBq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAxu02DJ793OWdTW2hsqc+6IiQTT7tmtIk5mzX7uncVNAiBprd98c9AILaimlXqOQe1TkehOX6BYu14TGcC6nexFXQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614162156_1528993574832_0.5893624398407402"},"_hasShrinkwrap":false},"0.2.2-20180614203952":{"name":"@atomist/sdm","version":"0.2.2-20180614203952","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"devDependencies":{"@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"de835e036486759522f65a5b84e0aff7b7fd6db8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180614203952","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JyCrqt9JkHz+efNzPUj8OgfMOjjtBXLwfUWZuFb30r8I65uvmB8jNVX0qqbg/B9Wm8uJKe8yOjjWrS42iXwHtw==","shasum":"88ef94a2949ebcce9c3d96e3e9dee22f9fa2e40a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180614203952.tgz","fileCount":911,"unpackedSize":1914251,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbItOSCRA9TVsSAnZWagAA7U0QAIPsUduKiLrdwrvR6qqD\n5WroMUAnaqZ0Ylz4JnZFzl4GIa2WBoEzkdiDqBMGd+S4O9pNgBMbCd9PMbGT\n2WK2KKvz+j1iRdznVTp32QN9C9OhXi/uWodf+tMP1Wbrfj/S3WRmVR1cAqL2\nCJSqr+LtfqgCIkMxRXqqestPHp1pyYVoQrbHFG5RTLKXNSxXhVQMaNIy3nux\nNirIhFnXI2C2Khud0Kqci/xW40UTVCVcJwm/dq7V2qbLaaXCJW92yKjVQRrA\n8YCcWXMIvsiKGs/774j03nSbAETbu1txWTtcdJbBf31CK0R2ULrpTdsld7Pg\nlRZxHlfPARSlW6FOhITi3BbpU2Vck7rvVwtcQR/IwkziJeiQYzMu5xwM4zat\nFeSMgfxXphlBRT9sIE3+CrrvMZJ/sv6vLJo7BHEWobzHQdENK0EvULQlRspW\nSmZvi3RqUvzjiTQBPrpZm1Fi8YgM1fK+DQqkXEKg26cil7CrXaImPphwAdvh\nmM+bMGjtKst4C6cWtUp0hwxwiugDfc1+jRzqOXX+SmKOrsgUbitCwphQtj3A\nY9+u8FoPpvUGPZfSCfbH7yTER79IzLlz78qv6tv+ZifSy6gLdvQ7y+3mLfaL\n31DT737lWRgnhzbCRiARlFOtpCdgUZM+cbCcUvR95b8yYtdpg3J+55ZkWcKx\n2dyD\r\n=6K8c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFkJvMcCguIn9jkQo4hhiYx/+n0jlXV86Al6UQMtw4gIAiEAmYbQkpvNX+alolkE0qNyiKEEePBvWjcsSMXgbdKPO48="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180614203952_1529009041762_0.24050686612071526"},"_hasShrinkwrap":false},"0.2.2-client-as-peer.20180615205956":{"name":"@atomist/sdm","version":"0.2.2-client-as-peer.20180615205956","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e18a7102f210c1f3a47a71b193995d4056709ed2","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-client-as-peer.20180615205956","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cqgm9dqfdy+c6lH2ROB2l17JmilF7cUewQPih7R1+C7AXvYAiHttIIi85ZJjZaeNYKSlXoTDKk+I89D8I8belg==","shasum":"4f4eb933677bec145d9f2ffa2b60206fab8b51ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-client-as-peer.20180615205956.tgz","fileCount":911,"unpackedSize":1914339,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJCngCRA9TVsSAnZWagAAnYIP/RBs0Dw6G7bBAOtGaYcg\nKaoMGsDtfxg7NwxyglDREKKJgaMR/q2USqjN+JDJ90pT93oH1bYxyM963HNl\nWBJCkPa8OzJAtZWaZ+fMZ725HBdeitS93pW7EMfQcmWhWginiHysTRR1fpf1\n2Op964ck3D9paweZLYj9mrud8lYnHTcDzWlNy3lhT7yeNZ9KO59yZdrwaRpm\nSUf37T52jNlWiV0Yu/XUx8xoZ3p86HA5ujqn5UO1u3vx5kAd5oy8lzpZ2ey1\npY3Ky9G0C9dJ7z6y0vmr87KqeT4QdM6pVvNqVw44V4KwRRKol2juLQYfI5ru\nprUCsRLHDi5lVztyhFon71aGvP/4ge5L4fhfcrgmwz7h7Fo/xwdVseNbs7RW\nHKohJDhPtXPQofe7dJuMNmqSa1YjINtByE2O4lESCF0a8Nj8A2xKLSTJadE0\ngj47b8WDRGyNEzy0l25KZSctZECI6ls5TVvez0KCBzmOoW1VfvqiVQJgs8to\nx5pWov/GLdgJhjUUUlwfISoQDllvJo3Vyy0Fokjk60Q/P3JBVLX6sEF3snGy\ngztDK8mb5MQ5YDpSnIzZsUm31iTArwRS3uLffgLmIQY4PmL55nTMJN4pRtz3\nsJB226Ao/qGoUm0rE2T/iH/0Nqhn6nreCG+tXAjittxGOKj9t81j/0ViIihG\n8LcH\r\n=x5A0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqi4WFxK+BnqA6HuZifmpBS9PahJvr105kwlWS4YMNKAIhAJNg2fnUT94mZrhbxEW40VGKp/xFx3WIFtO4X/VX65eT"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-client-as-peer.20180615205956_1529096672410_0.21813368219672857"},"_hasShrinkwrap":false},"0.2.2-20180615211533":{"name":"@atomist/sdm","version":"0.2.2-20180615211533","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"add37a2549bfeb32d754d41d16591543f62620c3","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.2-20180615211533","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zwCU2MR6SzKwRx2h3oQobWSowusihf/xeY5XEOKdlaeARCz9gOsOPR5IiaN0Hk7isD8avhSTShas/5DKtDSVNw==","shasum":"d32e5b408eab888c527a3cc677e1a68320deb233","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2-20180615211533.tgz","fileCount":911,"unpackedSize":1914324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJC2NCRA9TVsSAnZWagAAxoAP+wbQw4QJq2n42NpjXUEB\nud7e0RmixfTkGlIvWroYQr8pZRubPZyZ8kKoIiLaCFxsqHSBS4FYDnVitaSY\n1jfyjxF+UO7LoRlXYX8u/3uCdWaRltGK57Z2LjcHRT6L9ycbtbOlahG8J9Sb\nNwwVRuepY7f6+5SvqEZ+V3DsELnelmcuFw3VJ4XpJlxkY3t94M7valdELqzT\ngUNobGsknRbb3CSF+oqXfCTllzzrW6uA0wvw2RGbRWCkw4qjaYqb5dl3Ipl/\n59XxKjYUEMDcD5Zr6vvDpzHPeer6kfNiiEqb1LuOauuY3y/v+gxsA2aMvV3S\nO9e2XeBPFxy140MWkIJh6+ghg7uQjCcHcjKa4rlxzWTQeTlUTvFM0y8jyg3V\ntqtK+fjWOS5tzJseW853IioKXpg/NbJIMcGS/uOJyJQyy13dBLzgZrnHZi1p\n21Da+EggZ5oGbuC6S8XLl531TbEBqOBU0C12/9P8f4OPU+unr6KRbQ76Ivrj\ntGxlkPdcmEJ+c3jI3R9Xm3UZW0QKIKJ2fCQQTVJNw09PLd1sU914ArQHDvQH\niNF7Szy0fO8n0jJ0mhrYI0ubozANIIKUjz6+vOyIFycg/qMh5x4yWp/WKab6\npR+inMTeFfiGrW6cHQMUdbJAD/48QBy8L7vs4Wmlx59+7/drG6yaIKwDUKn5\nNpuT\r\n=W9yC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF5xlE3O9IxQp9neKT2dmcQ1+YLJO3bjqBpjWs++TtvhAiB3jlvj4GSVJBb7IpJbUciBiIgt3391NIOsCktfOE5RVQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2-20180615211533_1529097613129_0.5894426577028056"},"_hasShrinkwrap":false},"0.2.2":{"name":"@atomist/sdm","version":"0.2.2","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"add37a2549bfeb32d754d41d16591543f62620c3","_id":"@atomist/sdm@0.2.2","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Btb2QwCo28cUvoOgLQ0aGzXTYwWoXzFJEGPuFzeZ0dGzLnJoIqKdeXxe14LEXiFJDDS0Hvy/4z56Jy/iyn3L4Q==","shasum":"e1afed346e80db30eb6a01fbc047231706b18b3b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.2.tgz","fileCount":911,"unpackedSize":1914309,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJC3kCRA9TVsSAnZWagAAP2EP/Rj0QvgvOOFFHbWeYhD7\nbLv0x8oAw8smFzQHzb4nAAm2DKt3RkbBokW+RJyOf3esrwgV/12mGIYgTKsM\nSDSDgXfDp3F7m1rBjneoBmw2BxVhwhBHZ1BXM5BwgZEeyxEDLxhjUj/nOf61\nucaomfE76oU3d0JymenH9GR+dfiI8tgxaZ8wNY/peV1LRcjDpHTRddAgIut0\n6B5Um2xtJbUcbgwR5gJ92aYIn9pfkcaJzoORe23fu2yMYMUPEXK/ZrETRjdv\nk9UqtpmB19uTQrWNdDzFmVTyoC83u6KY9OdFUqP1IZ6Nz8pU7bvvIDIDhthi\n8WnwoXhajgmZhYCkqxdg6oCaLliRWe6w2Yrf5MMf8TYeEV2b8q2JkwnI9MGy\nZ9dj9CxSd0lcLH/rBVJT5afontLNSrz+p+L/AC4mmgfIxefvdldO8EZRBm3s\nuIO0rkxf8+ZywVPG3JnuevS9kfegfPl7c29R4V2M4EbxMAf0VVDkw0401c3e\ncvfhvndOyuLBY3cYjq1tKoH5kbDBy0fWEoCL+lVfadJwu36wJZiN7Tpa72kO\niEUaGvpTBD/HrMTB01JvCJbAxsbITB4liTBv1r2P44g5wlG61fouLT5Ia+l7\nFe5H4AlFytqgv8U7/8bX+/FAbKf8ySrXNDdrh2NLTSAFwTT8bFjmtZBrExaw\n96h+\r\n=t6v5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTFbdnYDfaSVyYlBUzr4GgBZ3FsbZD1Bpo0xZH+TJEgwIgTFLas957OhvtkH2z5Pn2eluf8U1L84wtHzrjxED+JQs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.2_1529097699516_0.023451095713737047"},"_hasShrinkwrap":false},"0.2.3-20180615212238":{"name":"@atomist/sdm","version":"0.2.3-20180615212238","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"86a8d3b83aa5f63533320ce51aa2fe2ca08f500d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180615212238","_npmVersion":"6.1.0","_nodeVersion":"9.11.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3EAEZ5YrxWyU1+wsgeMYJQXGLedVbA6JcaS5ZM5xRvdHNHr/8FxbA/NqnVjowc0hqdmBuX2IpkD4MLPkwm5mAA==","shasum":"c539770a9b2c5899a3e267b6a94ef6756f341bc5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180615212238.tgz","fileCount":911,"unpackedSize":1914324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJC8XCRA9TVsSAnZWagAAg54P+QFP9iyipDC+TlB4ZAPN\nK4wLcqYLGdT7aQ3beOQRTH4v7CD1rA2jvHgejaEckBo+QyQ5cJjRsoCAFBAN\nW/XggasLqFUyI15Px9FYYlCoxiBbwO9b1mAkSh+CxQzHOVwQ8xg+Jf5exc+a\n7Ov6YRI8795BinLzLiAFqfXEKN8iuCnlHfblGACoAINFoK9nfnL+5vg/1dr2\num/p8aZQadJ2PsHzpQRxvSXRdIuJa+aB1S/fEK4zcLVhNUMyYqo+yyiblz+z\n76/mVUEzYr3MuPsAUwkFkzLGpTHxLZIMKCQ3rHU/MLBpDSxEuOI2oUdG8Brs\nhV4DezhRQ51WMttuMQeYBymNGLIMf2TeuXDSFEM7C2i0AM4usq58TCCsxH2y\n9LrNwBIusR/Q3bssoNWzqFx9SnWOalv6E4kTlee5uHatzfsrbaCsnmOImRIw\n0oIZvqTlOroT1yE0fMFF/3tKlsXeVc0Gd9eLXCgV5oNR/ckKEuLHUvPyiK63\nBojNNgUU6pU6uJbjJG8iqAHiuhGe1JM4QDBlcD4OuhmFdcfKjr/Padj9GIaB\nKEzUdW2UeZiM6WS0EONjYsNa1NKSZdcUknAPuD5N5HbbMnXIuh2amp3r094n\nyAAndEWxeHGcTcuw+xeGpGkPG+7y3PEgKxHE8YWWajMuO116Hz2pDh6ynq6B\nT5FY\r\n=X/1B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICKNCN0xmEpHA9T7GZl2D2/3aDi9dsnoov3eeTFOwbZtAiEA7sRoojGM9iblNbcycJLaVoIWPLChVjX0CrUZqwW9O1Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180615212238_1529098006863_0.4429739144443734"},"_hasShrinkwrap":false},"0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324":{"name":"@atomist/sdm","version":"0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b186bfae1b55f550969b7c66f93d72fe32aa19f7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Kbt5XlqcG9zuDn9Ea2Zk/J/J+3HB1RTHlFsIPGI+1nDIAislibpNW7LBuCChu0cnUBCp5yS9ccUnzSkeH7TrJQ==","shasum":"7f99117ac75bbf62e151b973982fcfd1ff1f0cbf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324.tgz","fileCount":911,"unpackedSize":1914922,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJGxFCRA9TVsSAnZWagAAfVEQAIsHwCtG7k36uJ8u/BXJ\nBXE3S04KCCoVx7o6nrx85R/UdR/FWR0xhVyB2UXlSOEb0pGaGlhGknxTFnXI\nHXwkZ4KBAI02p5g5AnyHIT+/wAhPzCktF5Pi2NxomeIekMVUvtLiq81por7t\nOvH7cQ1CS51y1JCCgXSFjZFs8vh+zrWNA8sHWi2MMpo13MYsTg5iM5D/T9TQ\nbOs/Y9oA/bC+N/A109ArxwSnwzzivS5yHyagcR6egaiom7Mtepwn8aL2sWl2\noQaLxChG2EU7OrqD5Om0er+5sbBR9y947+SVrk5Qbb6rKPj1LM46q+Rp/bA7\nKE7rHQAYPx2jkQI69JOXjLaht5p1QfPBjP34hbXJs6QGXislqQsTlgNebtnf\nf/Ix+1S3AgUowz3lnIii1+zkyceAlmvNjlIwqzvPIJUbn7WFI0laoGuzXzpS\nRQREkxl1LrZR7i0zFWLiVivk/QdZQoIT7R1am3S4z6kHEezZ0fmxvKQrYagy\nUIW6UlcvmNjrgthGg+3n1ukycboCJJkqMQwm/7cfw/c2Q9P2zXDoVuMMOMWe\nxzB2TTzYNh8zjbIoH7KJ2L1vdtKisClJBRAsBLNRXtemfzerI25P9bw/A+QF\n47/alyuwiL7/L1UmiVLZWOX/pIIqV2i+dw8aE09T2WfeUGvbLr/W7jQRoOUH\n0NgN\r\n=WfWM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGoyizWLcuUtxKYk6PdjW7KjJD4pE8OQnoziJLUzZ045AiEAgyIihn2Jao0Y7Y4EIF7aLf/3UEQLdCVq0tV01sZCeN8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324_1529113669346_0.06958238418801121"},"_hasShrinkwrap":false},"0.2.3-nortissej.break-out-api.20180618174354":{"name":"@atomist/sdm","version":"0.2.3-nortissej.break-out-api.20180618174354","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"42cb73f5b1c4f3e706f1bf7f9ab4902da8084cbb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.break-out-api.20180618174354","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vYvOCi5K444NWwLQB+FbScQ8CGqL4yVryQASKSsVZoXy6idhYby/z436/EVmwC9725T6x1NlerVsOAeLnuV04Q==","shasum":"26d9a6c5cba7018813311a32016d6ea9e787c463","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.break-out-api.20180618174354.tgz","fileCount":912,"unpackedSize":1976177,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbJ/BoCRA9TVsSAnZWagAA860P/0MyI6oqO5OyTjiwKJIB\nfg3NbS1uB10C14gzntArbweKgbsuai+VoOkVIGDIYLd1jEf6dkBpEiu3UL+Z\nvpXO+26I0F5fFF5g/tF3FjjaLgOEAuCRGT/lP6s2oDPJMJ+zx4I6ynu/Ok5z\nh4t9MePmjBDeAKeu8agphFL+yOv4Rcvp8rQJCLV/FIhsgKi4nxr9UFlfdoCw\n0cYRDM1ZTbJrD4xvlOUFmenWsmyEEbIY4hSfI6FnXn9v7iHmawG/+mzpxCwh\nG1s+pcKtrRWrULg/R8exdIQmqh7VoM7xLI0Bb4Nqle0uwLLwkrYuRUeQghgv\nfAj2cmEqiT4hmmBQpt7nEghZOzA1YNUoYYwipRDCydhy4zWqz0zWr8BVLHIF\nea7kRxs0xsAZNdcEm4rhgdY9s40/nATiUmVWX3Mf6b5EBe28IGQaIba7qzif\nWVVQ7V5/YnpqaAGA2Q5m0nYkNAaF5my50YdsRK4kDza+S2H+tVTJs4UeM6yC\nBm9nBHxdWy2SL46qrP4A6CC8kOY17sfHyMDAtC/dDX7Iex3JTskc5YwIykB2\nlwCTg/wqhmRZgh3ard7tysrgFcKIvno5V7UJb6xQFL57cnbcdqUVTjv3zQq2\nqoutuDK60IIxfRXWl9baid3DD4Qln4QeJTafUB9bPOtU4pf8DKEomjJOGmWA\nd3sb\r\n=7RYo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCt99OeC3hw+ekHzJdR7b+bkISzOeJlZhomF8hM/XDbsQIhAKzDHQ3o/yqEAO0rhspnM2k33ucmanAGa5YEM7UzbTkn"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.break-out-api.20180618174354_1529344104074_0.02842273378404281"},"_hasShrinkwrap":false},"0.2.3-nortissej.break-out-api.20180618192614":{"name":"@atomist/sdm","version":"0.2.3-nortissej.break-out-api.20180618192614","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"25186c880598cc67640d222324af4d83e694c364","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.break-out-api.20180618192614","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5aML5Udscn790qYUSbTeeHW6pZgqjXxL9dfwSMz3qTF0+avWVM0rHDbTWc58+pRJM8TA4yI/rdVDWVJw1HTh+Q==","shasum":"e5fdd5bdf4271b8ce0c7d126bf8698a313ecf4e2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.break-out-api.20180618192614.tgz","fileCount":909,"unpackedSize":1968767,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKAhjCRA9TVsSAnZWagAAjBAP/3PlVKWCzCuBVbqYPW+1\n93JezX/muxbhN4s/HC8slfdGTMnOc1f519XDU0aKcEMT9wle8CautHmZCOsq\n5GGZVuh6nVyQ6S1uG2G5I7pLyTRh79yk0nboHY/YebdfuzCGfXgNvBg8I1f/\nsKzI3alqv33scXLjUnjWM3kli57eGl33rWvLLzMxs4ByAJ1NVPMdOnDc9l+6\nJbUK5UVShJ1eh1SHqn41bdr+1iGJKyWDD2RWQ0RTGnlYynyHYQwh8NtVLmtV\nhEqvNAosW/Wb6gWyLKRIZCjO3N8YWFFed49yDVaePPmXY068gSHl5itQIlFW\nlG4OVhkSeiQ5lkK2R9Ly/VOCQmElZKXywnDBjMwQ1IXHaE9jjFF/WQdPDezj\ntws44JpiYSLbRQvPdHOLoBsPXdXZkYRA3VG1ywIs5gYilwLTGoFG7EVJ4t2O\nIpNHXgIB4lmb5raYVwh1PtPSLJTV7q2mLhNIqhQQYDXBrGbkSKTnaF5zdPr4\nXgBirCMZdgvCptVpBLQ5bP2K4YObWBKet6Pu05dLx13o3agcs88oXlekS5T9\nw3fYKwWSUWUDSXm7ZwOv93d7836zfmLyQAVTgKsBsmGirKpm1nc1qcY59PWq\n2ByfJ+dcj/PMd7fllgF3RLlWcFBHajJTQSOo+wst62YlAVjUJ2YF7bQWARLS\nEc8x\r\n=ns9q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICz8ZyXLXDRtrZuQijgaJF0thzFlQrGwOlCBJpSiYCP4AiAClOJlWEZnpCkNnH+poWVMXfXpzFau+/YKJPAHnZ72IQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.break-out-api.20180618192614_1529350242975_0.7445737023329535"},"_hasShrinkwrap":false},"0.2.3-20180618195715":{"name":"@atomist/sdm","version":"0.2.3-20180618195715","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"317fd7fdafb30eb261790a1a151423e8bd49c3c9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180618195715","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-j6Np3t0mYNW1N+gmNnvxH6gjO0kQxVU0sZge81qQR5/4dAVaO+bGaIeq3LgsGp6b6JHFYkjm3tiDCDCIv064yw==","shasum":"55d1077b3775e12ed7ac7f8c68ac0107ad5c61ed","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180618195715.tgz","fileCount":912,"unpackedSize":1976333,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKA+qCRA9TVsSAnZWagAA1F4P/imFHbRP25IuzTqbHhrk\n36dtBarT8Xj3hHzIGEAF63lCqvMAe7GP+Uh81RdcpaZCl225pZfz9T3xQP7D\nNNjKzIpPn8/7bAdMTBdeJhYQ2QlXL2512XdSevEhIyFsj9pm0cB2VE9vqM1M\nL8ZzQmnKjmlTzrt6yxNcOeZkJyER0mj4+22u1/wNqwd/HZoZ5s8anpYuHqy6\neyHWPnNFJSBzSH7/umST79ruUvwd/i0eXVw/ajBhmYuVtijOCw3dFy0PpvDS\nXRdVt4ZXBhIRPHfxIcT++on/+pz0fz135R9ySL+sFsKFCArZ7CmXcV6Fu00N\nmC5fyMyV+07aQIPLJvu6ABfvpBal7YGWqMmHFaO3oxGe7EQkL/iW7TdcodMc\nfAoJQyaeA/OPbGQ6e0SRn24nA10nUZGIQl4i1TipU3lsNUrRb/JnGbRvmxLS\naXGY0KFI4GZ1I3Zhp54vGELX/Z+uhGMqyTX/qHETVVCx4H/hwF8wiVrGsAQ5\nAcF3mJaXKpX7ymPPI7A496rKwW9xpk6I+4TefzQEq7jO4I4Ehxbr17tJp5XF\npocyHAcGGNxmBzLEbiT0Kqgc4Cp9a5aF0kxB6feh9reQ1s6NLHRPQ/zykfOJ\nwMmTKbJfmPZ2zz4aNZP4Omlyx+Fi8RpE6DnA4vOfwBwg9CR/eN0fPvn82nqg\ncGHN\r\n=MnpX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEJqEWomFwITtFc4343UmLAoLQxpCojk7+er8OQL0A3nAiAqG0aoUVB0EETVADgsMJ6S3DhB/36mzqFgy7SDkim9PA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180618195715_1529352106654_0.1925288306628663"},"_hasShrinkwrap":false},"0.2.3-nortissej.break-out-api.20180618203943":{"name":"@atomist/sdm","version":"0.2.3-nortissej.break-out-api.20180618203943","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"48fb06956c893d97d853825445b00cd18aa3afbc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.break-out-api.20180618203943","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-I7CJFiX3nuTpTLnYumJViiTyw3EmUltlh4I1WFPUn4CvIBI7fP7ycndJL0qtAVZS1Zyw4QMEKETMtxPAeTnv4A==","shasum":"c2a0aeed61ff41fdbd1911e6126510898bf424e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.break-out-api.20180618203943.tgz","fileCount":909,"unpackedSize":1968764,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKBnMCRA9TVsSAnZWagAAuoAP/2CpW5hKBS9Qsu0LOzFt\n4j7It1odyfMzqJ7DX/4m9BfgjmUl9kzLS6aKJEKvo9nT99rV5KrtcSKPeG9Z\nKrtTk3BynN9WSkM5IS22ouoWm1yxLB4KBPpmu0euy0ZYmwMxA2AG3BD9C4m2\niBIRKneuvjLMiz268i0QVZ0UPwYKgwxGbc+knu+1ysUsiqBLJ/ZxUDvd8OfT\nYaQrp7hIrM5YLPlnSMTGWaUFKJxkJcP3XiMUFpIsD5/2lrPl500TmenIozGG\nVgGVgt8vyUC/581P2txZeYxxxpGjdeoX6VNMImX0D1UjeB5+qk4Fj2bmZEFo\n4+YF7GK0jBXLfapTdx7IoImSR9WoAac00BeP+qAzsELRwwvbvAPiJRUFjRNz\ngbXINvxqSEpDRqkYfT5SZTMF0U/WuyvPgh02FuYinEIlsyT6cy7yZZqUH1t7\nDGKeulVsnc/zGwidx2CbjacbFCwPDMZNX1JA8KVvZlqi5PfG9+qlvAEIk2JR\nxN98unP+985dCg5fL3BvEBBGPsx5EooaL2rhCu9ww0XjEO8HV0fj0nmJQrw/\nbqRVqX54nqewShd/45Sy2LCJ26RtUIKuaeGYRsfyuwoW9jFoepHLQD0YJ7xU\njLCFEzyxq6z7H7/rMhkR/lPmKzLQiD7eps3cYJWnrhtQUvUxzl51004QeL5k\nzB1O\r\n=xshT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH5JGqWWWg+S25pSCdfMQTxqsySkamKiIrWk+x/yfYL/AiAPPZznYr7e+KyEYd5ZxsHmF+4P77KoxhYT+nDDUF12ZQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.break-out-api.20180618203943_1529354699873_0.3630214768337694"},"_hasShrinkwrap":false},"0.2.3-nortissej.break-out-api.20180618205826":{"name":"@atomist/sdm","version":"0.2.3-nortissej.break-out-api.20180618205826","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c53c0fa9247ef6c0ead91a0352a73bd9c1cf33ee","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.break-out-api.20180618205826","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PKOQyJ1O/GLZwfqQumtB4uTKlpGAxc6vQZSBf3hn5bHOFlSBMawrWv1YgiOO5VPs1HjIWAund3/in2QcpXh0jA==","shasum":"ade418152b6f42508ebf3b9bfdda09d50ff35795","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.break-out-api.20180618205826.tgz","fileCount":906,"unpackedSize":1966825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKB4ECRA9TVsSAnZWagAABUkQAIDiIFJ3YLIktKYocp5M\nGQ8zoyq04oBr8aOayCUwDSKZhSJ9CsVAECnx6ef6KDBAm0CI5Mw62c6LTfLa\n7u3yjSHPiVh3uF3a4EFqz3+rpfMPLiFadn8LVqGwM2p2QVUxyPj5q4IeA1Pf\nVkz7GE52dx0N1WIjRjJ2eCg2UBM9PLKCswahpcURbah3InU2kIj9iiJvMOS2\n4XxkxQt9nP4dPW7pGgyQlmb3hPPji7q7Lja89ycgIFmNeGLjJEB7ZBpjlH6V\nkb1VXcFZMm++3MnMyYyhKkA2dzDuhcR/v//85oVHJHcw1MeT1VzZ53i9nFw6\nznLZyVNueI2MScdL1vGkk0KZ0oR4LKKQvgGWZMu4Qa4bv/uI6d1S0+rdcJ9Z\nNvrnxJNF0H/L9pOYB2GFLSbuauircF/QfL1yN4pOEZKXwyMnBOpsbBbs/BXR\nGCthenKK9W7twoevxIKQh8sSsNNotMTjmifJReoGUxf7G95ST+NeZEHEncHw\nETMh1H+kalxji3eA1HuSBCjOCNN9sf+CD76tz5kFvuLFXoFNZ0VhbkErRy07\nv1TGnVz6IFc5deRgI5TP2S9NAzQGRiL6XZHnvRX4nFHPka0FWAqkg5q7z3M+\nbS+24fMb7OQ8I1Bf/XygNViXkIdPDO9k0N356porS70ijTaQsUsFIs/If8mj\novAj\r\n=jdi6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEusy+l7184O9w4LQyuhqrj/hds+S+Dpr9Jgt+nwTXluAiAzPVQFsFBkBdiwpy6okmaARJqV6LSX5461UCh4t1EZwQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.break-out-api.20180618205826_1529355780330_0.34853522180868635"},"_hasShrinkwrap":false},"0.2.3-nortissej.bb-curl-or-axios.20180619000533":{"name":"@atomist/sdm","version":"0.2.3-nortissej.bb-curl-or-axios.20180619000533","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e323128bbe2a6c57a15e21f91e2021169cd5145c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.bb-curl-or-axios.20180619000533","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OTVstfYdW/A/HvleQkyrGgK1Eq+E4r3VVJjA+t0Puo84VdEMlASrwox3KyuU4/S9BnykSZeIxajOxbkE/49QEQ==","shasum":"fc1f8d24c471e73a2a8e785c6053d4ff004fbfbf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.bb-curl-or-axios.20180619000533.tgz","fileCount":912,"unpackedSize":1969343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKEnWCRA9TVsSAnZWagAAPXIP/jsnpWM1sxOCg0ni8PB5\n/K4uNBBc4fzkh5ZEP5/oaKkQxd9IDWdsqeHLm5UPsEjho1ZaFgGne/PcSBW5\nxnKZ56rv/Kxrg8Rre85cquKV5l7Fj6yyvb0JmvM5zESWiWVMm5y5Jm4QOGI5\nHIvbKrNY1jjIR2bEnS25a6LyF0rRRU41C/G2buyxXTO9cPAQubR8+9DSd1GP\n7MgiElZ4WKXbQoqRluem2khzAmtlNL49nk0cAKrSbFTSJA2oSg2gA3RwHk9L\nF/oJY1U4+OMX1dnlZTrM/G0x3U8Dstp4+KYFI5xVNKmTav2ZbHq1bPfpgmFw\nT3RGZZK2578TKF9vgN2nqr2rEjfJu4LV7J+V0Bm+ipAeBSVlijMPo5f51/7i\nEoA+kd2D9uRYN8j51yOPBLI4DEDbxiuD7zRpimQuUj+aJ+PDGsaiBYM7Va6Z\nCGiATwRsSya0Yun30MABUgOrYXTo7XgWxkCLKJZRdgfCue1MaXPK6eyQQxE8\nS40+K7uB3x9iml19wVEze8PAM0Qt4j2Sy3VR7Ym4B5QtEya0ch6BCmLNr84Q\nLDK68NfcX+Hu2+UuUGEliW2nPB5qFSCIemsMorgz8x0pywupqP07KQ300pEY\n4y8AwaSFOdUtAh7tpeH/ga5TFg4dzFcokva96ePtW0JRj7xgAW6ZbsBl3MYA\nNrB9\r\n=shRQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF6ACyFVsID8QwaUKYTCcY0J65fticg9iwnaVnY0m+/NAiEArKW8VvDwbqCSWAQMZxcjkwJLM2ij9AquCDwNiWYjQNA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.bb-curl-or-axios.20180619000533_1529366997982_0.5971806001561737"},"_hasShrinkwrap":false},"0.2.3-20180619015100":{"name":"@atomist/sdm","version":"0.2.3-20180619015100","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"358aa61c0114b74ace83b0450c6120df4fed7141","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180619015100","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-I7VdvPTmhHM1t2YU37ZEvYoRL2FDHlGNXs7RkhbPxfJ5B00JKqUKe0gvXZTFcuTOK51j4xgMBm0hq4p4Iv4TmA==","shasum":"76ad79010a6d66d83e3dc2653179c42f116441c6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180619015100.tgz","fileCount":912,"unpackedSize":1976310,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKGKKCRA9TVsSAnZWagAAFV8QAI+ljNbYOUhU4qrpDPMA\n5UfOzcL7LpdtqoxrhN6EO0Moml6Tpj29aZP2zqlnIT6g9YMgS9DI8PLSypSC\nMXNCMCdg6rXvy0VnQ/DfGMhctikCuRXelckG5YCu1dy2XqmMzHdDbwuRVy6n\ngtiyYr3FCW6od3Db2dOP94nPJX5jDeArm0v25j7I+E+lFJm33p/68TNRijqq\npC4MIuhIgmZcnUPAjvEV7RLD0yHHFOGP1SS9HkqZM1XRH3CHvjnRStaznplC\nnhXm9HchZxENxszUJ3yYTKLIxMeufS2pxB5Bf4bhOGsHLQ/sjAxIMII/SJAf\n5O29PO4A+5Be1dPM7AmRdfIb1q/0LT3+CvNNSdGdslluHW/MPEwJAN3Ut2Yo\n3YXYp/dIRmdQpdeRQPvBB4viC/71z+aTxDB0kkdYr4BKknfuh2LxDX45oKym\nIngJ4JWUeSn/f+egYzxv3edTJjMEOno1AAalIO4z00zk1uaCqZq0iyssuyxt\nVe/WhAZ29Zg4sOtaffKVDcaO1+0ZUi2+91sG0WEWKXRMY5DDGt3WwsjA7czK\notSkuy/vEFwfO2fwPs+nhD4WrIbjKnUcp0KkrEXegQ0bKtwpRRXieXeYBfmK\nkfl4Wb8sBgeyZVIjrrtVTfmAu109SOZO5i37UDHnqAbB8KFkiGQbIj6K51m5\n4ORf\r\n=i1A8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH+v3aQi0K1prT4besvKAA0xG0/Rk4v3CoLqjEQQnaz0AiApymO5UF4dPEmAcuL8Zih1ssgBKlRp+XH/2NgBDsee3g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180619015100_1529373321947_0.7832391156900866"},"_hasShrinkwrap":false},"0.2.3-20180619084714":{"name":"@atomist/sdm","version":"0.2.3-20180619084714","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"92f4c1d0581be3b1cbbfe1ab1a0b25f6da43745e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180619084714","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-biI0HWm6yURvNUAjuRDRKqzKQUGNSuk3nB1GEBvMkLJq5QJwL2AWfsmJlxtgZ29j/vC56EcHkHC4Z32r2C77gg==","shasum":"3294095da56689ebd26b709e51f2496be6e170c0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180619084714.tgz","fileCount":912,"unpackedSize":1976617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKMRiCRA9TVsSAnZWagAAzD8P/19QamK1eg6/TTvuNnp6\noOx4J47cBrVMZc59db4PxHNY6NBFou2SvrkwmWxGMbZtI1LhuHvmDFb9u3yX\nfCcBVfro2II1PK5Zmo+xORvnjdQ43n14NcSTbdndHrpGWHQSllWmmfj43gZ8\nl6yF7udpF75fpl3PynjrVJ4nUN55SPk/1krzY1ktYf5eztrSNk0mZCTikBmq\nGuf1tp+vos3U6QdFNh7cFbrTKx2fgSziEk+tDyhpJP92UO1FAxNP7UuNjOUm\nfk/+mNPuSxqMBeVX+5UlnJu2qA0QHHjq7pThXulrG5wquFxOaJgo/JGwg+BQ\nXIUwOBGkp12lAgU35JVN4cbkR6onZa6dYe43zWI6jzSZNxY7iIU/V2gaydvd\niKcp9nf3gfWSxxhrL4ZS8HmBWIoOzgKz5WI7MMSq9Tco/0htr+zIkz/dya2p\n6BjKpsEaAypNDfP31BujcttH4r87mQScDpKuT4absx0A1a99N2j7VD+gWkim\nvlnP6pWi35m5W76IwudpeLZ5YorfAD5ybVZWtygTSulFDieZji6JE/zBP6m9\nHruNbzYd6volBHKfWNY6CG2ePU7E+LFLoBV7HxfpoVdNZtJwsR8T5G7+4G5a\n4GBJuTQwhxELtmr4GZt6qjVXuie8auML5Q8zzw2GM3DNjZ5wrZQczNt0fGjl\nQQuV\r\n=cTYN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDe7D5gobqYqxor5gRi/snJxt80PSNp1wRej2SxH2HXFAIhAM0EQ6/VwZV1ZXskj9h6yjiJRoThW4zJlDNd7/IEfHvq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180619084714_1529398369804_0.2621880263881211"},"_hasShrinkwrap":false},"0.2.3-20180619175324":{"name":"@atomist/sdm","version":"0.2.3-20180619175324","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b91818040f4948e5b22331cfea8995cccdaf19f7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180619175324","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1ja0O3mVEoqk1FOXMxY93dIWWfYz4zE63E9LaNAHaaljeFXES1Y0VxQC5Lw1F2QkXtuOWJdCAk4NmfqrosiWLA==","shasum":"e173611764cbcb38593f7e5ecea524374f993a2c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180619175324.tgz","fileCount":912,"unpackedSize":1976870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKUQZCRA9TVsSAnZWagAAHyQQAKTJGFj9X+WvS7y6O2nF\n+ao37fkVydXqQ5M0mvXYZR1CEeWj5SfZrAjjpdzFarCih0+YWXzEytEdgTCP\n+RCGZgGeEH+3j9YeA9BJ+yWfj1bcn++BRXZRYAjMTesQ9wb1vn7czhTkdrVR\nyxdH0F6G01jqE8lvnVjOLE2Oyc4FFtfyLxd1DyUYjeuymwGHsSxwdOXP1hdT\nDpc74tgoqatdQjcUihuxtokuzn3PhDTOzxRDa9NJGwz6y91uMfTaP8wmlfjM\ndB0uaJDZXm3P7YH29OFjqJARZzLv1ecRkdFO0iCDNdVfNH6762frYMfoB5d+\njiBSpvgODMwfLu8z5gJ14v34tGOXA7GslvagWl2F7cxJUVVMnZ1RjTjfANJ3\n/ZKdgpWovFEdS+zMzhZzV0TpJmCqOxlu2hOZkyfAnYguLSMcMHVYU7rgyTAr\n+oyd0XzlAJe7QQZlMRwg/lFFZWoFNVEA1n+3LQWQBX72XVx3yygEliaOTwUF\nJSG07qVLEQ/JrKs3r0K6p1SjYCsyQM5CrGbm1E2qC3p4UnaUEOlW2oqDNY4Y\nqOji/N2ZoXpss+0LqWv3jg9Y+l0hxAJCXLDsj9a2F4VG77g6PSLrqzGCH4zg\nGdVm6VQvqg186qjpRMTQJtDMDB2An+CDdz4zeb5RJhlA+SF+F7Z98Ap8UESW\nH3li\r\n=4FDe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIenG9dFEhGI2Axn0nCa6HiafxqZjY+IaJuX3xpIvtIgIhAMrf+iyR5wOBlmKsE8xuKySfc1EIHJDNmjBfU4jtJ0ZI"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180619175324_1529431065473_0.36171178725615794"},"_hasShrinkwrap":false},"0.2.3-nortissej.custom-seed.20180619232043":{"name":"@atomist/sdm","version":"0.2.3-nortissej.custom-seed.20180619232043","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e19993b85b06376e0186b373cce14176a4cacb55","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.custom-seed.20180619232043","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RTy/F8ttwpCd0l/S8tql79hjeLjuonGH0oDFZU4/S1feLy9oFKSyWmyXzKjiiee6UpAgaxkg3PbLGH4t0tyidA==","shasum":"dd0526353a355f481fe6b4bb31273c654bb31ba3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.custom-seed.20180619232043.tgz","fileCount":912,"unpackedSize":1977568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKZDjCRA9TVsSAnZWagAAoDAP/iWWKLrBRGU/CTYjkAmC\nEZ1m3t16sBa38N0ufD97IFFF8LeOBZ8hVAIzzaWK/dYRgdc5x4E8BBcvdQOG\nUyT/qOQmUoe2b2NoGI0sbNOkEwJq8Csw/5LJ8lwv7MhlERggYTV4Q+GVQJcZ\nIZHvQ+rZKtD2J+lcbN+g8LdrtDiR8tK8cRf6ULzsu9QfA74GBAsggpiyzA7R\n0qj6OY3cNSZafco7iq6e/Wi8aHkCaWTIvhBNPjJAr5tgaTKdqHkjnZ6jUVoc\n+2Vke75Vvc9tV4O2DXSTIJRdKggnq/g48IWu3oW9pihnxLhXpwAi5ECBsmCM\nIIOwhZjsKaAh8zOWgUzr9nIt04iTwT5/etiq++d3cAe3TObDP6CXctEKdjRR\nJvWMqKppANUAbDeCsbu6Gqycf25j7HnWrxxlxYL9DjFQkt4RSJk3xPMdOd/z\nsenULOfqezhLBqlTiViLE6ESDc1pFkacnMrpILp51U6upIxTzuySNsQiLPvQ\ndPfcWgm6OsW98Q/I/x7+kS1pim87JqZYdMtWorFt+fgi9VbN/NUa5MPfxGJl\n7dlLqAVZhlLHlaQ0nnIe/K/OroJemNtuj8Z0Tf22d5v74SqMI+8h2sHPHahu\nI0lK3aYVdevSehv80+haU0iWZYHOfo7rmhbleVw0+Jtxh4LD/V1kluTKTLGy\nckbO\r\n=k4Bk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDbUV9UbikEyMZdjn7yUGAxmYgzwI85rRSEhQuriSqS2QIhAIq41QiqF+LQIjy15BmxeH/IrF72l1T6xGBLTITJP0sz"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.custom-seed.20180619232043_1529450722920_0.3293738959175838"},"_hasShrinkwrap":false},"0.2.3-nortissej.custom-seed.20180619234931":{"name":"@atomist/sdm","version":"0.2.3-nortissej.custom-seed.20180619234931","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"048377f6dd8182c3a8c9a90bca0c461b51c5d05e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-nortissej.custom-seed.20180619234931","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uirVZ3v9nxvXtxbFurTBJT7f6m6FVu8RwuILEGfClkWw3qhbOylz0Gmma5rrmFqsG5fctIOStgUNlyBLsqeLqA==","shasum":"0f20f47c4fe531e2f5c9265975fc9954c04e95c8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-nortissej.custom-seed.20180619234931.tgz","fileCount":912,"unpackedSize":1977581,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKZgLCRA9TVsSAnZWagAAcuQQAJG4ebJwdTzOSwl6YiQP\nVvnklAIRJT3+C3L4NwdgeatuC+HRTKJ3v/ARdDC8nRzSb0ZGu7OrPJxBU8QE\nz2UfqFM+7Q0Ce5XBzC++XoONNEwBvWOL3sX/Bpyhu8+BFmkcjBcuOGSl5nF6\nRR9X59np0JeUo3XRw3GxQoTJJT4KGLZwgHSEkEFiygrkqsC+NpqhBD4/LFIz\nR9t80t0so27liXJFsJA54K3IpGrnDji7vVE4sbDYCCpxXoKXTF3bBr68zLpI\ns3ekH1FuFAyWQMNw1tbkKfKEFwenxqywSeumPRZsIqy9V2EsT+N03nq+gRWA\nqmGGTLg3FXBhxGdq7Q1z+MjHKPDj+H0BgfTK8JOg/cOXYirenpAuN3I9hkSt\n8TsUKrHtBFNOSbUY1lK0AMtO9kIbc2QYHtpzyvd6nv60gFRPKFs15bX5zDvq\nTqRL1A7La++ZKVzbwrrT6rvbUbV06nObMv96NPxANX13gxkH1MwC69Narf8H\nyiP7jPi80QxKXcJUkYFRmodKaq6fR9MYWy6XnAFI4NYVczTFDV8U9hiaI53B\n6rHUKLhDLjKxisGz+LuiQCDJ03lVGptUj2p1kz6tiavf2EkxrkUgzsu8cF5J\nHulohS1Cjf83pUJQcFCS2DtRS9FdgdM0Y++hbtoD9euv6mdQNPEkrpjDK4U2\npvu4\r\n=2JJE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBifF0mYP2+LzIoegmH1LWmbufwmzzpxnmf1Qq/hVwm9AiABXIVl8J8vTFd61ArFCLbkLlvHsetm/K7MgN767x9hNQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-nortissej.custom-seed.20180619234931_1529452555030_0.06265239498209052"},"_hasShrinkwrap":false},"0.2.3-20180620025559":{"name":"@atomist/sdm","version":"0.2.3-20180620025559","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ce0565dc82e54992a97c50e277a38a53d1ba62b7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.3-20180620025559","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ywzhqV/ZNgmKz/FtX7ooIxVqvPblnaVBwMRXh1wHDxMfzRZ/52rPQCnjfdQNbmc7K2I1xSsBvuuRt/sJJDzIiw==","shasum":"722821117a76cad7db7df2da977d876a42067652","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3-20180620025559.tgz","fileCount":912,"unpackedSize":1977916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKcOFCRA9TVsSAnZWagAAWMYP/26wajGrpuNkTvdrU+pZ\nmR0iPFNEVlNtMlRaYw4IALAxdpac8sEa0qU/UYiYN8V761qj4uajvfidqy2s\ndqzduTw2HEjyFP6/KQdxvT5caRNjkXnkdaQn7RBOXQu1FXDVfxn6is4l30n+\ndcyVA8XoVfJe3intgt8Zcpcm37Xuq1df2vmnbN/DIDQ5+9iN5S69kaRpzhda\nj5GuBFVpyg6ecd4wV8k3oYNfYMPX8NLHQVb3ic5WYUf9bMv48eG/J0Xe+18o\nNrIZiIbWvXKwZD6ppP2A7cfSk6teajOovDFBRf9ZU9lw0l8tVrRWjZ9oGvBa\n9nfI/ewnGtDxtv2kbf29ZtMcjFStzmIKu6xvi5TEp+Ygbt+Q9qBpiorNtB9a\nf39ye14BcYPN/4JZu+bWReDXHmQs/eywwaivbFPg4ap+BtCSEv+Lly0Aj/b3\n3X0k7kGRZolmQLemFGoZfGZ2JDjCYZKpwLW1KK/lkYYb6M05vca2dSZUznSZ\nF6OT/h3NjA/ljj4THDiD3+sl8uGgF/XgIED9l8MbUDjmTwSrABknR9d2KNmd\nTwS+mfBfSXEG1baXS0KOCVGSP1k9YAJ4D5Y8T4dWcJ8HN0e/31dMMvvjClRV\nR5okNKf0ACKEVGQYbUH8AJZP5XgJs9ipiTqKEHfM39gwl+0M4AghenVpkkl6\n1+ZB\r\n=Yt5u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBW9rgWfR+hsu9Ikdc+ARPnniQraGQz2sVorKqxA7y0BAiB28y+EO9xLPU7H+sZKWu3boV/mwc7dSN06LMY7Q02MPA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3-20180620025559_1529463685461_0.3282962195456207"},"_hasShrinkwrap":false},"0.2.3":{"name":"@atomist/sdm","version":"0.2.3","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ce0565dc82e54992a97c50e277a38a53d1ba62b7","_id":"@atomist/sdm@0.2.3","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5g01YU9EPvztTRpAiK6szicZYKYe4H8AvAYvZi2/wPELIljHcRKQSjY0Ry6ZFttACyDWAh1TO5oCDd+5igSavQ==","shasum":"f373de3becd7df2303b6b35d18869c837e491f04","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.3.tgz","fileCount":912,"unpackedSize":1977901,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKcWICRA9TVsSAnZWagAA8RQP/jRAr/BHa0hqARJb/+rB\nSQpETrkxPROgRiAre+t7+QDQlH2mQALtP+Mw7UkjWB9LQQo5Ht7OtcdmC3vB\npCK70cyIRw9hGZlUVZXzIBVuMwJz7aEmgNB9q5msdoA5pv2ni4LOKq5+vPyX\nt3GgpaGxW2TQEeXHBJkJP1P6BZ6H/LpEpXJT6+bgJFiLi7Z/gkEGsPvQSUCI\ncRIBduM1BXq+s02DQo6Wq+UKtqg34rS4xGDHQav6Syi7zio7vycKBfPJcVrt\nFM5E+aayaZ6zly+niddKgTNhj89aYB5A2YSPxiOvMC9xTIXP881NJoriFgw3\nB/XxGXSNWpwC+WgctHYitdIS6S+5x66mx0o0X0nDIWkpYTkZYHI6CFuQfXMW\nGb8U+5/FCyKgSn/PvpNYMjzXaWgNtwgLXbfcopybc2v8mWEkoZjy6Nsmo1y8\noknYjce7tRtX7LYJk678YzobTJpjvLc/3Ewse1r1RsH1mxj90g6B+jv/beqQ\noUYg9j20VZNO8VkNA1zyMPIRfjTAuA2V/T+LTweTDee2cgPblcBWBUgUiDz3\nDAPtUdIhTRSCOpW10lFs2n2dA9jCGeCzVzNwvAZSxUD306/dmgyxW3i3FKrU\nGPIHsunHQu9kwfRgRIxlie7dwHG2qj6WpMA4RHZX/oI01dA2zbQGOYyTj/1N\nlbIJ\r\n=xFF6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH2my4eix6ky83yMy4SUQOS6E0KtAeNWhs9EVciB3rD4AiEAuz8z3J9f69umzrN2yb9CuJ2K14EvoqlxXgr/QZ61++Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.3_1529464200032_0.8358141915143622"},"_hasShrinkwrap":false},"0.2.4-20180620031154":{"name":"@atomist/sdm","version":"0.2.4-20180620031154","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8cc4b5d97e9b54dfb138f70b50d67db4cc039c89","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620031154","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DWBxebB14NQueBu3gTOwhPSLX27doAf6YcGkj0/+DC0dcLJq7Z3AotwZRB0OhqadfHOQ4U6r40YwDxlmcyvijg==","shasum":"d43a6491ab08fd6fc81b94f1c8292644eeebbafb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620031154.tgz","fileCount":912,"unpackedSize":1977916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKccZCRA9TVsSAnZWagAAamsP/ivaPakXVKRFl9lA2cPW\nykmAGJQB2PD6cYIF96jD0nz/a6Hch6tiDQLfOtKmtyTZgUkdgHH4yXqlADxR\nXnVDPqhn3nOkAJdKgteey8GPMgTX22yPKE1iEV90eHQ9mnUQm8rkQ4AesZdW\nJV1J9zFRjM9LGfapspvHraUzPTEAB3hfaQLFVD3a/Exhr3Ou/3v5PvRngK/L\na+ERMqGKkDoyx1p8o5DN3yfmqqfS0dxeInSIkbE2gXRXpqHAk0suQaC59oqe\nazQBHLOhrspCVmV4d9uwXAhNWuQFCri5kdlMBJ2itBLelQI/ksDBpSJ8y9/O\nPGSB5xCIDjBmn4xb3CwXXQgrO7g0Td0l6mvPE56W88jt8eVc8WO2hwQnKoK+\n4TrcuHlC5AORb4i9CeVWAPaB1QASUfUw1A2/eE0cAPNUThKGlp9lkG5dyWdW\nCUS/MCqgweqltyl4LdvWA3G/wPC2x9p0fU6sgwHgfSEqC+y3f5e4ZXRGx7yc\ndDf1qGiEBGYxtNZKEKOn7hmNB8UhnCb89n8bj0/ilA1DMyvnNBX3q/J5DKST\nV1m1cgexbqdZyPXm6ZYABaP5+4tTT74eEUkVZ4rz7v0RYbp3i3QcjE1kLMmP\nCqiGvBF+WEsqgjp3hnbir9TREYt1yac6r8BwiBVgOIpAACn1J0x3XwL3Z0BX\nK99p\r\n=BzzX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYr686RNJKOp2agF49+DbHRV99gsDkmGwKWfOdX55ZAwIgZShYAH72sqtml3aElvyGNp+fmka2OxcWbAWypDu6wqE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620031154_1529464601518_0.3830476680186379"},"_hasShrinkwrap":false},"0.2.4-20180620070909":{"name":"@atomist/sdm","version":"0.2.4-20180620070909","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2addb91f3fff47174ebe701fa2756aa6ee4f57af","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620070909","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-919pGWDcFvRDpWRooxsSw/33GdjtzAdEqTunYVc0OFKOpTTK9FeJAq/MGVoVwDexeAkq4XjPqgCz+bey+XMqgg==","shasum":"1081a8c0423bca3353401940de96b65e77f3b03f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620070909.tgz","fileCount":912,"unpackedSize":1977971,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKf6hCRA9TVsSAnZWagAAZ3MP/25VCXoFYj4ocUP+7oQO\nRxbVS4ctNexJ01gu016wQEvhtnQvPuaTcOxq222h5ck6U9nsg5ifOGw1TQhc\nznRiVeKH1JkS2SUNhO651SqvalBooHl11fAH1PO75g0RJJktn2kNzOFKxqgL\nK6Y2OPOsWvWTH0GE7VDDyPufj+zVJz7OYguWzKBkoAs2Yof1vlnhwl09KDsS\n5kSiCzMletobDiVYfUQ2/133XDXgVriHYj3vbeHLp0qxNHpK6ULwtWWxCIgY\nHTvuattm0eiOWhW4z5PViFlM+hkdo4lsaLvOAMFOiZiDZXdgTj5S5K3SFosP\n8Wo17B9x0JNkPk/xdwq/dup7DH1ZHAhAy9bDcfaSZYQeqszPaRuHqY2R4V+h\n9jNJsBStwI5OKz7aOC6QBYfVHptdI78M+2b7AH2J1gOMfSQ5NRdQ2izfS2ZJ\nQcAbClWtEbHr7Z/AmVxdpyAcNDY5h4yJVJ/q4pe5tIh5zc42VxW8GTZ01MBb\nIgwjBl5ytGSMmp0SGkoBnCW1AncIOWh+Rqpa46bgAh3hwLLlfEnC9VK071zU\nXZx+SkNOUw0j16PG5Jg2JXIS9va/G1zONVATFzwQMa+MMHpLh+g2Bw2KCAK9\nGpCASIn5qZ3CUFaq1XoPkDoRQzVHzulmHqnAphp/ibz0hX5vuuR8XzU2Fd7z\nvZLM\r\n=fYSH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFa5qsNljz1zNXSaFBgBcc7NQlKqNcdKHLjXAcX+5lTcAiB7jOcDN9A/0+Ea3X9+2bF5rxGeWd7uL8jXxfpE/7NU4Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620070909_1529478817456_0.2674180309768024"},"_hasShrinkwrap":false},"0.2.4-20180620095255":{"name":"@atomist/sdm","version":"0.2.4-20180620095255","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0b6793ca18b11c499de92263dd675217c777c73c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620095255","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qKHXHjq5YQpN55n//Mt/SznPqJPC7x3ERTbyUnU8qlyXRRRPo0vnN91B1PEWiguJX/oyPWyYe1UWydJQEHFNbw==","shasum":"a9728215641326eec7abb7c71dd1c6631308098a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620095255.tgz","fileCount":912,"unpackedSize":1978099,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKiT9CRA9TVsSAnZWagAAhGgQAIxYBzvKNnXuO2W9nOjh\nR996VjE1qbHDzFEGKXQn6fPNTagUtR1VXooLb74dHc5zrGaFJ2IHhCpOB/t3\nHSmTX+2AXqIy9XTR7/cNX9DyESv3lHOULp3AI6qiKcK1QuH00OzpPS0mWQIs\n2RZTeP8yZv/z10XCVK6T7YUNnNOFklx5+ivpCP682Rems8IRUsZEGB5tvbY1\nN24dAjnZVRtribuX0TjAJtb7afC3alaDJzHNK5k2IbODDfEokisRQEOwkvxs\nNk/QNuFXL9D9OloABkf9mpyWTB9WP/ktg1qWt/QK1MuTfufLUOVyf1rdJZcv\n+tSPyeOhn8UCF5v4OdrcFBkPNfASueKm+wn0bJr+AatSi7QSnlyLKnofldaH\nipH2PXnwD/XLb3CGy6LhVLXKsXE/pnxii2kiax7uBXlVApPXg2sBsNOgqSXv\noB9Y+2hoxpNoqTvVZsXvu+rF+CiZWVMWOWeXut72On8bUx0wsSsFFfaj9CpR\nIQZc2ODbEjCW6sGMMRBqbVSUF3VeGG+PwYjVsYQsC9rV86sFO3MwXFuQU5NX\ncDxpdT1sERHSaUUiZHvICcB5nvUpAXY2yrjUeKh9+H/98KBn7uL1zRa9jOih\nncOvwtTf8gWGhXYAjqf9LezcRaGTJ/S+nxcUq7vVE2WqO80kNXHb2gMVlX5C\nMoFL\r\n=5zfR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCHGjfbJKBFVY6YkmiwuKGVQt/+OKNOhqh6xM/he+ZvVAIhAIvKQjgtBAttaQR4PtzYlaDa51NzvcZowhag0SvieMP8"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620095255_1529488636895_0.8792386966005914"},"_hasShrinkwrap":false},"0.2.4-20180620102234":{"name":"@atomist/sdm","version":"0.2.4-20180620102234","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db678f2126125b4908913949d9cfdc7b7d5803e6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620102234","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OozwdLnSx1sWDVKdSwPtZ+OCeogLum96cCYUuZ9W0fBKpkT8nT5QsaMT44ESwHvebnSmiCFjzhaz2jssW5b2kA==","shasum":"b2bb0ffc1ca41d4dd28217b2d60458b24bcd3af6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620102234.tgz","fileCount":912,"unpackedSize":1978256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKivnCRA9TVsSAnZWagAAWI8P/j4F/Lcwx+4xLYw3wev4\njo38H/Itr8rmAnR0vPxIUC/neVj0lSFRZkw7jFgzioC475w29OYnKRsecM+P\nCnGxCvEbuC2ahQXlPVqvNp9adi1AnKSjdVukc33TFZKTGv69yuIzck65F/ng\n76H19OS/qBoN+zlKehFmGbOf1mUNY2Zn7c5FM12YgXPy0R/DaZUrZ8WOML7A\nElEU+k58/jovzpNerPJneR6O1ShK2+9GVc66cuMuHVbG+HJz8SC6aXNzAcJn\nLUn+wJ4Q9jLRSipI7EniUu0rYr13iRW9ovCvbk/AslQ7kVuh+yOlLG7En/Vt\ngogxnKMlmLX/+CwhlU0CPsmtLXCT5RLIaGGAz/it3gHf5FKjpP40HKxC8qPN\nyT85GsPUuFW/F2Bq3lrLBX+oXRUVBG5QKcL8TZB8XIaYCnGr6dxc7rdyDWaT\nCS8t5flbWBkkc6drZko4+gnlDf8RNDznZd340yjfh+zncbN+8IUef2pag6HI\nBDMRuAs+cHzYaMUYusWdrrdP1DiMAeDEhC1UTTuOjLX47C1B487Ny42XyS4A\nAkG1OM8gVGhZLpLIi9Yt5IdB+MKFAYhtmsItCJjsvoleDspJ+Ifq1QhjLaVE\nthzF8O/FbA2s3DfE3J2IQxTBCUcTp+axRpgZ5b7rI+Yhdzy/SCGvmmHm9RYy\nbivr\r\n=aemZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCl5ocdSE9q8UA3vXib15pzGYCwn1U7uRaiMgbdQpVqNQIgYZedYWnEMxaBJANWrPnRNO7ROTxpBIG2x9pV+nq7WD8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620102234_1529490407709_0.08083940609040008"},"_hasShrinkwrap":false},"0.2.4-20180620104508":{"name":"@atomist/sdm","version":"0.2.4-20180620104508","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4ce64623967f46796ba6dcab22febcafd281dcc5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620104508","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dYss9l26LRaUs1/G/+pOtt7wCtyfpBEA5SwVQCOJQtb+3B81E0PyNdYw1A2C+uyYRyV08BoxvUEwdIVuG/blJQ==","shasum":"e13de31572fb2f571e148c3384c6dd58a2590fa2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620104508.tgz","fileCount":912,"unpackedSize":1978256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKjE3CRA9TVsSAnZWagAAJIUQAJch+S7DzrFW5K5GRMVH\naydoYdBsKGpoalQ9ddmV2EIMNNNuwpbacrzRw9cqt1oJQ+VbtoPtynQXZRwO\ndSlMXdkbgten8noa6VY4dpCT8/uSNZuHSUoGs3FB3qlIXfzSNoKklefSu7XZ\nqgR1kGngtSk6JmYawXet8friBUZmoWFCxFCLZWvCqBBzuSTds/H4UEUwcVZo\n/u4ACgKHiQv1XlwYV6eZfFi73Z6Gnw1ob1ahL9xucmrj6Yamn4M6uFZNaZKy\nY5QuajshbSpRWZ2/ez3ARtHYxLMBI8TAB9EXcLT/IABIU/08CHwDUZ60eWgH\nkIuKuWJOLKong/IxcYEo2f3kOm58KM2+rYp5FBev2ljosQcXNplCVJxXvx79\niud+exxHWXYPR6qr/lilCqFXBu7dfEmP1ns03s6s3nJEZqcDJfSXGr0jIVuc\nEMeB/4HMSsbXW0v+EpmoIdDmV6GotkPzAO9Zt+NgnvhyFQknZ91Z5u9UQsrp\nEcD3xXwV6dahbgjNA50srTowtGkF+2vbZ7s9KZ6Uyct7RYMVtCEdhqZHyevp\n7jq6sMwpqvB6n4LwrWjMIMnWtFiRiQUmrB7Hn1F/lO0TyJSyDFrCr1VBpepU\ngLde5HT6LuPciy9KRBhb+Hb3maJQOUsDNttmCKeV6wMwD4VlYUJKV6gUPWm4\n2Oul\r\n=DNKj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHlMX9BxNnMsfRKddzJzKvJcm7Bld6u1OH01TMhYeWC7AiEAlpkeuuQxQp2czuZc78ykFxS5ziIM50yk5FF9uHbntjc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620104508_1529491767402_0.7910114434155116"},"_hasShrinkwrap":false},"0.2.4-20180620115206":{"name":"@atomist/sdm","version":"0.2.4-20180620115206","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ba9dae266ec06475cae0bd8127330ee7ac06c96c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620115206","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZZZ6rLGpVdIIbmrnbLWax08rKfyvSKBJU0g4js4lT6hAv4XwINDgbd12IHjzJonKcqfIwa9pBFNL60ldG9TRaA==","shasum":"c41400aa1b5a9bb352709071f555c21a5ef5a584","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620115206.tgz","fileCount":912,"unpackedSize":1978626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKkD4CRA9TVsSAnZWagAAzrkP/3LDApKZkDGOwmZ5Z9P5\nbotesKgcU7+yyHnaSCRFbCGZUNsHY0WQdBuK7h17EnVPZXfvJuuGPxVmRUMJ\nJ/yaI30w/zesKVLkJMtroe1/CW0YKKdREgRAPqc6SFI12RzX4WOiVGol8Tcv\nqHoCDjKZORLkyHuXRdyhWfepu1zbH89dc2TTAXvxt0OxYpnMSLkHdlk5pL16\nqgADMNcLBBDNoZZsamLZD14YkcHD2d8vP0kH4o2foyp5a7jRJ4HVdeaJacSW\nUyiwtlwDQf+qJIsgq6r56NH019nIVQw5CHe9tuT8aPRsbnoz1b+cldF/sOV+\nOdAU7TJUIjrKTlhJ3LMDkfU+NTTd0HDIcMh7nW78ngmYu9USb9KGfgaFRPs0\n8McdOSZEBOOWN8vEU7Wob0DyMAUdz64iuTRQht42+Cmq9JPSCU+xK7RNWuBk\nh+qZmui4+0/KXtUWe6XIOPW4gSokkwpFEN3z5VHpdcXrGeY90fsHrYGPPuub\ngboHpiTRSGiO9EXkS2ZKABwlfKQbvvr2FSW4d6oBb90PpHf9I0pNaVEuVLKB\nGtEnUeV9QTBjJgGh/0BME/fwVJhHShMHbiyu9yp2DRnNsveBuBo9Ki1InLAy\nKN+AOaKwbBEjy5R6F0IVTRxY5d1436+GAhxtMmDnJReAEhd4w+iYNBw2loIE\n0AhI\r\n=/YHt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC84/QDey6wlazdkQk/r1MBPAVzWtITk3tZqweVdr/dwwIgbCX8FUmyhlOclb++ApyBN5dHecpkbrKfSiwmf9AzPxA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620115206_1529495800206_0.5859620116216777"},"_hasShrinkwrap":false},"0.2.4-20180620132420":{"name":"@atomist/sdm","version":"0.2.4-20180620132420","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d79a20a42fced7e47498949ec6de9d4ac968d337","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620132420","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-b/4loOtELAmUoaCU6ivJP5wO31RXXHxgBZYZ78Dta/6/LUkdVFTBpe0ndHfIZpKXC3GA3Bj/ZcqIVSWOLpgrxA==","shasum":"8d4059e92574900499633563b05961b0e705edff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620132420.tgz","fileCount":912,"unpackedSize":1978953,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKlbqCRA9TVsSAnZWagAACn4P/jXz4cuPTLmeGx1SFTQS\npTv7Ai7YelvqCbsatyCPijQhoVk0209cqZLrKogibpQjJ7D3iGeATmP+r48l\nLXFFkkt7KhWWjSfgM1GIe2KPgrPzL7HwKh6FCh383JZz5U054n6I/xoSyGxg\nGpH2BaI32gK6re8sVSOdAxeyC+2I/xEVSkQ8pr4Jt4srFh2ZrSfpXiSJpjLd\n8+RDltPrW1idrbU+w0te4x1SUa9aoGuhz1yO/KLJMrG05X82Ie7g4nU7Ns3O\nyrcs22ppBnGe+qs2sAnUhO6rXrjsB3RRQuKSrBBZMVq4QLrxI8sWSi3U+z0o\nwC7FvAPXrHxucMvehqvT209uIWKJw/hC8i2s3Xqf++CBE+6s8N6LQCi2E4BI\n/UbKtQpqMOYTE1wf5OyMhBoyTJVaXsM0S80qw37vtIXNeT/MwnAYv8vGYeb4\nRLJ+u0oyzTLyx1+nMXVUSvBR549bTBWgjhlV7O119/ohqsAo/ur5ly9w3Ydo\n7yiezX24wv9yBP+hLlc56AiDaZ+RZJy5gyzSkyDsWx69Z02LcrSpIRIAxfUQ\nppaafUCLJvK9lBkWgbWGa9ptDTVHRQSmrxNloH07dzaJx8p+AlwKVCgv0qnU\n6BpQTqqUpQTNfkLE+vC5P4nQ/YtNJjg2EK9aGtA44WFxpy7VmB3SLnk696ur\nE0I6\r\n=dHLG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8xDexUQj6SE+psSrDoySt/wieyHkJaOiptdwEwH1EhwIhAKBQhzDFlxziJaclDdnATxU33mn4JbUhpiQj9HyuBL5T"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620132420_1529501418204_0.7796843210922326"},"_hasShrinkwrap":false},"0.2.4-20180620134904":{"name":"@atomist/sdm","version":"0.2.4-20180620134904","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5dfcbcec644d877e4d98c468ca1b6d017b4a90aa","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620134904","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2Kl63eI813P9llEyZcsH0qwjmOiwGGTHI3rR0fekqsQuzfv8NhXY5lSi0xnQ1c+lHLiCrd5wQ+bnZ8YVzEis4g==","shasum":"e77224d7edd79d6932e3a888eae81871f7997dc8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620134904.tgz","fileCount":912,"unpackedSize":1978818,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKlxDCRA9TVsSAnZWagAAH5gP/RxDHjrRoiH07uSKCHU6\nVkJZbgLwAUJW1dubCC8K895DLYuVOx4RJGfbZkNLNmKFUdc2TE3Qf02mW/ps\nfM+YUvG4f5a0NEH50H3kACfdq12Qg7kyU48irm6GUjItx2lqKrBNrBvswykO\nFOlCzEaqnkbdKH6DwcOoGc1LU7+vrsScyCvAcr3eAQOS4LhvnnTzoMQN/0PY\nXcdZZDUyzZaVwN3DQqCP3SPUpU/o063U+e6wMrXjO0h+7gqAaXtNnLYLzQeV\n1h28/5f8feZu17mWIKfCKlnU2nhtIfmb40DPrWobglsqMEthb8u9lVcEmUq2\nYyUTwa3XCdUjw6iJaIvSl7djm1q1rOG6qnE0a56MrdFFR+cDmYpSRL9VQRQk\nWEpXzWAnY+bEhw6E8i8aEEHMuIKjSL8Z1vWLVuHQhfqfw5UeSzz0pFEywGo6\ngZsR1+z2UIAcGNfwqmkDjfhi8cfMT2FlpZ1dFSolqDfBdOXVolny6JmQDOTq\nu6qrlke4A3cjmGRCIiFsjEMOL/VZotLmJoTejTv925WhNXnQSkEuk+IwCe7q\nLTNrN9OUcH0T9oTmfx1gQg8AcZ+Z6n0cFPjkhXM/f6gZzkV2SKwtmZzwYF8c\nYP2Q8AaXYLiDFrOqQOXMHXoWbf/21SFffdTy7QC3JOR3CKWH9BOVqAv8Cmhj\n5yYM\r\n=Fn8r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGUZkhKD32x+ZxlZuaJg4bQIRxf5h6gKGa4T1dDc9nS8AiByqNbMDy1gt6MoOCu3x2RgkpwQG6ZT+oPfG9+qNuscTg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620134904_1529502786908_0.982235238203716"},"_hasShrinkwrap":false},"0.2.4-20180620134805":{"name":"@atomist/sdm","version":"0.2.4-20180620134805","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f9d252d9b5b280c384e1247d6875bca6204ee3a4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180620134805","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-I3DOD94suol4q3O7Zx624fdEjpasXLK81f2954FojBqiVD1a8mh0+q6tYZ0uVLnPy7Zophe6z61nkVfMsHh1Dw==","shasum":"05ea805af30d1aa25ebdd9ec5d2a17ff93a2e7de","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180620134805.tgz","fileCount":912,"unpackedSize":1978793,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKlxXCRA9TVsSAnZWagAAwA4P/AgpJB8p6bw18OLe50A/\ngvlFdxpDGShbsV5bST2L/n9UNxxuuRMMtAZUfyReiTvC1yBFmQbrrzuRt0pr\nF4rb8N8Eh680HqC6En/R17tsejgeTDOS5YqtGmGdWC1DSZnhmqSDvzsFeV4F\ncBhoFv6DMabN5EeSC3BLUErPiN8VRTuC8I1yHByq3vpftLbvmH5Shum2xGWe\nd8oQV0NjJrcOXk8CRspY9h3JWn0z7h4iBL3RF4P5uE02LLiMCPA3fAG2S4Xj\nhWy21oZK57kyrc9YQhVeoIy6qCSRIZKotEWJsY+D7eVohYR2NZJKZz5oCsBi\nGH4KM4uQZLDXSJktldesY1m/RnlF9nILZF680u53MiIXR19yuaLTY2rXNAai\nD4V302AeQcNyMYzdMH6IhPl6MNI02fuw/EFTccoUm4Ci4qaPr8ioUg5ONmmi\ndljIGppyH40/+IO0h4IkIXIFev3womRtxNTgqLJC3S6EdSIaCmhxYq7CZhQA\ngYvhj838+w7sCmyTbFLGYRK7oLSYjR2tByQYNtls15UUnLI5Ane0Pu0HWkHF\nyp60ysgJoVtgHv76lbEwGbyzAxajSvezsETqt4if8zJovQsdjhYpReA2nwiO\nNJtK7haY4GUovhDnL/imtdnOz3M9LzTkeVVn5pimDu4+HRPieqeV3yo1iQC0\nDwjD\r\n=JQKs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHKvnEu6SUL0EHFGiu4VFsEm7CJy28OQrGDoSVHExoYiAiBny21Q6I+9d1pmEwr4M1vz1B49Rz4O2y2V9Gf8WDdYnQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180620134805_1529502807556_0.4939146927864446"},"_hasShrinkwrap":false},"0.2.4-20180621002745":{"name":"@atomist/sdm","version":"0.2.4-20180621002745","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2290cfdaecf9880112f562c0e2ff3ad5cadf5978","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180621002745","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yz4ccXwppuqH3V6Rm1agAIa4v1FgEF31g2UYMIB+9d5DhG6tKIOZz03bawRYIQ2Bal84/UYU9AWEJBJT2Kbl2g==","shasum":"0daee1ce4d52f447ae225255ceaf326e2300ec08","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180621002745.tgz","fileCount":912,"unpackedSize":1978860,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbKvIBCRA9TVsSAnZWagAAPvEQAJ3jg54/vT2MsNppkBDh\nsr2is8+EcdvoQ3OmHbxaEQnEyi2WKBh9hnPpuEsQhUpyuGzAqCflKrCNw9L5\nmBy/lhypXQLKaZiPuPcxdQXvZ8GbNpyjn8ur4I2LOdaVqi+oxyxnaK6uVWzY\n9dE6sfo5gqi3m79f3y3MUgP5+CURzJauMAYX4jOXjIe9BGHIe8iKpLECebvB\nl+xGPx0y3HOfTWVVQrgJ0oRmevgrxeiEBAfYua0vSQ12gPPyqOfncWj1QzpI\nnIR2NQyGLnqYxcYo0dpjovURDZdtEA7r7S2bFrDIFeBfm5GJRBp8pTo/8ZXS\nr9gWvX7BDrl20cKioJqC8/fOaGVIIb3UH8KYJZ9CnPjzFkkI9fwCdCSlPdOf\nHz/Uer6IowoRRjLLcnE8YRxlR6fbtlGzNFQaepxn7evvP8/Lkhhh5I4UiYn3\nNSMybJ63/OPEaexUUCtAwZ69RxTEmEFcYehuQS/SurPVXOp8yrpFB+Z0Ax2a\n+OEbE9GFG6izj7YfnToNqq2exw8yVQKcA2Ub4QR9Wv/g0Qno1DZPjzr3p1NH\nB4FpQZptAKu6ZN6aUNMorT/Ei85lXPMUHpKPuH4nZgQV/JwGWgRGEGpHwY6e\n1kASXld7dxkXeMiLhL6tP55ALlwkZG2pHGTPXo9bbV33gP3Nq+C/5CXS+eR9\nrnfg\r\n=lwje\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjRo1ds6U8G9ZoQPa43+enppQl0n7ynT9TX8S7GeSGFQIgbuqoH41+bWLIsfDaFwx3epmoyygR1dprrHY20GbCcmM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180621002745_1529541121144_0.2916006824624864"},"_hasShrinkwrap":false},"0.2.4-20180621120207":{"name":"@atomist/sdm","version":"0.2.4-20180621120207","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5dcc76715d6ed5c343909880c285e9131a33429e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180621120207","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-69MNCu6izvgLECjZg0PBU0VlePWiOR50YWDRg9ZOGl5uvI4XXYOP87m4t1cfqJFypGedbgtosH+6miOg3PxKoQ==","shasum":"19d965e39bdaf415cc123e8ec9f17a4c3e61904b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180621120207.tgz","fileCount":912,"unpackedSize":1978970,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK5TDCRA9TVsSAnZWagAAejMP/igOXtQGzlQCdlp838Wl\nj9AysluBTp92sc8TkarlHQ80Chhfmb8m28Ei5UeHKDAP2UKhFAqgbTFYYJJr\nCYozrJE8MLf7WalQB2/r/yjoS1dNqVNTvdGP0zknaBg5FUGLc3tHqssu2LdR\n1SCopzHYsK4LAcHuedDpedjc7Sivi70A2UugMLeApr1FYXcgH6CpgP5WyDFR\nsMIasTJGf0KCYpH3YYWRmQdoCJaZPtHSW+NKishdus07erny995QKPWswKGz\nFsnNKB7eYlj/RCBZ+wSymLW4sl59Zm9IpAhSMb35RkTSc3t0+KzTl2NKztt9\nIx++mFyhzhyKNcJ0vycqeSTlAvg817LvkaBTHp0nsMYs8o6+IOq+rByOVO9H\norwfN075Be5Y4+Uu/5LZRxPtER2tObOSs2ESC2MSLB/oLsB9RhhyPds7lGM6\nflzAT4N8JLJnJjjXEXr33yvZssvWlzUAIoKax/8K+S275rlNXjZvMIohH/tt\n3qyQtc5nJV8ee4ncqf42lOYY4eO/+Qjd/mk/W0Ag5W9oofkRR7wra7vCJxJ6\nnWDrsZ9oF/YwttF5F1PTDPUo55AFzIGTW43zMVc7wOsDM7+8zAUuCz8uQYH+\nDJ63wVrmE3y7pmi5K6qt1fofRiZGem8OrB0RZtnZOpkphityOO4mZZpEgAjn\niFz3\r\n=6AqF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEKklQ3krfjGLyKjiUZzWolbxkl2HodsbSvlCq6Mh4ZpAiEAu8U1jFSUj2rYX1LJXZ0YVccFdEXDUNzHEVugqc+LgCM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180621120207_1529582787567_0.25951355412955324"},"_hasShrinkwrap":false},"0.2.4-20180621141531":{"name":"@atomist/sdm","version":"0.2.4-20180621141531","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f9a185a48314a8b6baae37c85461fdd27d0b8af1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180621141531","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5QXdz2PW4QfXCs3x1mEnnf0igMpshf28dG3rEyYrGKBiX6og8n45aFVBqUEf0HlVPbSc7CA0aVBD6MzUIDNmvw==","shasum":"ed0035124fcfbb61fdb4bb6657675a5182963af9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180621141531.tgz","fileCount":912,"unpackedSize":1979310,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7Q3CRA9TVsSAnZWagAAH8cQAI9na8NMuxR5Dr5mM6Vl\nncxoLkgq5AUzKa+qSbE+FPpe1rVU+ZewF/52sYRXG5CilSIlKzgxPLiNZ9/4\nV+hyf5y8AcRKVJmIHodOtgyyBjXHWamj8unnCIf/3GynQAc/hgGdpuKjqKBW\njEcLGsflvDy7nMD23nQUUouZMCuklFlGPNK+3Nvs7lhaLl3oHhXBKor8Qga+\nHpy0YlT8+ZdLcuuJqCE/wwbmytcxLz5v71EB5E+l68efASJDITqU7ta3Bz+Y\nKen4jQAm2YadooP/LlxOUqOpUiXvr9AAlsUDuOZ865FilsFbNe5eVQoQAr2a\nQ7ILcgjg0gZMO27vkTF+Iqn22O9bSlhN9wI9N0R/kOqJfzve7dSaSJvFhM8W\nDVPEps/OId63RhOqo1eUyhiD6jYK/HTgbrUoZs1gy+1vVIViSOHyRtLkT0Y/\ntyW+1RnUamaljpud18BO/KZYg9midWHsMhV/aHbeVuanj1rPSYc0Bu9uM9zJ\nT3Wh5vfof4OGazXVbkLdbxLTJo531zEhOprmgLdXKYSOSCiwvEhb3HsfqtWx\nE3JRY/EOaKhIJjoaCch9/FPbSsffiJ/fuuzIQkEg4Sz2OpgO2zINLF4oyCq5\nQynvZqISwb5PjWyNOfj3Xdw1nFYNOQOP4gsab8ye/wzdnlzTBX9X0nz5MyR0\nICqK\r\n=ZnUp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC6QM0hYf3buvdh9o7yylOsWjbnEUI9QMw06Otcl76LHAiAzItgTzmaztAnAd+0FEvF3wmCpuupqMGnGFxw6J+98Hw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180621141531_1529590839079_0.19538330118502478"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621141931":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621141931","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"72ac141680ffe2ccf22f3a72fc60b956e1f13f38","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621141931","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zWvQZQk14zIutEqxMUnsxv2MwBKUqA5sNhGGqDCecVh4Kd9/glgCNL4fpaC6TZiB4PcHnZnVosrG8cMEDpUU2g==","shasum":"1cfb55ab0d5bcef756bc33859c7364c670dbcf95","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621141931.tgz","fileCount":906,"unpackedSize":1969545,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7U3CRA9TVsSAnZWagAA81wP/iyBoKCfaVzTFP6zjgLV\ntDqlbfzBmC/8eBdvfytkjSzwih6oMsQ7WdI9zTT3MHVRU6ZiNbx9w9mQ+5az\nSN2LqIBW6uGJINppA31cZu4AjnvbYiPE8nWyT1nMWORKd+4di+8lzMW/eHLx\nodfdt9yRqGP0/Qh7xWtJfR7l01//LTDWVdch0sMDYI93C22NaUJjL7XqUOeP\n1LCWGPTgZ87fhHjUAnRGR90mC5/rF3d/VjqKIsk2YY5k8FNFXwAi3M7eWxcB\nzewEPCCDduh5XKFslCSIVotjA1tZPd4A8FahdziFcIxk1nYXl3ZDRQUOGdl2\nVDtNGMOpYnMc7PTx9TgnNu/5szgy7jAAt1pzUurbganmoyu8MRuMYg9GfyrZ\nQgcCUIaKObLpjV1TY2dzL26lEy008wpB8incRnHskQMAwBwxTZzcg2DGOk95\n9kKVE4DKpe3Db7BtXL1Xc8ZPegesCP88/xUQntnGSYcTUf30WyGZwcR1b8kD\ntJoebiA6qVaNj4m6SVYa1aQRDiTQVDebRbLW5PUHrfU2MQXFaJR+ML6tGIF7\n2JsGuxRFkpJ83rnmoGLnwAtJcpA/2GyD4OIs/mZTIQNvySbeKBcH6mWThWFs\nVPRuZMqvGfvQyckT2AmkBpjT7QVQKRDf6M2HGXWQbXeOajFfTYx0FaJAp3Tx\noBqL\r\n=iSgx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICDdAkakiD+8uR8DlFlOxSaQ80m5zutD4JAjlfXEh8rCAiEA01KDnPSV6H5uPRuV4Jgc2Y7tl1pX4li4RJGlJHy9YkQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621141931_1529591095786_0.7481342091722154"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621143116":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621143116","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b46554b073a1d35df4cfc18e04e026095a8959b5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621143116","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z0OMLzm44QSs+torKXiwbt4/9HYMb4BXeChL2995avylLZP2RDPPSOz47m5HYdDOKOU93VXNHr/apoVtR0Wb7Q==","shasum":"f055597cb00f13e3919664d7e90441b5b91a9227","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621143116.tgz","fileCount":909,"unpackedSize":1970082,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7e8CRA9TVsSAnZWagAAgY8P/i90Hc8eE3f/2fzKHcwH\n6BrQomAI+AOXC8/d/FjewgomMXyQYlmYNvLlSj8/5opkth2Ch1tDnUykJtKp\naQuaL7RlbV8RCVe9n33TGSKPIpdHMn9co4abn8FJqGaWNR9LnrvV3qs/AJp4\nRugC3eYWmAxB3zWkISlYBskG3lBpL5JBiPsgm5P45VBGvGlYdKAeQYGbKGpW\nY9MMQ+L+GEDpMfl4+2UwTqKg63HAxx5zuYyh5p+UEXuYBoPhIgUKrKgVbUHu\nTNPOouRgOUFaf/J970izgtPlT+OVGDAdZkocc5h37PIoSsShbSskjEX1zIgO\nmva7cWeX1ebRkZS9bDRDwONGr/RalBXyK+pSTPX7sXWR/xpMVVlBlKJYHMSi\nzYDAdzaEPWwQ0Bn8iCeq5XzI0nsqKHIiOVOR93/BBfo1AZDZBo9PAtX4EnZC\ntddz1r86opY0jRvMmK0z60t5n77DLpqSnDb5DkwQ7VBWvI3DcBzdsuxld34C\nfPnnUp0o0SsVZeX5GfSFNd0lxL/BzyDu5cwjnKH/1AjcfRJwKREi8fFYWUNv\n4dZhmJ+KVdNGZRwpNR0IjZU4wmbQi3oC7YkapIo391mgaj3InkOMVzVylqt8\nSuzuFj/d91HMe4TYSoLuQaCfSR5qmxHiasNLCQ12J+b1K8Zjfyy+h/Cs+4Qu\n5KEB\r\n=/Bx/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBktKg5nFfJ3w4rT6kZZ5F+2a2uwtKI+ega9kJtbaiH0AiEA0Y/prNjKzuL5+BR0l8iIUq6mP477bJZAQtQ5iHuF0zA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621143116_1529591740094_0.5227742189837581"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621143608":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621143608","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1f3e01b3844c1d34c9956bd758295f364306096c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621143608","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-85dQyC7xIHkA5fGNT8w9N/Mr2RmT+0+Tse+iO6RVN56RZSrtxnB376IletDqWujbMSE+2eGBdLiWx6s7C1ey/w==","shasum":"cde07792261f1f3a126fa7e99c46d4913777ec93","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621143608.tgz","fileCount":912,"unpackedSize":1971103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7kLCRA9TVsSAnZWagAA1e4P+gI/wx6bAcsvGgWl4kPF\nk/FrmOBgYtLZ6FBPyPm/w51es++pq9Yyqyv3o5DRKiLq+3w9jp4v4IRP/eJr\no6E2p7IgN+2AEHmC3yj9VrN+LWktvCGGxRtM7HExxhacdCzu7vtJqwi7b/xL\nyPFbgwbxo7PyJCTGjkH1XZaAHzYvkZqQzdgQAlfv3nKX8XNi6jlLVOCO3cwf\nOC5jaBRO4HxRyys2zQt0ftqAmPodGZfYa5G8DkAWoVoe8P/0YKFLYc/bBD1V\nQbKW+igoQM91+zXxPjMWYk9OHRvqDFI7DGH8VMhSMGpczsYrgzoZtADW0TIP\nltNccEEKhzSR27v+ZUQqi6gwyYB3YVqHw0DWviy0r7rvrucYJ87Fw4uZVfE4\nLpd1pPUjRw+6QOXElYcfCENrHijxCLvNGBtrrL5/M7Y06wXxjDn9zhjzeJhq\nGF1qGHQCl1sVAOaGrVeY6F1S//Q+V1Sde55SpWSOysmqqRv8bOGQ8aDPqDzH\n0rJ8S9OG3rd/EcTZoRdgBllrSpsFPrmmrkUTExjvEheEx7TA1xSQq8MdnQuR\nFLGfg9oOEkIGVV2TrwMWhxf8/x9xweFI2JJi6EOQBNZdvZck1DkbzrGajxuf\nF3wp6HMBZdv2vpulC7qVufXbmP81WbCfZmyIoc9doCUCYdzUqsjs8SGKNA1g\nzLi6\r\n=HnXO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjA5a+99ojqlPsh2fwsZYeSpfe1KonXSg/LBYILcULJAIgctgIHxOOl3I59L4Bv1pHhP9EXWPcRh8o/fw3ZtfZ7cg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621143608_1529592075425_0.9772765213333303"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621144050":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621144050","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3676a744123c97e91d1d6d911871d650e66d2675","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621144050","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LSZ2Cn6u5yptisBVxnOw6EzEJV+LrG6l89w+UYd6R2Bp/B4E6xqtNMeqMy7eLEarBtYoQ47Pt2YBW/VyghH2YQ==","shasum":"c93b0df947b52a4458e9cad6a567e48f919a530c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621144050.tgz","fileCount":912,"unpackedSize":1970857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7p1CRA9TVsSAnZWagAAKKAP+QCQBjPRvpA2bRPPL96r\np7twYh+BqSL359JjzjE7cpWmleBNAiXwcax1i04Fz5GMJs6wJYA1gn1twhZQ\n+pdcE3iJQVBnffemhFofcbWg5tMo3eSqqoGzRXNBYamRbFetTw3atTC+0aYA\nGfZu7sQeX6++GbQeSZGMDQjZrEJ9REhx/KmFjbrY7wByxMU0cli7Zg+x7y2s\nvSTDaKlwBmDDTcD+t2Axy/Wly02SUhn9mb7SVOTg+xP2YRveh/oTG3+iir0x\n4RmLmZ4VyGAE2TDF+vBT4zh55JKXp7+TfPXjvDVqmzR5Bv7EIoPcAYRb1iep\neyUEkW76UeKKwxLImWmIDFmDL3dyZoW9EFeYICkgxWznk6lSPju/BDMBZb4T\nyPNVNr+fBpxpuV7aDpks+UwTdBScTrN5HQeTA7bvVKI/b3L3ISJMoGzOn8uD\nWd2HspmSRezG2c3+XQQdSC9dXwfMnZaAP9cUlOaybXDIp/Xda4nFUBC+58bj\nbbYldrDPJlh98gnRc2c1b967fJT+C+MD2S5rKXrsvmmgMeikgHSZZQu83B8B\n7h02G7UbwLpDFKeaWY5qHIRB+htZg28Q71zFa0ODL9mdmfl8p9zTzh+w0/xY\nAWcssgfsFYjLEFHmv7f1CKMUOS8HqgZ+uu62uYZ45tvoZZZDBl75Rt66XDI+\n6gkx\r\n=7g1U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDasg20ecbBOsD6MCVYydQY+rVsHMMObZ0TkzLZsAJn1AIhAJFvdNMv89olnRYfS1VHmWtRiy2cgXMW6lbu5lugwT22"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621144050_1529592437603_0.3138775043032076"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621144851":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621144851","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a290d094427b2b898d9adc0bf9755cfe41cd14d9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621144851","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LFpHnSgTW92oqzxPb6EbccP327QRIDZWrHPgQFWd6iWflqWxKe9jj8iQrjfxFw3IS/H9eq5Dt3YJYgJNo40fUg==","shasum":"29da5d4f618a720698d0acf640f7a143edc4bfc9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621144851.tgz","fileCount":912,"unpackedSize":1970745,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK7wTCRA9TVsSAnZWagAAP8gP/35Mo9bQIy/Hnxy8cFEE\nuYrui5kiOCD0l9UJRz4BnKil4k8K5SILSDBJKqEmgMqjRjf1CJxfYvXokYCc\nZ7yh7cTlpbxxvi0YY8YLqHuIOu9u8yJ4BcLxFAzPnGHR3OGU/tKqoY63STU5\n4aSfnD8Sfo8pMp+YrNkcFzTHgLmbRNxtxLh4NUzr/EyKk/AmoDEM1cw0mPNv\n7fYet937u2bfYaw3BwVyOW1WcMXwFK8F0F7DYe/TrhxbSTi/3mlySeS2U4Pi\nm/Fdp6Q73MUuqwSfslkPgVq+xiXzrn3C+vkk2u/CuXKe5HmJFajRH9tVtP45\n+u68qo4Q1qDjPBPcl8x4c9NiKHKqB9SxHdi3GXqH7q4daroMjmh5WH0ltIVU\nXV3TRRjWRvx5re2NgWpvG/Q4CC1w9Ge4iSCHyLnJtk/O1mU7X4jNbPH9zVMz\nghJpjskKmeusqvgC7fBknMzLH2gnxqv9PWnYjyOO7Sy5QXaCuizWUbei6ufK\nYedvodGz4RSgdCvGM/H7cXvi/AZ5xgVW3We6ojtIawtyr8mRDQQYzrk55z2r\n6uL8b4Sbd7fnXPnvkTO2kyIuWBLNTiOt4NnfAL3S5+1cRjlxeElLVjdCgH7U\nn+EldV/Wo8KVRyVF80WINejJfHIhJjiK8Ul12R5THBCBupcJ1l0fUCfP094+\nn4IO\r\n=rT56\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDfN4WMvV0s9MxutKcSGDvBxoPO6rTSjKqkalYa7NB/6AiAs15L2Fa04sZ/xbiLMe27gN47QuoH0iFpD/mwV5SX0bQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621144851_1529592851650_0.6848687026458713"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621145721":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621145721","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"522ccdc6656042f9f13dfe040770d4385dcaf1ca","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621145721","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9zncWR4RFPAyi6cd3OsZN6lTwx3uqjDKAAMldVGFRveSlUuRM/8eghJzM+g1QIt6vO4ie5k3iXd0jj81/Rvzig==","shasum":"118c612c3aa396fbd9a36f054850dce143603d3c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621145721.tgz","fileCount":912,"unpackedSize":1970526,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK737CRA9TVsSAnZWagAAtf4P/iy90tDvea0NmeUWQVbe\nIKQWpSm1L4fMa76lWH7U711phAOSb0dQr3KB/88Ookr7z4OYj+AaFhZo736C\n/18Fbkvid8akYuNLCOSmJFrEI1krnwOyjCIB8ycs8M4CGfKLKy0Z7HrnniOw\n0VxIPtRPFlfdLvxG0aB2OBsSbQBgf1jJk2D9jwdn6dvxDnut9ZHQkKnvdhD+\nwmpWgrD3JEhRJt1mWsJS91Q+KnOzPsqBmJXUTVj0x3AQXRYRkAWt1FMudM81\nC7iwCTDck4wq2HIMd65zJ0GyzlpXeoQts3sJ3Zp7cjnOu0a1lERtokH+D8+4\nUv1Ar3kIUDwxfRNjMuSBkstPUMAFuI2UFbhGMr9Yteg4p9v5KCeEAzgUoy7s\nT75rxoP5jALSuK06nTIuZQTQU9MGSGaYGKh/gHBTGknQtRLL7cALEp+t7G1r\njqKvZu/Nst7Iq7sL0NHfIorIJrpZZpg/lCTvWIVNS9LVdWI8EcqN5bhagCGK\nSAALO+iJNRWwJEr7O1YzWVgPzvqZalWkOcfurnuZYyzZ/f2PqK+UaQa0Tlpl\nCEp+HT78ZoC1IalC9d6g4gfwpt5Nhm6FFaM6CLqjUbJ/WIa7Ci2r57qlfxMH\nlVikccAMshbtvAeIVJ92uG6tgibfo03ScLhn6ilQ1GcNBwbBCfYH1WvemfG6\nFRfz\r\n=1uz8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+80tpItkhxdO/tDJGKPGpkCEpM9Bjh64Ghky1PJtuuAIgPF5S8nF0xmHmWQAiwvYllYwbYQ89C2rFnEqknJR5U5I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621145721_1529593339111_0.47257117816315874"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621150023":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621150023","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b9aeae24dd9bd5aaf12ace697ae34e98b0a2dfcc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621150023","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hfD4o8zTBlgcltoEZ5WdQ2UeWt8mDL21Dnm3EvvVdqRApmkP5G/65cYHVQLd085Q2GstfjvPRIYAPkiDcQO8yw==","shasum":"c557318e27a60dd6e102069b545bd3a39af7a277","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621150023.tgz","fileCount":909,"unpackedSize":1968346,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK77RCRA9TVsSAnZWagAA0ZAP/iAU7p3rmhpcEKYgQiyo\nBkT+LV1xj/RdkPXVYrv0y2VbqYE1qBVOOVaXrb/7aHUMs+nAq5eT6QPgPn7H\nlf3PBFAs+bssqbJbqMnbDoort4kMObaEvk5P6lA3JwPIMhTMoT67O89+++pd\nfYCaVBPxhkmli5iFgYrRf16oBgDeZcfmJOmVphC4qliIpSR539zga52oLzrp\nas93/4g28et/FicsP0T17eDs2Z9EEta9qAyiPMbvVcc3AUgc/rPpCbf5VPuv\n0Ihq6gntM8XAI110WcHrHggpziE9A2EIfERJDFRMgSTp/arfEVJvZUYTbvju\n0+JFRfleVCOWKLYJPvjmhYxzYpezOWEL4pfEMxo3mJ7SAFks16UjAdSH+cOT\nDSOuZiErj+lyscTyeWnHns3RTJC2Qb7NC4FAXQzhkACHiapYB+zZE/WGWFdV\nOfpd3iKtfnJab9aGMhtIjVyXbM/7m5N5Xyy3JUTnbIiEwkBF9AyExudGdvob\nc/TxWqz+Zg8FmX+R5OxFIH+mmF5KC8QvjaH6gECOySC536dFjLaYbRSM+CNb\n0t3D+eOWoHfY53ck7jk1Te8UvrwtEye3To038xLh90Zz+5XlazrmRlf2r8PP\n073VGVI/EotZYhAI6O46yVV7czdmPzlwvaWMpyPNbo6mlJGYM//GTbh7N4Ap\n69Zx\r\n=TMMB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHeNjZQ1XurUw79calXXkAcjURTV2RIQqPbW/ioufrRPAiB6+Hyp8kBFtc2pE/jeQgUUgKRSE1WooxXKir0OGwc4mA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621150023_1529593553313_0.6112872575592363"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621151712":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621151712","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4dde9801d115315e1dbcbb3f0c19770e179b7f1e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621151712","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hPkr9WRmvI1v3x9BJ0+FfKfuFOmCJxkvuqNWc3ZHd28B6q09wSuuEDeInTC5OazyqC72JlxTRP8stGiEWG9NjA==","shasum":"951c3bb456a0924b86860fe343585b4a4cc3350a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621151712.tgz","fileCount":909,"unpackedSize":1969201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK8L+CRA9TVsSAnZWagAA8pMP/jeU+QX/niESWXzaoEzH\nqb1gkQ9JjlYYsfmo2FsYOlMf7CV6CAKoBTyxEJdR1WEtm5ctKzNLdrISgPXt\nPYQ3LQeHKcOviMsxE9JpjmqJ5pcHiCQk6Vq0Q2drj/82YZo/ce1nMqa2LHxE\npe7nkPrBGd3W1CgBnbTQImJrQS4EtU1waoT87LuDROshu1fljd2a/fZejhj9\nrZ9sQ39Yhk2lqqlnqckupUvU75DTvgFw9CGezvL/e0IvSrAokjAYe4cIN0Lz\nZdO1HxPdTdz1WWFaMYYoEmmnSWkEvd4yBP9ois+v1XUsvWE1iEzR1JljZ/YO\nw9A8H8psbn6LRPhtjdVXYb3bQWpEAHPDbnJsyPiMY+ps3/2XmbN7MM7XigFY\nZkcI+kzaueLxu3LWOieMJ4rB7MVsb+dkQQrPWGMZH/hMMNICUG8D+MtH3y5d\nVTBSiHxIE7FrJZZ7L2gYrFsXLheGV3geNhRmUbqt5ILJDPmaM8IFv5eY7NmC\n0PH6bwd107b1gJgqzp6bkc5kVeK3+j7k/riHYFsFsJ44HEt+H7ADdqn5dqea\nuswMn7oku0REQNCQnX4NRE1ketYdbmdnGC+adAkNkcsI5agM1FH+SEnekfXT\n3TgX9L2W0Lx+QUaVtFwJLxG+jdKgHXu0a8q7mppuqLagv2DmNNsZIl1XEwrx\niGX3\r\n=Rm+g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGHK8sfl/wpEQ+UyusL/h5YQFceO8FaZi4usRRZ/BNxtAiBNyfF+wylPymUn7PwwVOyjqJqfXG3BiPuIyS7rj4BIjg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621151712_1529594622148_0.15882878063813766"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621173032":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621173032","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c9881ab3a5a4bc503fa764b9e8b8038aced2fcbc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621173032","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-abes/yZbqW6UorD7Exjd9rcvH1lu0sAd3mYKSNwWrihWjm1m5iU4fB9V6BAo17MWE/L/QvFoNpKOfiJsv95PpQ==","shasum":"ade5798312e49b2f34d8b634397112323ddab6ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621173032.tgz","fileCount":912,"unpackedSize":1970354,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK+G0CRA9TVsSAnZWagAAIg0P/RtiIFQccH1p2M4AxceH\nMhJ8rnEZMa7COVKP1rjyJzEh3b6FQl+17SrreHovDCfjVsW6tyO1t1uKVTVr\nXm9txvSofEqJljJLtkNq+Jhddr5GbSoM3JD51C0IZibiHxcnTdwX7gK0Yiuh\nq/RTAuDpEMGfLghBWRbPp9xQjv+cbjvodcMn/zLzeQ1tEmgKrvLmzQs+YZpf\nM2D5mJMHsrNaLu2Od3zv0m1/qzSzNk3s321WUGCFburxrQisx+nFZ0iLBPcM\nPCWmt+VjNtVdpEKZYjJR4HjtDGPrXW46uHdEEhehSLlvz88ma5FRswJmr/w3\nrIr+TQbk6Lu6fk4vSYyd6b0Vjk+SH1t0YcFmwdQjW0piSDVYiq0DqtcBDxl4\nhoHxdUK5RAhWTq4+AxC/inKFLaF1Kj8Hv264QVc/hRpa9Zi0ZyFdaL0AckrQ\n8j+6oLlYd6E7pSL76L+bGqzidfFGtzSLlRF77HDXG6VZdCkctpOt4YrHQs4p\nWhrlmgthiSkU0booqj6aPM7i/2BkTe4Tg0wuhvFZgmpqaMsd8a/CnHuExO1f\n8fKb23UFcE6lO+75+DS16xEnLToNj0KbV3LiWWO3KXu20ruP47ARlSHv5p8H\noyQBEx6Cgv/JBT9wEXNCpCekR9iPxOINKw4w1ZUMa4I12aIzMrG+51fPn4/3\nbnYh\r\n=dUFu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEDEzxxusg1GbwSw+rHgw9swHuARfxuf/2xIRKj1lpVjAiEAjPyc+2kwrqxlalyqR/CQyJrldbCLLDCa+aW7X8oZcO0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621173032_1529602484724_0.415350509692177"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621180625":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621180625","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9ec01a307b9ef0fea72181fe30bda1eae25379ac","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621180625","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NxTU4Kbi6au5MvOkSExqxbAVF6g63vWvVuaA4WkWWeuzBn4GOP636WI8Pjp8bBrgQkkhYmtQAUk80fYzedP2Uw==","shasum":"3bf6b999da7120d6d8bff6bb54ec66d979137f80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621180625.tgz","fileCount":912,"unpackedSize":1970372,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK+ocCRA9TVsSAnZWagAAdtYP/1Ox2c09eOLpiOmn8qIY\nVMVB06BZ58BaSU1DdR3MwPdibPISyW4HZlT7s+10YEqnNuruwyQMEFKgvqkQ\n6Wh57DFZPiyKYiq+N4wMrS6epQxJZ8fZc37SadDzhiRnuDvlptd577Kt52WL\nRsaKUKdNHEkKBklAlq6vwyORHL3vbZAHkRjdiSntxD6G1hQqgnj1YKcdg95b\n25XnT3uUWbZw59q5ZfA+RHiaTQiRjtL5y5TYnOBImaS2SNcashSlL0uXc7WY\nBpfsoyq5koUOnL/QYKqL8S4tU6KNONR4TsjtU2Rjvatu+x3bJjA8HfakfuuK\nDZVQxsB1+pFDUDRLU9lGpERMJMUs9pimW7FohzCHCo5CGBVgwjck7/SyX/xK\nHNpDcyb+foXyzPrUwvsu2SqPMBs/yPTol58bxpy2ctfg6UVN5cIfQytFZA3i\nu3WH7wdk6aVgzU5Sx+bWDEWfDNZzTdy6NnO6IRq/jYgSMKZRDeCgpEzFfofy\nnBCtVHEiOmxDfN/5JfNA4AHT3cIFq5n+KZ9B3SnZ4I103o78If4jiC+3y8s3\niNk9N5F0xpJKb/0dAVopEetgIT4vSni+ofZx0qhcAqnkr88AnbkC7xLTUjaq\nuuVS1uyOG3GUHrHOC00QZXcgg1igF9u7g47BWXSgVO4dFv5ig7ZDtgcyfCdT\nEMN8\r\n=ltQK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQd+Jc6rPIcnxDctF73VyZb8loReo0FwDU+Vyirw5ZNAIhAMMYIN26D40/abVx8/9hSN4DeoCW+OqRyeKVZH/6Lw3S"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621180625_1529604635262_0.14065238515154377"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621181311":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621181311","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a54aaf81f6b78ec1516d2ef961324a026abb2515","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621181311","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TuvQBVrQr7zsmRYvfpMO2006XZoUMqbBPUGl2Tw9ZIwCCYpml4RYYZfKtSiRM73yWC0OEQ2FrUdsBvpsPNGrQg==","shasum":"4a8dda56ba9475e06fbc0850217303eb564e59a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621181311.tgz","fileCount":551,"unpackedSize":1202457,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbK+t1CRA9TVsSAnZWagAAojgQAISkjMBCmkISqFnH8yPd\nkRp7cpJ4c0vBL4kNVwf/OtDIj2k3Ri6m8sgVJdFXHL6VWwUScJnSuR4cJEw3\nA87cCKv6lYyYQ4nD1T2LoufqbDTWSh8kaBqOkc0wNlI6tpni1DsK2HTY95q3\nMIwKe5FMR/CYG9ahXdAe2m9pETSXMyoSsNjkV8VuumeGUysUmM9AW1JhjQ4E\nxzNuTDnqT5/WnJWUgDH2df+dPqpHwhrhS0Hc3Ur9OYlPaGqow+zkx2cUW+HQ\nSI37NeYaenmST28P4n6U+blDNuxKzoaOr28c2VvQ4gUexVm+z1pOXlJueWrm\nPo74NTkoGkWBsxbD6eczQ7EmzNu1qKykEZI55ABkmzb9zTzHry90/GKnGWNy\n56hF1Gd36UIOroZyoae7FjXCkFSRec6JgeCHhnWnkkjalBlxZ4CCnBRhyvrm\nWBPm2o2b2yUAuM/LupXFyLILjzqGXBcJM/MX5pBZNHfvXYfb2XfpZ7Qk3aV6\nXSBnAohoEIUz/XpIQig6+TGdklJr02PpB3IZ/juRLuukDM59lCmPyS58Pd5C\naraKTRZE0LaJtuMKg+nkuUoEzRdEC7SIgJUJ04VFg5ulmel09P9FT17VDj2E\ncj6G7Ej6vbXCx3bmRnayArYbtyG9e/zlB+MVP1MHnuiBfy3nOxPBWaReXyTu\nqsp4\r\n=lC1y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAbqYlBFN6yd2/TNuPv9nsR+DmKfC6Hm8fyEWuHk+hsYAiEA5bs8QcJFBVJlJ0UgSQuJ2d9spp2L4JNRzP7y/YuW/cM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621181311_1529604981800_0.20537345207660995"},"_hasShrinkwrap":false},"0.2.4-nortissej.break-out-api.20180621212047":{"name":"@atomist/sdm","version":"0.2.4-nortissej.break-out-api.20180621212047","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f4ca173c36a0ca1a0a225cf78e6d4eff046e7ce3","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-nortissej.break-out-api.20180621212047","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0TC3qReTUvbQwlG22Z/FIP8+sglbUTRsGhTvDP/M/iA0eMTfhLoBiOrbhwrzcEZtLnYo+ANok7Wf3asJHVsQZQ==","shasum":"a1e27f25effb9f95481d642cd6d8c5cfb6b1b6eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-nortissej.break-out-api.20180621212047.tgz","fileCount":554,"unpackedSize":1203975,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbLBeKCRA9TVsSAnZWagAA+d4P/2y7IxQw/VPiGjiPl5+c\nN0XrHEj/dqFCP7B6WC9gWD66alHGOkqcUsT1xh+7VXSVBmtIolmON1hjwUEt\ntEpEUx5/akJdJXT+lUo+//CvH/c+mbffvJM7QrXBmVyhCAizUkLZRSwoDwUM\nlXpvoFIuqfdQL7PwRiwYpkVHHcUO97V+Arj6k09UdWP2Ti4VaXrlE7jHqApn\nHn5ybekAqpiWYKbq3vOkNtmjNoCXQqX6FJunIbVj7aMsy4Bo+plvdedlsFj0\nw85K7swRZntjGJP8pawHzG7ritapFnHVTv/THg9/1C/hWNDi/kXHtdfSaxwy\nly7Q3I7/WywdkOAStPySVl0H9P7y52BXWqxzmVlbggGcR8lhCTKS8chgt0ta\nBOqLFKoTjm6tsQKdB26MKU/OsbET54FiawgIJJIm6Yd3LyHCEskvF4P8VbIf\nd6GsjGGFGZMmE6c+R/rqUyBkDFO/Np9p4F25aLu/J67cSODtpcrtTjlFjHfn\nvRW31dby/GxSLAUpQ2kuMNkf3QeFWH4xPpW/6si1b4pHKQ/N8art7xzt3oav\nKHyob6Jyk1y9t34bqAeszAqcpb+sK71w/XfhRpl7qT8rG2zkoT07azlKUzNK\n47u98nqC/AkTdcLdqQolMgksPQ3mS+VHoa8HDZCLAl6dEVal5Pdy/RLoHTIG\nCYmZ\r\n=lXBA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIClI6eSdYxk4YCkR/1bAPrrIhtFe6sEHnSnFXjJh4pa7AiBku5CoH35CjpylRCv23dnHlN1wV7dIACk2X0Gfg9Ym2w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-nortissej.break-out-api.20180621212047_1529616266770_0.656622495940445"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180622000216":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180622000216","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d46c7138bacf40c0b9a74c581df616cb41495f8a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180622000216","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9x2C4rSur8PISBWVrUwlZyAaanJzWnYtuPy/wGSgRtZe+4pTjH9/zsokbXr6ztX6E/F5AZJZaKqMa5HfJ5dYtA==","shasum":"95062273880751d06b3f0aa805c6c80309be6db5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180622000216.tgz","fileCount":557,"unpackedSize":1211893,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbLD0wCRA9TVsSAnZWagAAyFkP/2s1vkw7UT2bAyGNueEz\nNyH9RzMK6PSQSXXkto8URklDmmlMcrNhV2Aj6DVDZlAZLR3KuQ3Tb/mtkMSP\ngb+Zael8kEwBSCy0uX/sPiAaBeJJD6WKwrx0YMARYO1ILJDY2ikxlKmvp9Ov\nVEq6ewOL8Ic7WIwug+wAj8ccqqHtuwGhnLn3jRg0Dz3i+2mD0QJiW7/WDBGr\nXEyhIXfwIjQrz4VuZz9zvztzdkv3J30YdWgg8lNka7pGjvbp1OUaRiok9YIm\nKZCAkt1zlZ6pBCH1CeWSOmlBeIXhaUkMreWKNxEqqmIF5IUa3hucrVHfjPHu\nc/ltOLMFTiImVLQ7h6Xxl2mwJMVxAcfhzr/tgRNPvog27LQ8YxLq/Fhgam0z\n6SzmYcdQoMoln75uiDjuHLd1GKBryU7kjGBpJWzdrhlG+DUZhUWURyljgE7g\nziWnh6ZvxpPROekApIfcCmDhBX6GGsNPSvgv7SWGfY+4lJfE6rDfT1QyyOV0\nrWzvdectYprsxrDFYl+4frTgMNPOpmaKXcghBRMq8hHdTUnJTzE2vCQ8AEqY\nxS3dC9UdgDuhHuUdo8FGK5ReHogYY9M8TWbrvMejC3OPFQ2t5DEzoqU0qX+2\nbz+e9kttLlEYWaeD599fODjUSM7JYOT2LnbY09S2VkmfJlTG3oIxQEaGyISO\nlx0k\r\n=9NdU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICNVY+MlS4yIZfNqPSbTy41ltMjFCNFDdvqFyTjV0udoAiEA+psVAqN5p9yDI/bzPvrl0VHpLB9g1kT8ol2mTc5QUOY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180622000216_1529625904567_0.2620359087707915"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180622003029":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180622003029","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5e1fc08761039867c346b5017484ad803068869c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180622003029","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pyLdztOajgy+r+lqZTlulrqp8QTZxco7zeIBZ0TPS5qhc8D4YY92dzKHL2aQfW2MQmbacbQXjwf6oz+L1Q8mkA==","shasum":"f44e57d115de87fba1fc259d3354336316867a26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180622003029.tgz","fileCount":557,"unpackedSize":1211894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbLEPMCRA9TVsSAnZWagAAAMIP/R49clhGlmqw6vCW9zgJ\nIzGf0oOrdDFGaFZVVWBQMRjoA2/lM0SGtkyhB2UuzlUIHwzoMLu1juCyaZcj\nnIdrcJ6xbipgkQzG1x3vR/SBK4Vo/h+x2qJY5URqDzFkLkent80ObbbF1SxG\nm/lqXTFmLvi7rTLtshKvkzdS8ERmZdVNQAW0dzXM0421Mr353z2C0qG3hLSx\nG3BT/v4lzU61sAu300mLPSVXigwguCF3nwk+xHgYjkRSjo29YdKp2iCK4GgW\nYcKWdedH7/TQmbWsJSj5ubTrdYhVIDxL+k1Q0eSVIOJRawXmOuwnYDg/6pT/\n+BN1ZcCdGk3GQ9kBknZm+0YAgPaMOs32SPR9UYPOetjhm8/fg3Y7DUEDgohM\neYYcWnkO4rCXmNliVU4GlEKd0/G0MBHuQsFH6sZ50wpIucLmgbVzW0hwBrCE\n9U+gyEZguMZOmLwNd0HtLUZhOMo4gswNWtl9/70Nrtv3jqZe9Z0P+NopU+3Y\nyrMPeGKThUtbR56Guccv5ZrWuiiAqOvo9epIZY6Cay2VUsAUYVx42URWWxOZ\nnw6ar6VHsJYbt62zYQZA5FWVPDQgh/gkzg/hllly8l7jzZpcalPZ2YdMzB8h\nJpcbgWvzbjM4CeK+pJ0KaRnXZATpcrpAmEcNhqqqzGhE5cp3klVLJD2G2bng\nszPB\r\n=NxEy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCP8wzDOPIQXLSmh2a1tJai/ypwKTEjoLROlCOkZYzLFAIgOa4xvLfZZ1e6ItPjHWC9/zEMEPGM8n8KhhXXYXlr9cA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180622003029_1529627596413_0.40156721833637676"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180624172126":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180624172126","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5cea975842a1ddcfed58075648ab6ed581f9eb48","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180624172126","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-h0j2bOKgDIDahC3aFFjgWbsA9GWzwJZlnxMRDOscUxUipYAbd3KcypP3yuX2AVw2uhjEVCOtM6QeQ6lg65Ja6g==","shasum":"692c473656eda36151676118fc2c1d308c3f9924","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180624172126.tgz","fileCount":546,"unpackedSize":1202501,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbL9O+CRA9TVsSAnZWagAA82cP/R8upRwHqVskF2PWxkZt\nGxj0ov8TVges/0anZfLP5Ff05YDKDz94w77iPa3I8ZK81qvOX1sRccl10B2d\n8TkpOIZhRSPev+Q3THLlxQmZGsKKwm+IbJoEqPDRhEp2MH8gc01//sUI0HIG\nP+KIk2T3Bdkr0HEPvVvhjljfrsnKCAsxYNSaRvFS51vcEyK8x6fs/wJOhdc2\nx2SM76uz7N1/AvsUm0v53NCU3XZ8BmMvPSYw5XAikNmlhYUsJS86Soap45DC\n8iS0Dv700WMJ0zS22nIYGgZvbUjv36FQtL/1rdY1ws7cYohQ52c+cX/JOYh2\ne0vHgIkVcHNwqvwBMmrB05pEkBGGEApqQdetSlbfX279dR2EbB4hHrynWRzJ\nE0bbK/kiQKFFVVDmrfOIeJ2zuAy8zqBOPKfsSkwywBZnY5ypX88DbbR0cj2z\n+uRPIfv+6TgbSpVHjN8n6Ls4sUH9K5FeoQF/YOIRtfjYuW8muhKw3wQ99ucE\ne01YlGiZHJBjn9k1tVwdBimsTdy+NcwIeuA2ILiFtnkSHWhfktKMnz9AGLUE\n4werJrBeiuknO9GuPNjigS1VYhKiQVCQF4cEr+W9g9W9i0tLxdVNQeD9w1fv\nmF6LcfHBMxFtitFgYkC1HwQLm7tPTd2qgQ4Nfp2d+ulsXOZZkS7exn1B3XiB\no39y\r\n=WfM0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDoaKjNu/XifpyKnJBxt2FgIfe1VkPbwNiH0Lq3bF2uDwIhALMef4GzvO56TE0JRmaNtJ0IwVnxx6OqDFnLlaHZTl/3"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180624172126_1529861054287_0.22122679063948825"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180624192958":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180624192958","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.2-20180613153815","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"68f0ec9b371155310e479301f2f989914d76d058","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180624192958","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KhBaqSVdgL47fub2FUDmuR0mhYaj7wo+rHugfqBt164ym26bZ/Dfrwccs36R3vYlciVmTlvXD+8WTSP8c1FPiw==","shasum":"3e140f5ee16e34b881085cb90335cbc4cf63511f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180624192958.tgz","fileCount":536,"unpackedSize":1193407,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbL/HPCRA9TVsSAnZWagAAfi0P/08mqYGMt0eE++ZuAEz9\nmue8DEoyHaDZ/pouy/IWKvPpNHtas1PWN+15P6604CcUgvGnOxPt4Nc7mdF+\nvirWGHIhaSBswnzGBM4V7yx1L5aOnou4Y4bWZYARCXfYYXk1gYjGMrXKoXkV\npitG5jWltgEhSCr+PAox6CKdIcHXuAosj4sWhelbvytibvE/uq5sVm/5lGUq\nBlRzYSA6cAT59u6n4FuMwqGP46T/fmBPmL8cS94bpdSMkykdFXt/n4s9IahA\nKy2lBvS83WZ9Uaxc+UqpmcC5KjR0gQDAsqj+cpxZkS0HUmijM1Li8lqJPAQh\n9rWalEC2SUiw41ZrIe76VlV2HMdoZFN/MF8PgD/DozMEl/YIuo/rNeDbtCzw\nW7hMgOharu3NEtvQrQ6D+UnISPm14h8wbIMMvkWWQ6/ycuJ86HExr6PTfMO3\n6JU6z9v3a4hUe/i8Vgfz3Yk5W+0nes/8Zv6BgHR3E9IC9/0TUeCtvwUHzPBY\ngNr5OKYaOv2eyJdFCn87MPJ6Zdler82NdeaJBHXkTKWIMg6PUaRAtJnxlaD7\ne2ZE+ub6tfDq55dbXI8ckNkukzO7f1T+9pfboSZQOoJZN0Et/UVvViuibJe2\n87JCTmufFkPhoQjYiWwGZfFX2xbLEg+1fnpBli2n2zRhraGiAMFXId/RmCbl\nx3UK\r\n=oiDa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCX9JZu8PALeUR/rZS9lh67B38+zeMumbXIfAEjjmjf6QIgLyDw9hAG9oHB+NK51qHPgJc+mUsEk+d7O6x2m9usDQk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180624192958_1529868751216_0.13940277523463407"},"_hasShrinkwrap":false},"0.2.4-20180625012428":{"name":"@atomist/sdm","version":"0.2.4-20180625012428","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a6d4a3ca91a3982a58cae07c694f44e6ab3c2c03","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180625012428","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FlkCamkcFBvHHUC6r0T0fMo5IblLui1hBStBsms2B0cxykzIV+jH9Cy1edyvd+z/0sfiuGG4WAY0S3m1YB5eOw==","shasum":"3d67aef8bcfc45596ed655658bfc69355ccf7673","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180625012428.tgz","fileCount":912,"unpackedSize":1972293,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMEVtCRA9TVsSAnZWagAAYHMP/3k7zaUVUHM6O1AXp/kP\nhj7KbIIOBDyLbmpiI5R9UC84riggPsN9BRhVZVC/RbdlAFSBHroZ837VIShY\n6/OuU/RNxIAcvWy7ZQdGCI6RhrVlWH++VHAn5BeWrtEnaqgQtqABNT2CKqr6\n8py3vZrA426JC+Dhn2dpJDrF8BZii+laMPDN36CTeTUMMagslV9PkfY/NcO0\n8GV0ASF0cFhHY1hMbAQUjnGN56+l2DanfwIuuUoiwnp7cWpLS3bKPSbz83p9\nZQFrfigtTX6aRjywLqa1Baes7QAx07FQAwd8Rau1Bd93wmBGu867yTEZBDDJ\njesYmuM1ykf8jnGXTMlWT0T2DdCw36yFD99qtVATM1lglsvNWxYdBkJ3Zi8O\n3jaLeyqg0/LISOJyw6KA1gAk9KKQMf2IZOTYIYjyhV7jiTJVgMPEyILNMGGR\nn1L9Peh4oZSN9UrpSJ+0gDCVT8Gwm7mMKO0PGxOwrRvZC5Rcqc0szVXzZH+T\n8ZMg6nKSq7ML3zJI/puI6R2Sz48Wd6ChMTPqAbzPH+CBRdTjmwny9Exp8H/C\np8cAUve732341pv4K+6Vnn0vpUJyGMWSlPY1daFgmOSIErxlPjumJZkxtaiP\nnF8xGVFIxtPWX6E3CQaOsKrJYVcNc4CtMVb0P/hbrv4XpQ9CiZbEt5q+/s52\nGJYj\r\n=61+G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICjTpaImanBnHyFED6CtArYlgsH9fmCwyuGdgJdLYLU/AiEA/lxaa9lE20nY+0QFEfggkZDR6LS5c0yhDSoJ2QHnMz8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180625012428_1529890157593_0.2528988731609978"},"_hasShrinkwrap":false},"0.2.4-20180625012513":{"name":"@atomist/sdm","version":"0.2.4-20180625012513","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4b6fea7f452815affc02ee30957f0ecce47e2100","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180625012513","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0SuHJMzS5uEFgSzQncsN15stmoikQh6/aqRvmo4wlQnrkpXadzpBB2w/8pt2gZPTstNc90P9eKalK5erH98udw==","shasum":"8b5b51af516e747e08bac1dcbb6d1bf9c3f35125","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180625012513.tgz","fileCount":912,"unpackedSize":1972847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMEWLCRA9TVsSAnZWagAAEZgQAKAWPz3RPqGyg3H+DEof\nQWFpe55IElGjqL45lMgGsm3ThRsVq9OyUSHcmiaIdRuOfyfj/2eqY3EoNFMv\nCFEItjwYo560aoRTDG/0saxgG4rSFJeD3dd7HXVwEdYGrQnBhIdJJS+zBL+p\nr+2tpk4lby7pkHytHuhOvvC8obo8LeGXVaK89kuOI+fq3/dRKMAG+p0SzpYf\nufyxMVVPDNdY2UE3vFocGAT8ic3X1oUDREsNBOksMZpKVi4Qya9LZLhc9V4x\nRQp4AtHl8pa++ZVoCwFN6pd8XgIsSEgpvsbDJP3gqmb7i8riAfOcRP3zRcjz\nOEsC1ztqFEjHVceUzoGkM6VxHGC8IVu9Z8x6JmqUqA4gP0Op3B34vVSF9m+5\nqLiS8HaTUhgYBZFhlLUJNoIiNYiH5/KTzYaCkXUZVXxG+xG72OQokeHaf1NA\nckthGADAtCYgrphY+1mzy2qVh5RH6bEZSR2Jk9hJy52bwVToJKHWjl9aYnEs\n0W4ThEzHsJ7VHnFs4Kvnakd4W7jGL8CDmc8WnJL8HbTd/V5pmX+cgPr58ehr\nNIQla238F8VRrHFWHFlJSElpfgr9Aiq84jqaZ9kCGquXeyZVB7pN0vf3MQyB\nCv6NcsC/4DdBT1omcN9kSbKg6FUzeXf6rGN9BECvGK/sLajd7qNE3cqGwx8R\nUtF1\r\n=gio8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIArEbwXGXBhr4nRhUDGLHoVX6gzI4hfLVPE1WQb1NF4jAiEAohaAn1mJD6PXOq0j0IX/UF60ec49KohzopWa45ujO60="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180625012513_1529890187225_0.9379390908222778"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180625013532":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180625013532","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1d4ddea570e1a2333b5c82a77e34f55f40d9058a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180625013532","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xTlbAEcEO7Xg76shYrvpg9WUIisUbSU0ywk9Lx2nSppAkvDkyMbLxRaUsFLkWpYJGq6hPgSU2DZ7b2IncndgBw==","shasum":"a590f1c73bb55f60392c975bb925e97c1cfc1dda","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180625013532.tgz","fileCount":536,"unpackedSize":1187150,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMEeHCRA9TVsSAnZWagAAPbIP/2Nv2C0mX2P1cPlNFj9C\nrQpneIgL7xSTxDNLEJv2eynBx9knmboVn8aqBTpjmKjWQimz8RjTblNp+SsJ\nuFbZyr8BCn45Gp5xvyPxvHM1WqmKuBZBJJbPrrOZkUiQvSq2xiqwIlGbrEEv\nSV2HB280Jg9/hD4xEArN0XPEo35uHDD5sAwwMUUU5K9RJ0TdDed8JhP66v/j\nwDf2krpblxf2qgwSmfgiD0JN3flK7odiixhwj2prptdJZrNg3XLY1q14YpSy\nus/f8sEpWohvOuTunABRHK3pQRC6KxrQkMr6wcWcOaC0vBkGxLONUuuj3Pp4\nsljRO9/zBt/yKOhzkGCJaJmZZimfQgfsb3nKIbDSc623Wjbk23C2B1xUqvIi\nIbQotlal4PpGY32Ppq+2H/UNNU6t2gvoPok7W7dms4BwpPuQlOnGaF+MVkRx\ncD+RxpR4FJZQU63KH8nzGkJpcT4z0Y4v1VWBc8O8sOIQVBOvjADzbpT+ElEy\n9bzOC7EYGu0hwjNrkg+L885ZI0P8i7yX5MuEp4QY6cCyGJjRU+rxBz4X5VqE\nYLrX4Z+AmG90vpmaDNGjIN8HpJt8xJyUJqtk9pABbCZnANY1VBcHvATL3pxR\nEwQ3PcfStIhR+P49bZthio4CYh/h7w7w1LqJel+SkvlTHK71pdEuL5k3VMNn\nF3Xg\r\n=6W+e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIClY0AERQ9C9cbt6rch5i8pGSYyxLNRd3Ak709BJ7nP/AiAodwXdGLbuETvJuxA60Dk1DqaED4w30b2ZJBx3E4a6Sw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180625013532_1529890695477_0.09280232964881496"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180625025708":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180625025708","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7f6910bcd3cb01dcd9678149c57d803b504dfcfd","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180625025708","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SXJjQpir7rbDRtQlhkmttOfzq7Fk1IT+g6cmAGHtlFHlD/9ZTb1CyYBHSxu6xczx8cXO/hL7+ZfnZaYLFOwZqg==","shasum":"9658eb63d5433c6e2b02783d387374aacc7dc331","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180625025708.tgz","fileCount":536,"unpackedSize":1187381,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMFq3CRA9TVsSAnZWagAA5bAP/22n37AIGuAMdnQ4cfcz\n6vheuBQsuwHAdjDOFPQ6OWt8d08rWL5eu0708rIFrDPpJ3oh0p5cbGWYc6DK\nlJseGZfH3WZ0gqUpOKFngK59r3P/hUPHmSAggm8oARTig3d4tBsgpwHchCqZ\naO+g29bDH/8601AJ0fDa6jDAppWaAUihU4/qXxfBjhbIeGFIOf9sqN1HqzNQ\nbqspK0jYFyeOb4cLHmF+zZVGSmOrPzaGXG4t/w3vUZrHvUOGCDs8eaRqjpZx\n28IQa+SwG7FT8gf2wPa5VpOA8g9V/n1cd19gZk4uYOyf0CKy/gQVHTDcnVLF\n0SAZTztWYL1gp/go0avvq5q78RzhyNAjITZ0ZQScIdevFa6PKVNHuQ3seMMz\nTYv4LaM82Zvnfg2DHJbzWpTLO4oZuVNzKmlZBRprSkp2z7ttx/sCtmQCCn3S\nlWg8zUjMD/J40BBX0TlerytopUDIuwGn46HkISGXIz2qFYTPQgZ+TIygm8Bk\nDAHkPrTEAkD7Q/YWqf3YZyPSMllGdTybbVm1BHv+QdiBABZYaEhTa32q3yZW\nwopzKSWoNjas1G1IwdoqqZAMa/jBQ6yFfCu3HI/ZPOMs2+KLTp16MXefI5wG\ne9GMJqkXgZjfaWfeo8CTq5HEPVcMW73s2OV2o0U++TpigX8w/og3fqrjdtnm\n9R1R\r\n=Qg0e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGw4vfm3Q9Vh46X9I5YTajmmBKhLWED+kUd9/moRprzuAiBNy+l4LroxZZbrkPx2UIj+hJhUN7ddfmIKtyUOpZcElA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180625025708_1529895607204_0.3530019518979577"},"_hasShrinkwrap":false},"0.2.4-20180625070826":{"name":"@atomist/sdm","version":"0.2.4-20180625070826","description":"Atomist Software Delivery Machine","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","@jessitron/git-branch-to-npm-tag":"^1.0.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.1"},"devDependencies":{"@atomist/automation-client":"^0.17.3-nortissej.bb-curl-or-axios.20180618234615","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile generate:barrels test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6c6ab64f94c04c8f654069f9f8ccbd234690237b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.2.4-20180625070826","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7pzwXR/G9yfdeqPFjUw/2SbaGiJu+SwEN/3p+IIDq6Pxkd5uZiG8HGPP1A3s8m9eV5Uz8DoQ3JDewc6yGSLT0A==","shasum":"902bf4090a65a3a0950d582de349fd78019528da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.2.4-20180625070826.tgz","fileCount":912,"unpackedSize":2137800,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMJXoCRA9TVsSAnZWagAAAf4P/3FymQoS+gfJYaMuOVB0\nBjpdmpgd7e0OdCxuhHZnQY1qieA5f/iIoY1BNzoJqJFZ1s3MSwc5M3cCjE60\nQm51GcHXw2EFwhXv9FRUB1d1U3txVHGpSiJZxzL9zCRliTND2kpoICBz849h\nptK5CGNa4Myc08sashAk+tm7Beza88NGzRmLuUyhleMU4bwcv9y0m3Zmb0L3\nhb1gSr1WL0jVA/mCTZXZ1XiskuItLN5OEf3OpTNfX9ATMWUcAZnge6CeKo4l\nrWf+gQrd/Dx7cx+x3fa8HO/yDtcZOmhJo1B6uc7Y1Zjhq8GUg+LAVkbXLJyi\n+lRXdVGeBnc+S09EOHEgc7l22c8rzO7YUP7jxQud+zUfSP4XLNMPJ3i1wj6j\nMOpPfRdJorgQgckPJPMkow5+DHllwAllP27Whn2hV+2kGrCm2LGWVP4jnm+l\nSyY48RIq3XlyEL1WrRBw9vT6HeDoiCx/rCsldLlUXbIVu6zAHfgiIoKNd1fV\ntXNt+p6UV5V+EggdT0f1aSl0smReBaBu7j6j6r7cOUKkZ/i/helav+n88Yk5\nn/EZjOfISwUa4zJkQ9nswpKUnq4h18+CCBtGuCFV06Idj7ta55H/MLq+yY65\ntZRDQJaEjHh0CBYdT6MUqhL+Uky6jrGCDpwZlJIpuqfNS9Rmur0+2V9mHkwX\nLwKz\r\n=VXbp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGps3czQ+7Hqvsdadvyk0FYcwQkUy7aA02d+vG/ufEXMAiEA8nSlOqOTsqOXz4obTOzGgHlknA31CbrTInNqFvS8/HE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.2.4-20180625070826_1529910760251_0.741645646021879"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180625080552":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180625080552","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2a80087a0bd9caa72af41954936b08acfbeaa416","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180625080552","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oFDVfQlaObirz3FEcslxJu6xipcfcp4aR4gQC+7mmLoa9qItdzaUpKCmDQ18bsxOWDR3RhdlVwxT/+LsK8Qlww==","shasum":"3d89b2da401335c442f68b5fd75165bd67562a30","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180625080552.tgz","fileCount":533,"unpackedSize":1342553,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMKPMCRA9TVsSAnZWagAA61UQAKDRu1u09pfUXXRAEfvg\n2cpBvYRDdetwhlQqiV9S00LONnV5WN8gE7jszEE5w1ZfROonq1146/IFzA9D\npiKb54/W7cRFdKL4wVdEOiEF1zA4p27aF2dmHag2qyecwANeZu4KqzmncZ5m\ngenu00BcCb6HCmUsK6pY0xajrfTjU/rfUoBPXbOfdq+JuxSYjddoWge9adVU\ngxL4j6wUMmI3Ouvr7FExBlzJHTYTizP1hfF60ydugpKJ2/Mpuu2U00h+L4U6\navoUwb40nDIl0uTvO5cRuhazUr9zDZZdZbXHRf5Y2sqBsTjCp9s6NLZS2rxn\nKZprdoBOkegogsD8uc+7ZYK5nHOYYsv0L+tO1N7JnDGxBTMYgpMsYDlWjdpG\n3DzMgnLA3og6xiUUuK5U6Va+YVN3noyflH3UrmFTnJWVzH/uimUXWmVXJwp4\n0vfnV1eAz9uWYu9k9Bw9lxF7cQs4IwZ5z17urqL/MGscxUsEOtij1KWRdilD\nYv6ZD3oH2pII+ZxeDBo9yNf3LDy0yNL6Y+17J1BmJfMHIAnfz+F8MlWmAFSG\nezPM+i9BHm+5/o5l+fOJ12Wb2nOIPSZN5+JQi9KodIxktqlpNiTLiH8UxXGn\nY6D0ujkyFQ/fj1ye/uQFhze2JNYNW5rwdTGTuKEuAiozq7wOD6T0rd5dDsH1\nXHYC\r\n=qjNP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKGVNaBqWph0Q43Tvel5l35tbPdfPmTDch1b64o+cJtgIgAmTHN3RFiHRZoOUhB2T7Ao8ld3Tta2TtuQ3bQuaLbUM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180625080552_1529914316090_0.15337595744397348"},"_hasShrinkwrap":false},"0.3.0-nortissej.break-out-api.20180625084757":{"name":"@atomist/sdm","version":"0.3.0-nortissej.break-out-api.20180625084757","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0d9a562a9b5837e4bd79be58d7dd9700f48a697f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-nortissej.break-out-api.20180625084757","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1a3Xl5vCrhpZkeyI5gN+R2s8jzgwnqZwAIzC+ynHflTpHumAI5jkjqao2RlqiZTT+WYjZsKupqO9aatjSHseww==","shasum":"1e30001cf79d45fdff4405816e19499b883999b2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-nortissej.break-out-api.20180625084757.tgz","fileCount":533,"unpackedSize":1342594,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMKzkCRA9TVsSAnZWagAAlQkP/3Ldgnb0d8TE6VMF2bCw\ny4s0nJmZwtS8srYKDzt5xJ0BypMlw3KgN8jO4w6sxX9YhbpDmLAbPr5CDMFG\nfcprCU+Fhpk0PdRXxl72/hBJ8EoX8hH4ag5VQ6vw9MOn1Igkd5iGZVI3mbyO\n++Eon4ZqlDSopzJeLrik8c9wS5/GpNt4vD4OzLFM1MZhS35walRPq7Rn6nB2\nffwr+ofds/xzX5HP2Jw+/TuXWTYIwVm22i+cVxrEWNbjRgcZQ2kg6lrYhMhW\niImsJqGIVMbC66/cfmVizjzRvA19gDDEF3PmYUKWACopizCEOVP5hJg8x4ZB\ny2VEQOm3CC0cqKyI+fDXg4el9fmgyd2fUmnv1b70J7wSRz/5TXBgJpF3b4wl\n5Agvk8Hr/kVGOZRkph8F+cPO7PALa7WxYtzIxgQvZ7o7f1qymH2ysI1wPakU\nfiGYcjj2KMjYhcdQT/bdQ8qrsmBqQvYpONyjtCDYEiS72TyaJ+ngUQwHHA+j\nB2rUrfLWlRs619Po30ecC41skgBDtrSCPpaixWKBYGYNuggns3yALiERJmpK\nc7E6ZUmh7FO1vONnpJtVs2OcNmIUT9Ouvp55pFhsisug0CZZI1E9mkaXP+3X\nAyTBjJt9k+WyNvgYIg1zrLGE91JQo31Fjt4cRQliB1vE8RDDplfusa8C7aJO\nuWHp\r\n=VXGR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBaZ+VLCKiqhVREa1PHocr5FOYeM37x4ub/DAA/5zO4AAiEA0mYQ5Ew6sZEUhDMAIfePgiKpxDU+cto+ehPL55ELzX0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-nortissej.break-out-api.20180625084757_1529916644263_0.06936851111409226"},"_hasShrinkwrap":false},"0.3.0-20180625085140":{"name":"@atomist/sdm","version":"0.3.0-20180625085140","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3f7d7adfe8758fd7083db2c703bd50a578864770","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-20180625085140","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-52gsHbUMnPd+oJ4xmt0P+EtZwEDgBVZq/ol9WJi6Icw1+LMdQ5mFvxWahdV8vNWaN3QvmtZJvI8MJQk14zqsGw==","shasum":"c4c1eb7e423a8ed562ab13a9da5ed1b654310e1a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-20180625085140.tgz","fileCount":533,"unpackedSize":1342570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMK3DCRA9TVsSAnZWagAAaVwQAIsh8gTitLIJOrQWW5Id\nH7IfOgSLWCpDAjzmnfcOikeOIjri/Z4UzLG49/AuT+9DItWSAUGZZZ2IqZKu\nBsk4YnkCOyF94HLYhkABDUrO6aa/7gTQaCyEmRQ9pSUBHpuzqO5+KbpkX6+c\n6w6ES94jcmF8thPQtm9VXHjV+G+BmYwysl2nfoFsBKTDf4NUKOusxPtsAvDK\n1CewappgYMiXi11d+LZ5SVy0JbkKw7rwjMj/JOo2v24DuCAD2ajyHYO6IkGf\nxRPBEmbJDnKKXsMjhgB39tcMaGMaKv1vlfc15Jh0yrhc0V/QjNFWhCmAw1RI\nXCarh2SkMEIBxoYe+jDmdKvpRqi5+Q3oGS2ajZmb3ZfBcc4DOGV3Bk+m9Avi\nXmr3/Pvpt25fAjnOYyqJXATeFlHUmUnuhlKkE7eJJRLpNW74gkgEWFjR5FAp\ndea59KeETPzOOy4MO1gYEHxMJXc8u7h5anWCQVG90A75CX4YQY/VNldm21JS\n7Q8uni9YOe/8e5zOv34mdQKhch//V7j4b80pBFTuCNNKGD+MIneF/LHoJFEn\nEX7Rvs1Miqrdcte81Mpay5ij8H0M8GFWnWtXyDEeWnY4VSddkbAyrUGvfROZ\nvqnHlZc8BD1DaVoq/MUatOIZEFCG+jlD+OflyAj64if+9wtXP4YvHA4E9X8s\nJpBC\r\n=OnkK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOaNMhKeCw/JZ3+r6+QZwRI/icQzg1634vo+eGfRcqpgIgTGQdgRcxn9scrWm4DfwXJ2jcdQuIJTemCftAjtS9p1c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-20180625085140_1529916867209_0.6836341068399596"},"_hasShrinkwrap":false},"0.3.0-20180625094611":{"name":"@atomist/sdm","version":"0.3.0-20180625094611","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"36cbaf80db92d6d956e26e533519cb7151711f76","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-20180625094611","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ty5p7VDdMDK0INNBTTF9a5VStZd9yr1VcktFi0iVRt/NB+se5YP2jS0hOPd5kFRmDA1d9zItUFmmw37jGu4iEw==","shasum":"e26f983f0e7111d51a9c35cb63a8b70e55224847","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-20180625094611.tgz","fileCount":533,"unpackedSize":1342806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMLqRCRA9TVsSAnZWagAAzG8QAJMjNvN7T/TvUhPniMpz\nnvlUYpCHXZUBCXqdYxAJ1ppKDfdgihEt5PE8gvwCIbBZS4svPz0EAcIYQ0nH\nYNqqkJw/IfiJJHkaZ5T6xIC9lHyhop4xid1IVN07Xb7MoxTOIK0b+N5LSCm7\naUXYij/Ev600PF8eg5V0+FL7zAtlkL7rGNYUfqoJHICPKrNKhSg6pRpp5kMC\nTDgls64zxrGf++vd8kYBjAjCayriMJoJC8rt4tYKoaNQ7dVWd4dNZtG36nur\nyrWlly97xQTm0bexlG0B5MfCaLA0fM447rNBgl7fRZNr0MsiMVZXphbg5eNu\noF3HQnkdYOUqu5H4AtUiMrgWrI70BrUn/1s3P7QsQ0bna/74zAr/iW1Suwn9\nOQ6KS/zVVsNmoLslma38Uo4oU5kkXhX4wy0JJuaEsebW5cfeJREubuBzwB1o\nk3+rybs65PNjGmuUixr/WIUT9E4Fi2k60YFD0cb8maXR0sihmIMtHnv3lTdk\nof+hzZxs5MQptFLNswiKhrVwbAJlAn3WPOl9THEkLl6GiYa8ClK12x+djdIY\nNE/Ux9kujZTj0pXNPO8zzZRDihQW889qcBoSgNozkHBlEqIhlTKIUnWagdsz\ntS5ag5eXIJd7PPCzrJ0p5aWG9ToYbjY/ECDbf+tZohNjg6wntxgUeEWGrwNI\nX+k1\r\n=CQRJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeyr0uXkohOfaFFJHBNnkZIiQwOzeXrcj3OL2Hr741pAIgT5wOG72Fu8layOPWwyVCi/mCa/qMdiGfFMtJ8Yro0I8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-20180625094611_1529920145544_0.7504838336839763"},"_hasShrinkwrap":false},"0.3.0-20180625100939":{"name":"@atomist/sdm","version":"0.3.0-20180625100939","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cd165d37f78804bae711ff65606ecb17b346c063","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-20180625100939","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PEnIPzE4EBuycmH8LuAYq18dH1b8Kg0xQq3pfCx5zjqkCrcizWbGYNPE6gIrSCPuVHedsy8RPWe1w6+vzPNFtA==","shasum":"f1f758c77f8ad300907910e4a10a5285fe656c46","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-20180625100939.tgz","fileCount":533,"unpackedSize":1343319,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMMAOCRA9TVsSAnZWagAA3/cP/Ri80ed/zLxQOrK/yHGw\nBm4fXbmVekOM5Ev2qNFuPrmHRHRaqAKut0PUONOf4Mi/ol4VZYqz1gc0GR/z\nWd7D4r6QFkJXCpgo/Eu7l7AXbj0FgbPpACf+DSFnBEbkVGPUGiq3W/nLEnzj\nrhfKhq7Pt+PlgIOmDFFls7xtS/8uJlL0QOoY/pCwzNZGKDdQpuKqa3wO2+VR\n2BUcCNBSKwyGwOH6nB/swL1jdNBrRK0OSqhBvEgbsJP0lQjr7DYGiXOMPZct\nhOhnzT/ER8kBZ1Cr71Cd/xbm8m1o57EYTu6cLb4GF2INWHldi56sTDbRHKgQ\nUoRaSyCuMN/bU9LnLcPMU0sMbPoOYmW3JulWQ0xOvxkdJBrvB5t8x6m30qPE\nT+qNuu8tZeW0/qS9Qz/8VoPtUOCoj2ASKAcJ0o1BwqVUiJ5FB1d9cGA4aSf3\nleTEB5ciAzGB0tYtLA761SosHiytySpZyyiyv7tVaubD8Nhj7jkmwYfm7RVk\nZg805QdjCA1O7ju8v5C59fcRH/k53LBLA/EtZawTsutUTmJ18vv7BwXLOpHH\nMCpcK4fpgPboaFN0Hfj5mAoqT+u7OmjWQTls7xqXsZ8JeezniI5Oov0UKf0p\nxxpOVmCGG9cjH/TeU9RkWJ01kIjrX92zdI7bw2LJHnC554xEvBFihmLFpAfQ\nmpsk\r\n=v8QN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzXRg1EnpEUE79/9Ddxdoa7dkbdJKTeeru0TRd6AWyxAIhAJYCFByaMOdANCrHqn8fP5a99zKu5CjA5xXWbRrX3XGG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-20180625100939_1529921550062_0.06109257242296251"},"_hasShrinkwrap":false},"0.3.0-20180625110209":{"name":"@atomist/sdm","version":"0.3.0-20180625110209","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5cee7badd82264030b174267139ecaf24690549b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\nThis repository contains an SDM framework built on lower level Atomist capabilities.\n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.0-20180625110209","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GnVtccB+DagOX40FjNJ3kgCBjDOS2GqrroppF16kGaK67CTEHqEni9r351jMRgjHifOU20n1AbLqvtDUphFyCw==","shasum":"6873b216d10a340ac216861833a5ac8f6fa07ff8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0-20180625110209.tgz","fileCount":533,"unpackedSize":1343500,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMMxbCRA9TVsSAnZWagAAOx8P/jHutHSa5+JKDG1H1z7e\ncSN8HlMv3sU3csd+W1vwYs0xeI1InyE1Xr4KzNePElZ5iFj0tsvvoAIR+jf+\nFYxtcAsulrpD8MDTTCo+MF0p8zoP4/gxR+NGKfkASL7vbbIpjF4c37VYLxfj\nZqA6G5pe4N/rcQXgR+mJsxD5/8N7+Z2KceptoBLUlxVwmP7iJ2wWtoGo/DT6\nd/MIyCUcUwOz/YC+jPMugzz/Mc7SiSBHpF0ZhfCknppjtKIewl8oZ+FSHyUJ\nINbUgQmKVBPj9pBtZfbBzjkTsBdD9dMKYN1PcFkRSjWEnY4Mug6sf+RVhz34\ndB16agpwOlrA5peYcORKOOI6mD+Zx2KVKcHWefw3iCTi6YgdVIhcAwa4vIGQ\nzK+Gwltkvh1CThmKO73KtUUF6Rgc2vQLX4RG263InOTpldd+jHwQ3lcp8nQB\nP7OZfEtZDd5a0ufShayCZOYrcrMpNS0Kd6qFv2Qg10AmEJESTmkUTMjQv911\nkiHuTLsioj4XhUJ47NiX43X3O6RUQZptypVwtrhOTp83jrjgVp3aHJEhy1uB\ncPvY3pEbZLFB2rLuwEbFdADxsfSWtW31ZQZCS1EEHNhVAqOgrU3qSArXaS/M\nbk4C4NanmfbOpjsu6vVbVVP7GlsaLaWF08J7hn1UBu8oDhBJGPCjpTLLuG1J\nRZrP\r\n=MlAg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpxORDGaQK8Z1zsrx2xgPpv113ey9lY8IbloHMFDbyugIhAIWwzDWxR2XATeSQbIZTY4N4vCGEDwPqJT4Qj1E5IEm2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0-20180625110209_1529924699450_0.16427546367360457"},"_hasShrinkwrap":false},"0.3.0":{"name":"@atomist/sdm","version":"0.3.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5cee7badd82264030b174267139ecaf24690549b","_id":"@atomist/sdm@0.3.0","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XSF0Qo6WRQVkz4UcVBcXn9pKVl3ZISciP6bnvAtdhhQouAY6wTmfMMmN9wzE+Nk7GcMGOz8l2cljZ1j5XWvLzw==","shasum":"33576f3f51748d827e6cc2f5f836040cd8f226a2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.0.tgz","fileCount":533,"unpackedSize":1343485,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMTvqCRA9TVsSAnZWagAAjTIP/3vr9Hm1VlGN9c7i2Xg+\ng2kKKBv0RyJhbK12BBMEnQvmUZSGOcyIEA/cstr2d77R9Jq1F2YFrq8kmmA4\nXI+D1cnBrnLgk3/Qg90yLWOt8lKVOrLuM20NLXc+9OJF+RTmUEKpM4QB6SKx\nDxaABBouPURA7Fyp5nKdudsYzqA476RPbIPgbVEnOyXuGr0bXORwnyb8XXqu\n7wppxMYfhdwo7e/tYp8PQJzB0RUfrwSe5UvcKGGj5wxtIPY8HTGykiWngHIV\n2QmEEokkjE6p7sZqULpZI4G2OXMQuz/LAaeuG9fizo0MxEiJ/COA7Aez4a7n\nrhtEZmvOYbBijwpbRUzZF4R34+GzLdNlXLAN76sJ45nJfDbM98re8bqiGxGM\nFyLIAjtiTey5sxddqVmvgv8Yz+/jb2gJs3ESw0HnVMHqz0KLl5LeLuWD8XMu\nsDkglO8vxETPeGekA2/Zky8xRhERVP+Bez6uAav0aatVZPGFth2mx8gQIpK6\nMAgZ25e9WVOmvtUul3IyJpvlPaae9jqlcopAZILzyg0hPwNSGY9H5JXm/Btv\n1SBGDppP+rk48I0SpvTMEkS1LFEJ2hkA8zB5/2Rc9Cea5zAV3NwxP2n1GHXW\n6SRIYhC431QTPvqqLVfNfOeBh0VLO1N+2LHp2T3acgD0RamEXZRp1E9pEw5Z\n32TO\r\n=OUuE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCb7huZAY5WI7hVvoUWVex95rjFYPCnF/7SaJDSgGa6DgIgO7DOtbBI9w9CKffGzeDH438CkPwBVdGgVlrewtUs3bM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.0_1529953258552_0.04034794499395189"},"_hasShrinkwrap":false},"0.3.1-20180625190323":{"name":"@atomist/sdm","version":"0.3.1-20180625190323","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"47124f74eb8cd52192ee5f04b89cd42ab7af3167","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180625190323","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dMrH8hZ1l2guMBwOEZJitIgOFsMfmq/GH0K2UOpRcB+Fi4K8/0cHL3aHkXqmgExX1emNwkfF1sHhuhhSBpLtbg==","shasum":"a74af6d4dd177c8396c97d502e7e04c9121cbf22","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180625190323.tgz","fileCount":533,"unpackedSize":1344254,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMT1jCRA9TVsSAnZWagAAXTEQAJy52JY/HAo77uFlWCy/\nRLpp0W7Wm+A1wLkLXrgkMFUtqx00UJFdnhkMzzEXJ9+Pr4+9rRpB2klMBBwH\nH6W4Vt5tbGKnSwwamuKYnRBFER8XdD7zChS8m8jLtfceNtLF7B+GipbGw0Xg\nU3tL9Yj185yx86AfZcpqxPIMDbRQuBSy+s+XpZKRep32wE0IVdqgI6vl56g4\nz7rEmYGw/f48KBqy4xL137yVkFaFM4JWCyQYVvj2FIwnXfvuMCuzzeGyXhMD\nFYFHE8lSzrxehOEMpJ6FoU4ArFT0OiSbBCDXHnFBX2GasaU9f4xkfyVbZEW+\niQkacmRfHjw1c99riS7WyRjl9KWBAZXZva+g7w8FEeRFsJFk7ASXCEYF/HAx\ndquVqu7WKUjBAkOtzfKD1yDpQz1H4yWMfqFuTLUU9jE6wgPynOxQpJ8CV9AH\n4U7zqhyivp5R0mrTVc9gNovbEUbYOoL7/+7a3hAt4dXYOZLCRZbYALjX9X2N\nuQ476ksbP+4DMtKSC9o4xxOiz+eWxuQ7NZAkiqGXxh3GCyGr62krclIABSVI\nw4MspEmBgj6d+rFnKc6zTK7Cj/vHVsm5ZBb/K61dJFn6L1eDvMr41xElzl6G\nSXbn9ITpOaJHTfTAR2lsju8JSRfohBcFPCVc9BOtfrNA2Qj52TYPbmpfkQC0\nFAVh\r\n=EXh9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICWunUeXfqokSdzn6bBMLgC2s+xtd80wAfaDHZ7jVSKRAiEA4m0CwelBxtwIuXZh6b3IZpXHxCswk7wkuK1WnfwXCzM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180625190323_1529953634980_0.23501383572024293"},"_hasShrinkwrap":false},"0.3.1-20180625215920":{"name":"@atomist/sdm","version":"0.3.1-20180625215920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"46d3d66bc489bce549a85de5df2b2adf41c6e643","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180625215920","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-12sd60tKxSfPH4IDZ0ia9207vGTdAKFpHMO2CXHD3nanSwZpYW9ZzEoxy9WOWbRAbFLvTnObGvu1gsYkGSohBA==","shasum":"e40e27488501d86570d5d1be03b9360cd165316c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180625215920.tgz","fileCount":533,"unpackedSize":1344330,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMWZxCRA9TVsSAnZWagAApJ4P+gMMO+2pUgWyS7y1Sttm\nvbmFkQ8WmAv+mM+FqynxrF1SEbp8PRdevFbH4N0/kT9qSErnswNpWiVFToad\nDQwxJcIA6X1ZkIHKVRhoPHjn2FD7ig+aRPlHjsbNC50aI54cvvU7/un6R8zQ\nzV3h33ucmz06xvJhtBjyWCMhyOG+XSMLo/n5Dkbb3Bh0GH2ecH02nOagppqS\nkfEi3E8R9pKxw1kgS9wi2X2WfGbgUDUHWDAPoaRxrUAbSvu78bgwtzdlwrrD\n5DPckhlRgZfHr5ROe7fR5TTYNdQgwTsqiPG7IxFoYOhNVBkKxuyOyG8F8DWc\nmx/0piHkxKVHKUrJnj4TjLQOE5ak+RIlmYnyezxNX0XALFKASpTZ84Q+jtP+\noPDmJ7YrIwH+t14av9N6Um+L+WFj1W0JcTEEEVaeWudHeUYEboCX8EI9IlUP\nGJyhEVh2SPfcC1eVCQfabo3wMfqTwk0bakLJIsl1mFuO3Mc5vLiHIOVGN4Ip\nCra2crGhz3JgzzN4WiDC5rqstWRlKwbJ0/NhFWpcb5+bbVaG/ajAixF2zc06\nl4m+p1jpKogujo8vG1LUTJY6oTIDmVV+sAfhrafpF97DfaIOXThXpEOxAV8Y\nSGeb27l58daaDZFJTsJaYMOpBf/RpAC76BdJoDpg4QjeON9kfHStMMVov+J3\nC/53\r\n=NtPf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDES5x383k1Tq2rdn+sdp8eB+bVehW55B8QWv2ai5cPcAIgd/oUGi/UGS8IhGduoEBwqGKzfry3JFGEjUMNQKH9IYk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180625215920_1529964144896_0.4587852947740656"},"_hasShrinkwrap":false},"0.3.1-nortissej.keep-log-newlines.20180625225614":{"name":"@atomist/sdm","version":"0.3.1-nortissej.keep-log-newlines.20180625225614","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"734a9480ad357f13460f451c6b238093a041d177","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.keep-log-newlines.20180625225614","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C/EfdduAS0PqNMFMdtk0miqL1vi+CqVwPvS21pqD/t1dxJi0Z0ltcy2LElSlFuOV40WUJlbOpjLOA/tj/eujlQ==","shasum":"36cf1ec1dd620112822a92109837bb1224d4d2db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.keep-log-newlines.20180625225614.tgz","fileCount":533,"unpackedSize":1344399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMXPICRA9TVsSAnZWagAAsKkP/ihAIi+WDm1NN2llHylo\nuOMPvoyfqpYRYh6rAUP/zi7JO5xbVQvGG9L0JMvztAJ2XohzWHciRVUVn0KT\nI10rnHY0iOU0wGt3Kv9X5S0EeJkwK4EJFbTRu7/2JiZ9tVTqpajOAuhId0o/\nvNkU0Ikn54ARmojMlriW3scIP5FDNOqqh3VtpC/eAbtGrOtrhEY4wwyYdgBz\nu3FAxcQ4EtC8+hVWZY5mzL18Tv3oQEa8UeGKdDZBOwTbvSZje6hr04HZx3N1\nAkFqaSkxxOfrMDs7098DL9Ha9hf1tU1Xq01D59hgKPBKv/3HVbkx4n4Y+Za9\nMhd2/klQnCLsLsZfcTJR9ZrY/L/B53Ukx2fzun63fmOEPn/vCENk55mWSJVy\nVtXKJhTtaJKRMF9czZJzytTNM5D+ei9wpUslTt6iHLVQ7dFjzOsAl69BqJp5\nQuivLtZTOBLYnKkpdFdpmRdfyY1OEZaslcMZPbdWWKWs+i9pqjKkTYy+0Fwy\nGYk1yKCdeLJqphlRh6bDiMqA6D5kJQel4sDylCkqAD9cW2WTmrUZUeJLPWJW\nYb95w3+Rw1+suQnXnloYEKuSPxarL8M3xVVqRTSSEAz9hDImsPGFNauIwGBW\nt/vSRvGEBwJctzmvzP8OzLWpzGnrAWmvgPexbIJ5AolJ1mEI24FWgPDRDKfD\n7fnG\r\n=nEJ6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDlfK7aPbleJ1EWu6mfEtG5S9nChI5RnBmuqrIN4mN6fgIgTNKZDciHhceKW4FVAIW1atR4dg4tsYcLaSz+C5kLw5I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.keep-log-newlines.20180625225614_1529967560644_0.6648041610121671"},"_hasShrinkwrap":false},"0.3.1-nortissej.keep-log-newlines.20180625230522":{"name":"@atomist/sdm","version":"0.3.1-nortissej.keep-log-newlines.20180625230522","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e19141a8b9d934af67ca07193c8a3d9b08cb112d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.keep-log-newlines.20180625230522","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-W4JHgAsx0ao4GY3VC5+nsxFoN2VxeJcBO1AfA+QkhE3ciqabXC2yHYrd6raznGwL/OxJEUj29OFV4/5xwG4MFQ==","shasum":"2b849a1acf6624b7058174963a5a5a4cbcccb6a5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.keep-log-newlines.20180625230522.tgz","fileCount":533,"unpackedSize":1344399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMXXrCRA9TVsSAnZWagAATNwP/RPVdL6uz9ZRP7K/zKvq\nnHVuHhogaHRmMb0LXLTzoKpfhCgpViK7dgLQncHzREOFupBW8FuNhljcWkja\nwnavkiOeSd6Rfepd77uxiEpN6yrcKbUJLVhUe9DZz5mKC128IkGiKdsMzZ8Y\n55NHyc+AUlnFUddID/R0iHRVKeEmgLLQ2QoOsbrhUWsOJ+9BD/PKI3bF2DPk\nr3lC4AQPObWVuECf7XSLayxv6LXU5zIPiqFLCoV73IOSLWiON8jWYCt33rb7\n83hsBoOOo+/uTvmBZLhtMQxLqop3C6+IYDIigjkoOz1zwfiOnMLO0k3VhsI6\n000Hu1gLh+5LM71QwifsGDCG86oJlElk30R3aUx4PyoH1pyKuYWDW6JsD5LK\nDrwO1N1S0eTdltpHS5jfvjYVoXkt6L/shgq+jpZW73tnYpTJ/TTWxBY0eblG\nPpdhijpvtKzfL9y8INt/HtwZD+X6XunkD8mEX99ZGEGLqaCbAjNYeIb5VpDY\nsRY8hetkh4wFqx7716ymzelT0P/y/HNQY9VvS9Sdjx6v/AgSeIG6x0RaEZpW\n1Dp3uJOQHPqJR+VwcuAPG66Lw5smsoW+lyrSPW6z6c3yo16vQ8dDZMVycHuP\nbQy5pxmwBqL1Y5gZjHQbAF+W6SsgKRAn6//845Upwk/LXFiodKhBNbSeuv/5\nWgK6\r\n=TQ8A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQu8x8rDHvHq1GfTDFB5QuAXTsPsjGoUE7S9JGMrpllQIhAMg37+99qJ0454xVNCYJGYzIM1dwZhvIDjCWAPTuuu3m"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.keep-log-newlines.20180625230522_1529968107031_0.8131899748530764"},"_hasShrinkwrap":false},"0.3.1-20180626143351":{"name":"@atomist/sdm","version":"0.3.1-20180626143351","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"47c9f1c114a9ef948ea78941d78b76edb41ef356","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180626143351","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aCec2l80YQeOrs8kjvWixNVtW9HdCBuQcKg3WpKBtl/L4XiE0Y0JNDKwqGiFNDycDl2huNEYHDUXUBrHnCeSGw==","shasum":"a9b2218d1d51a0f7e54b3a3a278f7334de4053c2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180626143351.tgz","fileCount":533,"unpackedSize":1344371,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbMk+fCRA9TVsSAnZWagAAffUP/1SfHGz21IEGzP1i65qg\nWZjV5Y7aJzX4YSduLY+gIjDbJ1h95Nt14Tk594D39uj/u33F93uxAvWnCpEK\n1yQXZrLmcG7Xo+jQEPSr8x6YetJ724clD4lFCNWBp38aq8dvacOdfK1b5D8E\nwmTGtwSVL4TIbsCFgdwN7elAQ+nqxaF+nCifvgag2LwqueACmk87HelVPtg/\nYbHhtH6fso7oCnszCBRbJM0gsgEMa33BmcZJmc5mAq48NAtyAL/nEQ2tDsd+\nkM3Z5EfT4+dO11x6XFrSditvAQoOO6XxGDzNedexEtlSYdnC0z5jBnnsH8GC\nv4TnlhU3WDEIY2ScP/VM/LnxnH7yyap++8DoY0PHjKnbastO2svdTruFOcwi\n7VrBsBee8KhHEPSdLDyYLxNqb8ugYmMKqhYKdae4+pzapaLoPaREVr7ekg4s\nc8l+zU2/K5AkrmNyHsdQe+k1VmB7bxQs8BLMssx7AaRlnqMDA/4rnRwX1/KO\nVyAT1pax3Bi6rBazs90fz4g1XALelnYzUWHGtq8lXZtBePXUEhAQObiDfB4F\nmTUV47FBcmVWU+d36KLj7DvJa0Wncbaf+pjy96tBJHzzHuSL766VNhA3RbJU\nCPvlPpNkt6qpj4pCS5MzXez5icehVfqBVtoB3qH7O6z+354tk6IUSb8qVm6Z\ndUlB\r\n=DD4U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmeDuqYZiJ4jtOorr+L0Z43EbacW5HzE6ISlimHxnFtAIhAMvaYN0cVCDsUZsddsNvd8SGYtElQkUnQ+Q2hVkjZZUx"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180626143351_1530023839113_0.1620693454060531"},"_hasShrinkwrap":false},"0.3.1-reg-simplification.20180628133119":{"name":"@atomist/sdm","version":"0.3.1-reg-simplification.20180628133119","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"39ad35a643804bd9a639f9ae0314432049df7b5b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-reg-simplification.20180628133119","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eneW6bJUp2S6grN7otYMPK3NCt98MstMAhT+5LzgAauiwV5mKvvg7b1gVLiN5KDFBnkyjdbYZGiJ+yzu7WppFg==","shasum":"0b0b0417d0a76b459d71bafe79495db03f0ac3a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-reg-simplification.20180628133119.tgz","fileCount":530,"unpackedSize":1338278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNOPbCRA9TVsSAnZWagAAD2MP/3MZNsdR7zQPYn6jaqlq\nPWrJ0787305smzLwzB/GXegF/2jlFxIkVsDV66RtItsmOJQNPiphHulX889K\nH6b4+E/KoNy3uZ5odU/HsuoJJwpmD0r4vQXME4HUib0rgzD+wJYdU5/u2LzH\nph91ZNC3hLD+xK5MFBiHEuIxS70o4KWzCc7/1t27tr+Kay92/+k0HoFCxNkr\nch+OqOZzdmoKNtn3J/IU0TUocN/6BG0LyDv3/qDr+g2D3ELkUxd/mkPa4+w0\nN+aFO6RWMZaznIbHbphmILjdbMogN99mfcBkwdxWokx3gDGO9CQ76QBuIKdv\nu9t7w8+9V0Bckq9zi5OjL/CwYlNJgJG4xoEf3iJ/XInJM7Jj5EzD/qyISlyH\ntr/3IVVL9ISSw5kniiK+z4wS//q7gfZFoWqij4Dz5Ni59xMV27dMjtINExcm\n60BvBDZgvJx74SHjPdOTdopuaiqhJ4yujQGop5+L1X5xQodjImBCLnCGEhNz\nSNCjUZhH1S9L5LNLvDxeFZOxpyAno7bz6pErsNJ4m7tsm1C4xC6khnqYxnyE\n2RX0WklqxLb3y4mvpq6QVSxi/YkIOkiWiULAHF9I/hd2NV/DFKSCtuCgZLqn\nW/tfY+JSqtlyYokmTftCkJTpieR91zDYhN33CKfK2laNOf/nTX62bw6O8eYH\nf294\r\n=6W6i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3KOlAnkLFRy/SX+OvJCDwYyboG8aFecSYJyfvYz6F4gIhAKOqf26CDzx014D7eLFgTg/67YjmlV6YABtaPAUF8IwR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-reg-simplification.20180628133119_1530192859634_0.8834432974359421"},"_hasShrinkwrap":false},"0.3.1-reg-simplification.20180628134819":{"name":"@atomist/sdm","version":"0.3.1-reg-simplification.20180628134819","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8e9e9c645305a27346c5525f763afa487af1814a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-reg-simplification.20180628134819","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Sx/qxheBQd56y/IE3M5nwyvXvtiXbA16C+s/F1bJipDZGJKOfsN42lVcAxKgA6ifAS3E/sEx2fvPOyo80F8Dxg==","shasum":"6341c5411019fcaa6f1a44e345370c40b8a72bf8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-reg-simplification.20180628134819.tgz","fileCount":530,"unpackedSize":1338291,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNO7YCRA9TVsSAnZWagAAdRkP/3WE2VTP6nN0fEDaopuz\nW+nls1QSW/eYlRaughKWN40d3vo5XXTrKaGBfm8WFpVD7MOOxjWASi5gwQZJ\n7x5mZr4lGAniB0BOhjCw5K/tVVyoQuRVCxq8rMnj1PDO2db27FoeNfiV12Tk\nGEqk3BcJrE1hFT//pcy+Yd9ymt9Nz5D0kEB0KXmfng4Z8zl66xCTdp9eiZ2A\n3B1GU92Cd7Clzklk4yxCQo/9eSykxvDTT4qUpuGYCauLIyyrpLqe4h5EqRV7\nEMQ0i99W3wYIo8mH26Fuwoih9yQLyPJjW3X/2jga2c//jtPOW8ZF1hz1iaMP\nneoBj/+78JqUts16izZHnmDzJJzX40H6BFEoB+v8z5n0LGhFrr2U5mnbt7f9\nKIr8Rq2u8HLU+SmL39tQe5gkzAx2totjeRcNm7NA7ujoblVoX664YljLiCPv\nFZIfnVKnqRaAq7ZhTCm+TS/3rDgLKQG8aJjJkddWd/U8PT7Fs+nNHn8pQL2b\nRaE37xW99P9ddviO/UYpqr4FDk8z6Zjajp6R6B6hEuRMnWnlXTooH2ymtacr\nFLHGvE61AvnV1licurFtiZCo2YYMxPptQLfqs3TLIueCpqrtpyXRfU3/yBLV\nGzZSaRgVD4YU0/2xyVA4+qnNNxzmSe7V3OyRZcPTQ7ilyvJSdBxAsWAs55nx\nhPJG\r\n=biFZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUuDe29VaRe/eAwMyzs5GZjJWTjDptKll9kZbjEVxr9gIhAPlKjD/HsOivkaclUqPbKEzBKpNauEN4bl6uV5oa7Rds"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-reg-simplification.20180628134819_1530195672417_0.7623820143313658"},"_hasShrinkwrap":false},"0.3.1-reg-simplification.20180628162605":{"name":"@atomist/sdm","version":"0.3.1-reg-simplification.20180628162605","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dc108aaf9e1d12005fabf1809359bb50dcd6b481","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-reg-simplification.20180628162605","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OZ09ydtGZemA0miwl0uqiNWl1m1KU6ZZd8z9Yi+psb6M/2WPRmWYcxpKp397zVH8rptFB/txL2HloRYi2APqPw==","shasum":"078fb93c2f7b2154732f9190202a611d467b831e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-reg-simplification.20180628162605.tgz","fileCount":530,"unpackedSize":1339170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNQ1BCRA9TVsSAnZWagAA5MgP/2osaJUj23p1m42vPMpf\nPyzj1DEPjF0q0mMqMyUiZLGNj/TbU65lwABI6DPDNamCeC2yH8DcfybW01p0\ndTLM8GbE3faYRc6JFTB++BZj/ez4nIuPCeZ0wp47WWPKjLvo7R+DTbbnCr5/\nm/W6Bk9Ips+azxUpLeDols/rcEYJ1ef8UpwPCN3J83R4RKl3Dwm6g+dDk6Ii\nvUPGUdCOv0g/Jv3hVg7rtZvvf2bj5twlJttpMdoCwa6W1K8yhQ9E8RK1+PXT\n4qet+wRWdl4mfghCu+mnoiR5WbxCaajhUEJsGntMEhIFB0dnESH4xcFStsM+\nc8xfB47DPBtUkllQpNAbDQVYVft2YRROb84br+UCejWcFmX5ePIpn8wdPb00\n6EhhFuXfiTpwwtYeSqOU70lkO6Gn6Nyn71ZZqdEYNRJ4rzfvsxwQV17IJcPv\nqKaUin4IKn43ipPbzOanAcdzmEShaaGJJ1s0Z3hgv4pSq4cu5cAfj0PGUMez\ndpvN90QIxW39bS4XjDn/VAUvU7NvMZY1Z6e77lXpDKa2q4lrr5w4j/YJgu8M\nP8cv6zEtJFLfGf06emY7Jzd8+FdMESbhwFSxt/1cGRlXUMhS741MKiEfTcC5\nhn/GyP30rwA5uglh5VcCvyHdpGRCUeRZatUWgzn6lFaSx82PFnARXCnEyRrb\n1FQz\r\n=CUN1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClg/xsF0ycSHBCCcsOM3HsQSugAtQmsl0h+KUwzCpYxwIhALVQY8+UYmvH7MIC+GHI/7NOPmZDJ1n2l9nrJDVPKXxz"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-reg-simplification.20180628162605_1530203457119_0.13140501947189365"},"_hasShrinkwrap":false},"0.3.1-reg-simplification.20180628172510":{"name":"@atomist/sdm","version":"0.3.1-reg-simplification.20180628172510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a32ab407369a49110338a20d1a92322c8a120c59","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-reg-simplification.20180628172510","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EDyUcueka8rIU/mjC+65jMHb0Q9elnZwamYbLXdryzB25GxT3OaX9k2aJ6atrkT2TBbnsBSE87e0+VMIFEWFsw==","shasum":"b4f523d1377d82229d1753d63b966c4e1857e5f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-reg-simplification.20180628172510.tgz","fileCount":530,"unpackedSize":1340422,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNRqgCRA9TVsSAnZWagAAz4UP/2foZ2qu34AwJa7DJ4Bj\nn2xlYy1Wu7Cez3Q+K0+D12MuDPVe/8VGZ2b+6dGq1hlUWhMV1EiLcCgRJdG/\nYaIpMo01LbbNqMfq+WkMEkzfTZEuRBmVVXIqepBcNA1waaO+CIVEOKy6h+EE\nnQz5W7CpgZ1MP3pghk80CUFY0LB1Bc70NkJ9ZUKFx9r8yDp3jhXudWxcREJ+\nEmHJduP5gyKDOj2szYp3FspktqlkMhRx2Ubv8s6D3g0j0YtuhookIBoPddJO\ncN0MslZbYIIXrhKYrabjAQxBYAnqmNDK/F5ZkQZBB4o84PQ2iMC/4KW4Q4uV\nast3qHJcoA5JjkppG1P4Mybj0HJbORW9OpajFK1pNeu/h2uEWWQtDzLGoQAl\nJyiWlCDZAaUhvScof4JX69o87rH01zAkAkgyZj9KsDvuobtnxT1kYlNA8Crx\nvpuATH6FVsObXwQCdLOacHw61WlC4CohkGAO1nCMlVtR+at6nU1jRclTFP71\nqFQXSmyeyfqtIvEWLxIrVMBMRQNP7o1cC7FDyK+G5a5J0DPmEtRIcUpvfQ8o\nkL1bHUtjRw15k/gOWtD53m3Uebc6PkUKkBwImRB3Xeq8r36Gs2/i+s+XPK6+\nrHTSGlH+RTKvKK3dIYquzz4jf5tDwC3WNZgAIs3ti35FWGKuUWcyej8Wyzho\n7a7s\r\n=cjLe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCs88KcCDxLJ0WsqKfsupOD8LeQIi4HI9YnmcJcCX24lAIgNSx/COn3osVc9hsRopH/KpTlgXK2EKRdE5gypnIU39o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-reg-simplification.20180628172510_1530206880145_0.6490324582700935"},"_hasShrinkwrap":false},"0.3.1-20180628192236":{"name":"@atomist/sdm","version":"0.3.1-20180628192236","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"^0.17.3","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e4cbd537311290739b2cd805eec0e511a6148ccc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180628192236","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SNAj2emHjqJ0uin/fxmiUZAmQQgeFDCN4AF3YjxRm40dNAaE7d19L17TXvmjtDi4fpyu82w5JLAj0cH8spBhuw==","shasum":"46a1bcee432e789b72ae5d2a64db4d5f5332c6ca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180628192236.tgz","fileCount":530,"unpackedSize":1340403,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNTZJCRA9TVsSAnZWagAA7VYQAJO92Rc/09x59F+4Xe2I\nHjoWZdlYDOCes+7Ol2PW8rXwQXUE0KyJu3X4W7sXoGYjSYdOTfRcBzMJEOmd\nlhT1JW0ogZbs1mLnWORZ6q/EwXqUXrIft9XD0cvR/24DHAbTFsE54MKv7LrH\n4OwU25fw4ZerGHGWT9O5mG9XVXC1Bzy/T6kUKKFaSMOArYVNB1GXh/gD9l2m\nzAdCtQ913QigtsZy1LH1Gch6eSBU/pd9wCdYSNBZl547SgKPYANAuAWdhR0N\nYzdK2Z1HSeBADZYifc+IeHJSIocNntW0/xHQbRHZuGU9NOrSzHvZ9hSBr+QU\nF1NTuwjEC9wfXoNzqAsDjgVtEdrCX0LEMnrWjKJcC/PwPFcadAen/HjQC+IY\nEXpYvqahlyAFbjqAQCXxYkjSHWlu8I+xxAR9mc/MFf7c6bWElKd053qSvXGZ\ng5NscqQUEViRFVvvhLXSTq8SwKPCzdaht8efYrb8YdkQoE1HTEzJE/Yc1e+A\njB/qtKKQZ9Et/srgvp3saO6fm0w/vMPYqC2/DP8YvjFtA7qmG0JfYLWECfAA\nWmYMOFgGyr10mSVuy4zYpMSLnLYaOpZ93y/MjXnHrTO/0ye2xay8YpIYHHNM\nI9F6Nrjmyyzm6u+2gCPBoDykijjT4EVb+w4lYDTAec0Zuamz0GRnBlbvKAdj\neL9v\r\n=EcwP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCG7nZ+l30cz8AKp8HEmsqn+3Hq7zufe2tEt8lbRP5g7gIhAKdXv6vKW3aW0EgNu7AKuBXa/6w5qgGGOA2AxvQpEDk5"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180628192236_1530213961493_0.09440638322206696"},"_hasShrinkwrap":false},"0.3.1-reg-simplification.20180628200224":{"name":"@atomist/sdm","version":"0.3.1-reg-simplification.20180628200224","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b4fd8ddcf7611b6eb294120fe71fa9a8f28517d4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-reg-simplification.20180628200224","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HW9c6fhK39kiPonNd+eLMHAzDDjGAp3OQ97Xl5VQoD4sZ9WmYF+DYGdp8HOb+nQACVOkWlRl9shZhFc9SBnCMg==","shasum":"11c3dca53fff6090a603e1047af96ac0d75c8ae2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-reg-simplification.20180628200224.tgz","fileCount":542,"unpackedSize":1347252,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNT+NCRA9TVsSAnZWagAAxIYP+wah7JUqNh6G8B0e6PUT\nkgpFlM2tH7RaZPhEH4+bMNcBbHbmF861xxJJppWxpA9g+YYU3pzKIYavGoGc\nr2/+d+JKZGwpmQfb5O0C5tef4w33fnoNrvl5pUPdzb9vLMLwdBmTLkwFHOb8\nKk1rv97hV+z2cFQrzHJatjTFBBSgPPW1MVv/5X8HOMpbgM+AVlAyiSDA8BtF\nyU7IiCVhhs2PPAG40d6h9o7eiBObb+gej/ArYwbNk9fF8SpZjNq/O7o+nG96\nLfZIzRinc/wqGu4MKjojuPJZv1DkizIMkaKenvg/OKeeBSGVKYC0QsP6ykuD\njbvE1LAB6hrsLaZiF9hSnWN2YixSuXCuMrGyA10ds2s0VyygtWZzxDnOZr0V\nOHiY1UnL/iA2rNZFyH+ERmr/odThnX4mRDYVoKa9dTXkbebIFCIkQPgZnKV9\ncTYVWiI12tGQxfS7lhV8TrpmhunMhnDrDuZDa0vlGJr36+ykb0nrx4muqIcY\n9b39lpX00S+p9tAPJ4smiXOewq2fAENm/qqLXKxzANGD/gZzy4offmopGZI0\nn8fb8PmYOxKxhSnkrbx2UIGnyokUJuPG1UR6zMB6SJ4CpgBdH77GZyoKb8lx\nbBdKUb8GjNY7Hm4YaS5AVIVMz+a4Yvb0+kioKbgLhwsQMHgHybEAcs8s+Z3m\nP1LE\r\n=fXo3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4ifcJr/2B8u5YLRxEY17WJX+5wukGH8R0096s9q5CxgIgQYaKwima6GKSPU1aOjeTUkjDYWXYRCRLZFkGec7zuck="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-reg-simplification.20180628200224_1530216333454_0.4914203747201997"},"_hasShrinkwrap":false},"0.3.1-ingester-event-registration.20180628202635":{"name":"@atomist/sdm","version":"0.3.1-ingester-event-registration.20180628202635","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"615279a03457607a41777e3ad90a13d385adb840","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-ingester-event-registration.20180628202635","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EFjjijfpez77N7Sp34giD7kURe2Mt+vnkMwcWczzwboa4d+g7aaIDiwz/X0WGfIJn3PznHo0YBtc/TCdEd6Aig==","shasum":"d305108e22936af7807e545fc4e36fd248a1c89f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-ingester-event-registration.20180628202635.tgz","fileCount":542,"unpackedSize":1347410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNUUyCRA9TVsSAnZWagAA380P/2qYal4dn/rJ0y48/wBm\nw8FyTYJyxRTYc2WhRcJ1y9MQl2dsEkyOcnjG9GD/aFl1+OSVMaxIDr3LL2CV\nmdk/IZxeHZUPhDmWTmcKa03grdGTJGiaxn6JB2ZYUDgGNmzzI7bRslAi3D1Q\noyrnHOW110RLu4uKZrokC4zsENqBhIu6PTWspWpRwGQOgKEfIt36nRoM2k6R\nQ9flmkyp4yLPR1YXp4m6J+PlNu+tOKw5cA1Yl1Dco2aHkjUgKvlnLNdYHSmA\nG11HFMN9f6IMy1IltA8t3o2evGUU1eYaNA8qsA3j630Acw3bgxY0oJ9J7MqO\n8FGdrZjCmgufCqwkzniXzXmwRw4H5FhfUDEIKTe6xRDlLiC9CxDTgBuOdgHO\nA5K3v+up/RE1+6jd7mMBoTLJhecSu6hkqMl1CbnxmrKGJY4+RKLKORKosHgj\n6uDkaN/TGIcPDVYyoGtwJxsYX/DGNCmwQGA6eIyvqrLreGFSpmsE29MVF8z9\nohFbS0yOg5TvWEBQjZkuzUQzNsYTwq3SFbtODolctneODpkmvoLlTDWiVldd\nuX1mXizl8ZfZqh87ZKtfzNhY1jL+i1WOKFXgkPl06Wivhvl4Rc09L3hPPEB5\nZy5bH12RCKfhSX/0VvsSSwBIZ00AHZOdOmdFgbRg8UYPOwhON9uLfPAqM0rL\nnpO1\r\n=psqc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVI/pMrOk0BsX9fxNuMMaSItyrHEv6TUGzDzBoMybWDwIgdxv0KtKQ5f9xwP0ScjK8i5/Rk1rRVLnFzsYSj46LdLY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-ingester-event-registration.20180628202635_1530217778612_0.92025327179818"},"_hasShrinkwrap":false},"0.3.1-20180628203626":{"name":"@atomist/sdm","version":"0.3.1-20180628203626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e589a24d79fc22b1a0cfe3547b3126f8bb34cab2","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180628203626","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vFDtPmWf+brP4cCIMk2fYjnbfh0AJ+r7XVTbe+kX7HTRf06CMdII+ZVHP1ruFhLR87AsP+3thaeaeknlAR9iJg==","shasum":"9428ec73e8f9b227c4891b8b92e90aa1d5aab215","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180628203626.tgz","fileCount":542,"unpackedSize":1347382,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNUeQCRA9TVsSAnZWagAA/CIP/35dc6tP9yYczEmv782x\nVT8rlEXWEDNPBPZjk7vFxYje2/dVaEMrRWMloO+xu19yOi6ZWOgr8tkiOavg\nssfxw5RPlVHT07g1t51x2scKLcQBo5EN3Hu21bKc4Xde1C5+nt6gXYZG14Za\neqGCJsSFWuO4v3f57basMp3CCbs++vlrLnCn1KcJzv1sfhGIBALitvJbb60x\nyKQPZWremHLPD0rvk9gmSw0PDpKALo8oMJjFhhZ1zBjiXIxJ5vm7wdHuAUfP\n1LGOeSo69W1hmLvQ0pgPq3EE1OonwMxFxYFM613bX124BrS+hHpuliv67/Ws\naeeDZlYbWr6WIAeVjs3UcHoj/E6LwG36ThQTuTKgSLlTIa/A1j0XYUbqnqik\nbEiVwYNtthjSrEoNhe4IcGxGO72LOBj8K/ihAPvTse+c2yUQfauWrwALOI/D\n0LXTkkkJIAy/bl+aBXcFZpBTKUuG7Z7sfsbWEKHP9EIsan1bLzWB/k89wA1A\nF19JJPej0bRUg/Sp3FeRA+ItiSYqczFBR3a+OUxQH10X9hMDhIwbVTdHLnJW\nN1BT3PxlKiOO0rpGcSim05ttvvbaSfJIEDCHbI2qzAOQMlfpuySLjkilK7oV\nK8txxsXqzG7FJt1eSuycyJDfQ4seLYlrGH5GwJ3hykMgYDSBLQJhPFFEoB2x\nYZac\r\n=oGXn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZ+qhnTjTl8hqazhcdV6hDUqVmhYIqFo0xCgyMaIfB1QIhAIMGHJCbV3I3L+L0bJQgKmbI+pwZy13HSSmisGNz+U7B"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180628203626_1530218384621_0.18589503230566384"},"_hasShrinkwrap":false},"0.3.1-20180628221505":{"name":"@atomist/sdm","version":"0.3.1-20180628221505","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f24dace043bb1bea998d7df1499a05d06ba94a11","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180628221505","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gwSaEs2542YfvKclr6IqImIXs/z40tSwqXQK1Lj3npKIMYAaPpf6DVSIMDx3DefRzqGDQf6er1cgQ2NSX0tDsw==","shasum":"591e2dc78638d72d8c2b68549dd458bed4bd8e3e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180628221505.tgz","fileCount":545,"unpackedSize":1349694,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNV6qCRA9TVsSAnZWagAAwrQQAJ6ChvMmpEW1ti4YSY5P\n1es9whViNe/mY9Lz75WamN9kxQuXVxS1srgY9baZZXGaKrK0HpaiY9LnHDYd\nGSLaX628BPy/TSpUxJdNll7UHj6iZ7bEJ+zcle7pRT3rLkawnCtrGWXNJmEs\nSVR52ZYcoWMwQ6ZqpIy47YnfroOnuaE3CH+tvH1mTQ3WDAA7uEAmztAy0++v\nUcp4lKgtcoMNZoLsdp51nHDo5hBit6SzdNVEBhZB57321JpB/z4ze6JB0Cou\n2byW5xNj9LXvNSraMf+RcfybIkaTBKT1pBLPqC0PPTMDbmLNBu5smQa8SG8G\n2oRHvnKUGGVnXjpz2oKqqcKdIhGa56w8VciZcpHJBCF9s6rMEEAF7hYOGbl+\nPfYmDGsurDLSMZa3IpvfpgLy3wpTQwiHYrs15bwD78N29xgqSWENKv3vciCQ\nZiCbWS3BTxbqfkn64Yb7djYHpxR99HE4MsOg1FOWpoHClRr/3AZXjBlI0zWl\nmnuvS0UjaYOjbs5dcBKKdQgiipzwdmxJBcNmbiscwXnLhmQCEbEUJPNd5pnc\nIYk1Yf7qGMgNb841aivePUzWsGalWmee2ZF35TrxBkAV1UMFEZH21y32iyvX\nw1pOT0KahMGXY8tIASobEftghGKFyPjWgrP8i3YefaPy0GGNkPW6o1VPoe2z\nGpI/\r\n=Dm1y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDWaPfOJv3MXZKgNKQP1TDj1WcKk7Zsyyzyiq75dn5l4AiBwxurBLHTJjewdpX6awDYHyYm4Gc4p90ibRmk4pRuMbA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180628221505_1530224298318_0.15931440321079116"},"_hasShrinkwrap":false},"0.3.1-20180629045944":{"name":"@atomist/sdm","version":"0.3.1-20180629045944","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"72327cc2d2af2dc8305d040812251b295189407f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629045944","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PKWhxQgIDwSBN3o0tA6+p/qqmXnuxNKBmzxusjjGwZ3HD9nXoNZwgcGFvhxXz2yMtlgY+gpm83Jt8gfkBm+GQA==","shasum":"da5cc8f27bca00200c462b42d109bcc9f9c58835","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629045944.tgz","fileCount":545,"unpackedSize":1349718,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNb1zCRA9TVsSAnZWagAAzqEP/2JtvgY3rP0pEHM/+6ff\nN1FBOEUmY/6okG+rQHI6GuxBvnhE1aPoN/PsitUa3LL5FEhZVOKvgER59wTa\ngMFhxe7o3hORdK2BQf9afFNI05Ntns0wCrzrWy5vnQ1lxmqVnDVIX7Uv5m3V\nYgK5hydQh4Gn3hkIDQNlG6uSrIlaJmOs9KBSsochw1rxa3Bc657ZVFlr1gLs\nOkIs02hmdhZ6GDjB53cuWn1ekTuylfLrqf6/DmxuIVVM003zgem2DnUjJsKm\nN6fudKpw/fVeMPJoCYf/ovphvEM9hq0PbZjqAk4zhVbm+i0MnVvoxnw8OUs0\nSg1RpINkGlPkR6GNWSMqTFulpJrY8KQSfylUh1LT0/q7Fo2WHzaXYNrjkfk5\nRP3fCreBRjeGPxWHbe6sq72dYurX0too68UjLf3499WkhgatYa79zHf439vE\nYYknA2tGJ1pBPAHFwG9mCSLVVrj/RXLkG5RHOZjsy75Lw5tP0GuOnoh/i7YD\nsD2Kcwn+vLTz7aDTRih+y1/QZTzSkZQo0f1I2pYnHRj61c5mi9y8qWMRzlzZ\nk9tzja1BgD9hy+ORmtn++MYJiMlplRIljdclpzPBjhxQj4LqzPWkkkf9Oxxu\n+E2Q0EFlpsqTWwnoD//AXBcWoXzwinjnXccZZXKak1WaHz4pafWxU85enZWC\n8fHq\r\n=OVCn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjvIu7L63bQlUck476xIXllG0HA0Z37Nk+SMOc+mt49wIgH8UdvXWgNKLpx/CxRebsG6v9vQ5ZPO4UhemKzeShsrI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629045944_1530248563678_0.5792624463050013"},"_hasShrinkwrap":false},"0.3.1-20180629115205":{"name":"@atomist/sdm","version":"0.3.1-20180629115205","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"344c4537710f5fcede9d9c7108ebc928689ecf23","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629115205","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lwOsnZoyJB0yTDb1jzyU7NPIq+sfT0bt540mWzrlnRGsqYZiPD+qp6VK75SevNeMaCgQrrAsXRWluVtvOJFaEg==","shasum":"a74f632c0cf3ad7abd51500d969a589e38752dbe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629115205.tgz","fileCount":545,"unpackedSize":1349916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNh4NCRA9TVsSAnZWagAAOl0QAIbiXgP3vpTi3VOo840Y\nUpq3XikCsXod9lF+AoU+SR3N/FHZpBz6JtviItO5bcmM6A86bl3LxXzMreNs\nxGnmrntGpXyMYvJKsVU6ljB29e6pGNp2CkJSkWktGm0IKztIz/QoNU00GiPw\nzF4x4hQs7PwzxdEaBSqV9qASXhXpJyWiK4qnnrYo9Ew6n4/ndAqgx1KoYmrp\neDzpeBhd+xs4ThdYjlx8Y6YEttc4FzjDRk257ZqZh+8S+DxRxBNVQy3sDurb\nFWzNyT3x9J75QpAJUlPAKM9PuyeaTJvIyX8HnsBs9Z6LIzvqQBRwskIx0lCf\nkoKv2Og5CKnrxe6QfuSHQZjJOqvbbxl9CC/1dS9ixdp+oFeqFuepkZK/vPDc\n1cMZ7cSBtgrQzjMcWrhUy+H2waoaSp6oc0gU1+v5H0cO0OqGKiM1vcwg7oDr\nmxPJwWmBx1xpqFDyR9aNDYjB6IEwCD8cLPtYOY20pXdKyTVGZauGD/NKudb2\nAyEfEAiP8Cx36i1NYxwzIjaawafTVekUW9cth2ME8jAMT3nCP916qGUiw3o3\nV2m8t5g7xWwTjHkMv1EqmkYEfzbF8BEgWbxz1gD6C0NrvQM/jqHE6KzQXmI0\nzEH2vxW1MgmftqBxo06gDtPZazTHouQKVkTW1ZX7hfwaGwSqgFm4JTKRkwds\naqo1\r\n=Y9s1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICP7w4OPtZ6R+8BeZN5kVzQfHAnkoClD9G4BOo6q4CXbAiEA4zmacWI67R30fWDxcqclhcjSRtbsGeBZqn/AlOmvCHw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629115205_1530273293304_0.5880433876023101"},"_hasShrinkwrap":false},"0.3.1-20180629125403":{"name":"@atomist/sdm","version":"0.3.1-20180629125403","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7e5a38837df4488ce5a45aa00775ed524b936ac8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629125403","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ETgtWvSGnfoY0gXDpp4rtVFvhcY0X9JwxK2SPxlMSQ0m6Hq0DC3LcbbXOmDrW3cqsByj590b9nph8PlL1QMYmg==","shasum":"91753df3a6164a242055e5aeb3f1a3f244bc1324","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629125403.tgz","fileCount":545,"unpackedSize":1349933,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNiyNCRA9TVsSAnZWagAAx/AP/jtb1zW2FwarxVH6zMWr\nrPSCJBtTaIzGB25l9y1RVjUOjYnMRY5dTJ844LYiNc2ApqNjsMZzTAoT/iUa\nTliIYHCBvbUtRodbiukvLkSZB8+damXAG15Mxx71iSy2nGHhB+2WHiRCxbdg\n4Yv77zRIgCOsOjydKu9/AZodADdaptN/V9FdoGsVnspq2U7LTNVRrcQVQlso\nnv78QCf8OMdE0+X2dAE6qpNfbamrW4VSLgJRFAUIFLebQSzuZplqufeiSMVv\nGlsIfhuZKdwbNMaCePgY7jgSZ8cz5zhzL+L9hdlxITOp4egnOsCto6lkdSmI\naBwbdGMFWDGY0M6vj3rFkmVDBekQ0VOJGP7rQojTzF8ogsxJr+pLDPywQ7rM\n8D2Wp1r0+HI8gwdfjvmh4hpSkCgGw5yww8eeRj2QjWrbjCEPBxd/jxr8j1dX\nWuY1vcOVaw8lKfUuNHuJsM5u2mkqAqOwbeAGCC8mpH21H+aBb5sULBCS/XYJ\nuNnkrWl4YCzHyjFGOwd21Nnav784hXRmjHggzM8MFjFS7a0g3uQik4ya8HAc\nwUXRle+oEXGfpcwVHAevVVt1w/nsxE4q0TXQRTS2qtMg5iWoG3DNgwC50RRd\nEv1cnNa7uQvnpEe2ZbjGVlB/Et15DeYkxm33qrBNX+qqUM26M6xUwvtLbWHQ\nGep0\r\n=odlP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHAdSrXnodyn09s+HDHB6wWJe480mdWzJQhSJ1OKLKpRAiBD0dIXX2QLRh8X0Bv0jjj0xVSDGnE+JEGEHWG48eP+Dw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629125403_1530277005416_0.5414540108297095"},"_hasShrinkwrap":false},"0.3.1-20180629160827":{"name":"@atomist/sdm","version":"0.3.1-20180629160827","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9101119aed6d927f4dcdb2a1ec7c4c9c68f7eeab","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629160827","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lMfWgN+dcU+mIcRdAvnBxZ+1uJWdY1GZBdlVzjWr05KVGh5TRAs2r7eKM5QNpy5ZYYI/a+O09m79Sln8O/BHvw==","shasum":"ea402ddfeae521e84295a354b9fffba0a27199f8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629160827.tgz","fileCount":545,"unpackedSize":1350088,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNlokCRA9TVsSAnZWagAAMiAP/1KnbeNMPO2kmZz9McZH\nswAnQIAgKF9nOToLi10x5cUJegtaMz995ZZ8dadkvUnjzQTvSAoElEFdWWFn\ncbG76BAaD71ns5yGE4wcC0wewL4yl3RX6/WiAlNwe/HTTA6bo7S2FhAsd6nX\ny2HdOlCJyYvHGwH+852JNoQHJ/5yQZl0GjlfMfltv3sI9ItoaT78X8QEFzya\nChfq42Pm2Hox9UAbn6LC/UrKIobJlJs9NQFYHpxuh9B/IeshIqlSpGI1vFAq\npSN+DF1hnM3it4rwfRgqalwAMlGFFiSS6o05h9dbrwutk9NDZjQHUXm6lB3t\neCALtOUAckaE8KK/UpRCt3SoQWvf05SC3WMTLcLD29YaySTRyOWy3rJQ7akX\n+lOgVtrONkkFJyz+0ndkEym2EpM9aBhWOZeYFEQV5bSWsgdQCnjnv/b0vHuK\n+hjBgn4Tssoo6ya6j0hPXyk+71PsKomgYAV929fu8aLhb6NRlRrU2c/yyfSf\nzwd4Sua2+dud4DOLApva+VXD1k6LaJXtRqzTUtKTnO4aLlD7IqhGEvZChaoV\nJYH2rQ8Ml2URtRlyAIxQmBQvd1/fuyxk0uVIsxJGQgctDVrGua6bUpab+aBc\nqdtNur3Aa9MRH1iBZP3F8ofB1A5UZIj2fASCrEsfNihEfopA0lrFEfanQbcX\nYd3T\r\n=LuOq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD3RtLJWDwwYSxmb5BzSHv0wWAB39qCZRavsMKrozdXAAIgGmwBJBf7eT5W8ZiYJ7gxl5oMnl7ZIyf2Gyuc0et3FKE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629160827_1530288676297_0.19323313354077487"},"_hasShrinkwrap":false},"0.3.1-params-builder.20180629200133":{"name":"@atomist/sdm","version":"0.3.1-params-builder.20180629200133","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3c9885e0304ffad3074e90f34a060e1c9ea6d0b0","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-params-builder.20180629200133","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OGp5ewhxJuzI+nRqa13nLocsO5O7U4lsuMvSPfXiWpfvn79TZYGkhK+gNQhgoMpwdj8wknz5rI4cGGJjTxQz5w==","shasum":"8556e86f7973cfbdd5a03f640540d62516a5d67f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-params-builder.20180629200133.tgz","fileCount":545,"unpackedSize":1356680,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNpDbCRA9TVsSAnZWagAAyaUQAIy62KMlQ3K4/oL9nYJR\npwjd4RNeFK3dKxW7vg60fOrfHsyaWfRNUSb79IKTiX8QuK9DEycYG4zjucS2\ne8B/D9CLuHAMvdu+8CFIgLn7ADCg5zSFBlb3e5J/sWahTXGZrlsz2q1PxIbr\ngWM88VhD6I9KSch2vK0buydLm/I+djgCG5oheifIVoVSpwxEOwwBZ7K4/wrh\nC8ZnUR4r/9AqZn7zMFjdQ//uikgYkbT44yMzw3lJVQqrkmwbpE7hTReRKz5X\nt1PiTXJZLrFlVHl4Ku0pBjS/o6+kvq1fZlkD1ZWiRZh/HgfZgABaRqr3Zlv2\n6vFTZ55CAOIOdllqIfgpnVHj6rwhiKu/cPZnSsPCyRUgeK0QHN5RPea8ifAD\nOSAjTXTwQLXzNzOMnJ/CUC/tZ2fo6dgb+a9iSKUWn7fIs9gvNDj4fMEK5ftB\nPeac66iLSqENQdpnfxZn3snwnLXRGZUrs2eLsdl1dXZX0MFvJ4jQP5tjEj9f\nQTlGV5JJkTtZRc5AwhCQ/5fWMFjvSoLuwqroXbH1HPvsac+csXsKiLCYWroQ\n2g7udIgu1Flcrqgq9/1+l02WAUP4//mvqmxxShZRYYyP5XTEH25+ulugckec\nGn+YJ5vDTlMg3u5c1D3kyMDlb+Enmkw3JbtDIwOOkO2K0u/3/SkPWG4eU4Iy\n0TXL\r\n=54HA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEJ4n5siR+WkXYhMSWxiaKNY4FyNp9Bq/uAFJfXUhbwgAiAio+NateTL8+NNtVQ8wmGTxL0T8gklKtb7+AYpnjjXpg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-params-builder.20180629200133_1530302683288_0.1567193731877108"},"_hasShrinkwrap":false},"0.3.1-20180629201053":{"name":"@atomist/sdm","version":"0.3.1-20180629201053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"62a27761066adbf7825e40529a599438971a7f30","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629201053","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ujh+zmbo2Gx1ET8R7p+c3SRIYQiZ3T7x6wY7013eqYMsM2LnA0TAqOFHZYb+cmOgKq5fggx3cIf3chKP2wrW/A==","shasum":"610fbcc43dac46ab58c208414a98e6e64d387f9c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629201053.tgz","fileCount":545,"unpackedSize":1356665,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNpMICRA9TVsSAnZWagAAw8IP/A8hV3nfehAgDKAVC67s\nqGvx9GBo+3gChRlDvRimRveCtrEUNSbY3yKt7aSnFHxei5U2TYHCyQaIcMWx\n2V7nw1UnZbVugx5qoYTYOI4wSyBNE4W+Gl0qrfGRDuhWb/bjh2CwQv6ZqAEq\n2nFXA5RhxZXvxTQAaiX8fcwDh+R9O9qoSxtk4sHKB/RdgakInrtjPzzhcL6J\ngM6nb0ndaM7rFi5PAJXJ9gl8T2JkX5KstFd2k2efkkY0Aka1t30FJZBSgIeK\nqYZUhVYjeHI8T9Rwuwp6ZzlbgqbQKPfFtdmrcpVU6ZD1fQlxKJP+LtDqqEWB\nGOjSLMf70H3/sqSvZ+vUKgTGBSd9Zm5dLNZuGAvKJc/knyHQ14nbkiW+etc6\nejJvzdYqHDQOgQcO+nz3U1kzeHhqlO4yXS0Mjx5jkT6F2qddKUIX8cPcRsI6\ntu/WWkDjXZ3KR+Ryi/heEn1kg8nIIRD1G40zSABrXPak22vXIgLBZuy0O+mU\ngguqA9yQoKrnoUZCP2AVC18MALXsRNKLN7fr9Tuplb6DrNHTOImFZaqgSpua\nYgGcZmgxr5lHjwr8RmNEwTq6LjdRcEZixzBnAk4bCZ+xqDqgvx7VkSoC9xF7\nRHg61Rob2j8XIDK/iePn5Z7IHDl1MCCmMKD5j9XFFt1szVW0KQjDSe6BJ901\nbdzn\r\n=YbCJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHkvzzt/9IfvOXZxA/DSm1l5uxv9B3Lvo2CBupiG3ruAIgEUSR3ig2s5MBTS69QVOVBiZ1j1KXL8DdUl+8s12DIic="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629201053_1530303239889_0.42998252699577844"},"_hasShrinkwrap":false},"0.3.1-20180629203710":{"name":"@atomist/sdm","version":"0.3.1-20180629203710","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e8fbd02227a5e72756cb6a7fcef0197c65208f06","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629203710","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KU1K3K0TOk/EDwqPgkq1gimGVGstOJ24SJjlVxwV1OEJ/4v/LHWB7BIpYSzkxImXAbHaUtMNjM7Gzauk26IUHg==","shasum":"10365f6676f23661542015c77a125fdd29c30e4c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629203710.tgz","fileCount":548,"unpackedSize":1357744,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNpkxCRA9TVsSAnZWagAAArwP/1isGvhnolrdoCWKYQbz\n9IGLLi07t3Qb9IRsyBeVrSCQSq5ZaPtAQ5V3rjlf7mX6t8aldb3GaXQgKvdz\n5JZLASuqGt136nZu03x0A/vpya1fLkoXJt0oNFeqGFCJbNvDmlSD3WJrWt1X\nfrDgXz+ETDvF/aD03B4HTCwEUVf3KMCN0bGvzbRrVvPSmfdyyAbaPE5wKN/e\njXyrqgTQS8FYV86NgSdQgyD3yUewwhjjVuOmLi9Q0g/0p0NhxFYLnLo0c4wZ\nosMES0CjzO+/VpFP+SY1++CZ58YAddNbE6tPSKsFWCEnm1PkCURdIdLFBF9n\nCkRMnCXlExWgFN1Vcr3tVv+rDFFGpIVMgl6RGYJ1BES9nnq6I1GoP44CkqIU\nN4B72CR6WtU0v+EYfC/fOSGTcU7/i+DOagZsiW4fwuQTw6y7/lUqLdnJkJrW\n5wqKxEh3Gpu9ntLO+N4hXG8o8kUymh5As48zOX+pN2niHCAdrP/l7o6JbUsY\n5rpLuFR6LuI21XRCJQ/fDukFcX399Yxh5JmkOb436JqH74AImGbuJknoehga\nh0/0niVRisI/YwT9jtOMXl+kUGfXvoz+GDSy8umAUUbc8rvMpJ31rJd8chMV\ncCjP7/I1ZInHzGB/KxGWPKdCxSaklYKp5YeTQ05aWU/nYbN0Qg/ZjXHJaj6i\nPTKZ\r\n=erMw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA70u36Sg8inXdQcP2ei2Ohy46rHdS7yw0lpvPoTi1huAiEAtXwONX1PRYGVGub9Y/PC0nEyGj7k37VskEEIKTyVoek="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629203710_1530304817319_0.8742216533377698"},"_hasShrinkwrap":false},"0.3.1-20180629204030":{"name":"@atomist/sdm","version":"0.3.1-20180629204030","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"21418b22f2e562122966458de5a24901d599eca6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629204030","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FLigRiv49DTom0FyL4kVb99L2pWhtyYdQwh6l1BBpHlTq7h6fDdAgoDgd5iYqbe9ah/7eCkt0mutVF6G51/QGw==","shasum":"c40b7dd4a691d89579adc138814e59a04ab966e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629204030.tgz","fileCount":548,"unpackedSize":1357744,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNpnwCRA9TVsSAnZWagAArj8P/1cmCJE51nFPrIpkZkrU\nSu3w5+lU4bPP3CFYHp5vjHcem+cSwpToA4Dj7b68W7ecQpXuAj+eob65Q+3X\nUpWOAN8Ki9HVoZztlV6TroqO1t8JhHbSwVk9M48hrCbFS4ZKHvuh0E1cCmYW\nl6yZfBQYgTYbwdTRkzI9LW22hvU+Vy+UmkOc8eiXv0kapA8nc0M3KkZhvOuC\nBnXPN8pHpmd34prfP9ayTzPgwYt4uCWVtCJKb3esrU82YgHKpr4Uilu7tzLG\nSlU+4P7r3e0P01ByE9mh9jrVc0DzaOHg+M3p6dEc6eCYIo1RIVdag91+JZ7o\n31MhyvSoy3e7IUp57diVIwlAUYBqpyUPR8yAzUsqVs2+Gq03u+mf+Gxwq7Vy\nHbsL1Tc5OAuuO7ZebJUobKTdQIhhAgSAPPFUY2lRTCKqIxINl+i55s+R5wNr\nrL0pklxupG81lpbtGYzaHvLdCgNaF0qFDLgemc79r3zjzVM7XIkVgH/nf/xk\nE1zp5jyAU6ezV/YJGwjZDFF9DYucwSD2Oi/nX+bWahJ+H2uU6eRSL/IUXXMq\nByaoSTOOGL36K2F83JK5TSQGCzXjnUnGn5MY0lASSqPys+xxAccx1zokr6ML\ne1VVTfPsxQOJetXkf85MugXaaHHahVR8exf/TrVFkB0WB078nil/D9MjnJLM\nGrMt\r\n=lGaS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEMCH2dAuN8XmB7CZuoKy/yaQ3BHEGzfZx3h3BpuPMNpltkCIBRNAvGKOUgDwMCrYkCI7k5StIeAT07R2ihGfUqiHcCM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629204030_1530305008204_0.013809346697127634"},"_hasShrinkwrap":false},"0.3.1-20180629211402":{"name":"@atomist/sdm","version":"0.3.1-20180629211402","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ed4a8630dea7a656aca63c452aebbc8d069f217","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629211402","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ObZ9BSc1xn9HWRhakHYwXOmbA3xX+Y9EfCXv7FsezWs8GO5ed90J6Od7O1VhQuRXrjdmz1SauFY9MifLRExwMw==","shasum":"cb7c710da97f4ece8ab9c070987679798149a15b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629211402.tgz","fileCount":551,"unpackedSize":1359379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNqHHCRA9TVsSAnZWagAAHGwP/R7NcY0WfTaOWt4Pc9bb\nKHKTNyq6nF+eGt29rsOMnHYIUnA0AbX7vLKQZyWoMnNf7eNxsl2peLVPsdD3\nZ4YqmENg/qaOS0N5/kM8dQ0vnEESX70Ux7gkOQEqaygqub7MBMt8LGKd/bgM\nY3l+1t2FiUu4g4pPzOvCmmE43QpZaiCF5tgyEORHt1rrm/EJ8GmWptP54o9Z\nJsI1PcP+zJj0yeL2S0nCbV2JC+guLUOwxyG24Wa2K+0p++3F94Hvl9GJELFT\nCE3Eo+Fs6GOHpP4VjeSCznBJ/rAdvSdJ52K7Iu9Jg3CBF0MixI7c35r613Ju\n+P/dbrd8LBSbvaeVCyiEmhaqmmRyBCXHcZH+JuEAn7r9csBAFOraWPBjStZO\nZDThhM+yOeiGzeeU4nil+mfrv1O3xdIV+t+nV20DGfAPWjqdegzkn99op6or\nXDCcZgCdUATXDn6G6SJPhfsFm3DpIqyUtaD7Z5Lk+keHcfRZVB9kelK1HlmK\nK/7p4kcJ5ZY7/6wVGyWaUmenLQ3zI46ERNCRWArGLblTE/rp1lW/DVFqyY8f\n5mtCJ3nPppoLaEnbsEfIKZchJjIf7gLABQ5cePih3UUcNQK9dVlz9l55Ntkk\nYbJHJo9GWD3U0BQ/lrHNpC3UyYi8ntYZUITQpisMhqF9fMthMFOV9Jaf6LM0\noeei\r\n=btrG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4MHF71zvlQlvVnATKesDm2X7XuZ1rQ/Yvlg/1+Ptx4AIhAO6LkZJIP9b7W6KiIlgeyzTsPhSKKTqslU6YKI7NIjUn"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629211402_1530307015117_0.7771552459784705"},"_hasShrinkwrap":false},"0.3.1-20180629214333":{"name":"@atomist/sdm","version":"0.3.1-20180629214333","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"973a961885c8328c57f2d93e3a8a542d82e0b307","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629214333","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lnbj4OSwV3kDPwsoxQSNHOkH1MxsYMCR32laos/PqBPQnY++sKzjBEB9lc/ABqTlvhLOnq24H24C4PhaINbd1g==","shasum":"5116816c5b61499632ab1eef1390f849de9794e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629214333.tgz","fileCount":551,"unpackedSize":1362644,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNqirCRA9TVsSAnZWagAAbEIQAJ77P+nyAWrACUHggNdh\nEq9KeZ+BxkVpv5npwrmdvpM22kvoFvLF//bgd0/g4jkWfvyAYZAY5jD3PP84\nSLnGtoJJfKf8kGDM9nXhT+1emeYyPm0f2MsVaIraK6jP+rMm5yp1IZhgCGXn\nYSth8Wa0rd6Q61c3XINgUYe+dpEm55yhXnrNaXI/VW+sQ4cU99qoOj9/71Dl\n7cj0EGQMSxcuC0hNasqhxeF+vKwiZbUORnrNZp98hk4Br/bJTjh8G7yLLYmM\nqbsFqU/qfa0R7dGoRaa4jFwiABXVoIC1pETL9Xl9FjkWCuQCUyglehJdQNXh\n/UiAwc6EffdoJ83vUJaOuM+fVERqXH1c++3Uv9669zvV8+GAqjRDa0cMW/aL\nP/lrP60pELxeE1wonPp8p8MBh5s9+Mb1x9A0OatCqZPvK+EGymH0G4axBlqM\nIqWt3OgNLIIfrOd6/sKoH4o/1QhyjObsUH0ixzfNY7I/cvLFVbon2QGdLkdV\not3gBDhGQp4cWiDnxkPKKmGSZhRm18j6T0cH2J984HB/2qnpKqhqjDgOSqop\nofs48FwJhabPvsAOKAt1euD52RJRIz/cxmu4HiCitB/xeAatlqM1kFAPhFMO\nbWsm3WGZPlmM6Rvth4AL/OLED3eQepWKU3jn4mwxW1mAfANkpNLtSsxbM8Kt\nlzKu\r\n=tng0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgPAs57Qbl5WZNykHkjD3dMvUEJg5rf3HyQx1eZDx02gIgM0MLCcOgelpcqHwuSrUNk2HEsEeEjxLdYekAzxdUXrM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629214333_1530308778911_0.46343208962539095"},"_hasShrinkwrap":false},"0.3.1-20180629220406":{"name":"@atomist/sdm","version":"0.3.1-20180629220406","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"be6924f00c64aba03068bc996297436162c1c61d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180629220406","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nJnpl5mvu8ibKBtCVgz/HW2a/TxdKR0qB4Srv6x/vBNrBmmYuJCT3dwcs1koogYB2Hp6yInJ1uiqyIesfYjgqQ==","shasum":"80f2a8abd668a9159b09843b1c92f88edb54c968","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180629220406.tgz","fileCount":551,"unpackedSize":1362776,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNq2FCRA9TVsSAnZWagAAbQYP/2BwWzbqHIxdnx1zlkDk\nRT+G06JpC9eB4wzWSdTun7MAvxFCfaeGy/xOFokqoC5rcqwwd5vN3KRQcoFG\nxS7b30Dy/TAxhJgPV5lp2rS4+3QQIzIDpxi/pJ2rrD0Yoc/wdjzDnyqk3p5T\nWU8ethxylXgNu5LjlnjSlcXejCgbugl8qPGnkKa8xwDGrPQIdDz3O80JMVvo\neIdHYs4dRujGrOOX8Oc+wcs1ofmwmMg2hXd99X8BSpAWlp4F814wgIWhLlqT\ngAWd+ifmEjnmqYx4QnniJCrwzB7B05B4OntppIviU0SJN+k1Ihekr1n2T9aa\nP8PSleh08JvTw9NORLZ8A6ix8nNVIa/YJaKrcrNu1INntsfwIjV58Q12OCx9\nUYyD4BbMAJZmyQcCSvbFru9LsbgYU2wjUb77H0GDS2Caa0qnyKTEEwOFfav3\nW6gXwUEzE4UxGObHLe997v/2GhtVU/e7LMhdHSoWebGiDCCDyURWB9de8KlP\nIPmpQhTUZ1Tv/rl8LooANJfDd2bd6U7ctV9OxM8vuRfoIhAOT4o9rpdV9J3p\n2m0dTYRzfgq/1nCjYhTLqfWrhO0685CissObCjeoOT/WPl2KwMsmToTQU9kz\nG8yea7b8svIX60C5I8mMtt9y+OxUc6qELdj+TI6DI2OTHTgzN+7wkuf71PCD\naM1i\r\n=P8Cz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCNX39n6PHJUgGlkIdnweZaGJzA+LIVK99r9FuYIBEVQIgaRZzap0xNz0SVi0b/K/Dr2ayvvzVKpOS00VQ/8b8rw0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180629220406_1530310021861_0.8165930809510753"},"_hasShrinkwrap":false},"0.3.1-20180630015315":{"name":"@atomist/sdm","version":"0.3.1-20180630015315","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1b7d0a258755f88d3e0f9a86981936c6cc2d25fb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180630015315","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rtFuV0sd7yClSZS5JfA08fKZ+nhHHoe8acJD9x/qFesChzQ7PbzUZNDrfQyNYJva9KehNdZI1YfAJ0mBTYvdYA==","shasum":"1eacb87a0dfd99ce67c22413cce1dd2771042321","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180630015315.tgz","fileCount":551,"unpackedSize":1362776,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbNuM5CRA9TVsSAnZWagAAV1IQAI9PygnY/EqemoOhBo2R\nw0HH8lVH9MBcLLGeEi9vI3BZTgSqvkFL8pXu2jHScaUFktCV5rLzn4sa5gXU\n/Kg1+YkrpDXp12dkVnK8o7nbuTvMkXunTWQcxT7PJnTM9XLRjWW8WUQRV1I+\n3r+/qVG1DQgpK+BpB7PsEvYFLemciVsLmdAN5RkricBs76KGrXlQtjAyVBVi\nQQOYQ2km6nCm3ya3K/WKxqLOueo7OzSd1ZoTLipbFOlEqe5j2qOTWSK89HtX\ndHWUXBX4tKp1Ee2Mby3yCt4I2k92KxHn46Mp+FV8kLtKCOPuT/prMtKEPc+c\njieFtVIua1GIErpPUSjp6mlU6pvLJfYfwouHbNofzFxnLlk11NguLTfhVhkq\nktE7lOWlq6veVuKEcirSQI0YrAAacZKcFlgEYrzvNndh8I/yBr8rfgdIqL56\nctTHaYh2x35AIkx2LtVlKvIYB2IRct+UKLyxNirhhP0T5f3T6pbhmrc0/JOM\ni9+o0VsRalJPQP+UygwccAnroyE54QgVJ2ttWdmUOawJr0ICdUgfSlubbQeY\nXx1NE9sk80ymGVRXQMdlKcZQosKOQFzDV2xZxKkb11TgXeLvqWaDi+glGt/6\nXdzEWHkRz/g9Wdtob+iqBdUmGuFYMRZ0oUH7vgpN2qT8HVC7WYDdKsNoB1Du\nFEuS\r\n=vK+J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwpZIyHoUhsIQ1HP7lS3V17nCB0bExwaTE8+2ufi+mmwIhAK1U++lJVW42cFH9QJ5HCQaSF0ij+YShQDpDvDkZ5TJS"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180630015315_1530323769595_0.25552938604152176"},"_hasShrinkwrap":false},"0.3.1-transform.20180701205251":{"name":"@atomist/sdm","version":"0.3.1-transform.20180701205251","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a07392a809a332b9ae860bc9d7f96d36e7a7fc6b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-transform.20180701205251","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NyBbZy9r4S7c1mhXyfjLOaoxHuKljZdkpHY4o0jLeGpPODOXdaHbpWqOiLV0W8kvalKibBEzN+rQXod2xtRbCQ==","shasum":"f4df9b03af35c9a7f7f25c7d33fdaccb1f5078c6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-transform.20180701205251.tgz","fileCount":548,"unpackedSize":1355411,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOT/OCRA9TVsSAnZWagAAhBEP/jrVrFymJRNvxdjSa87Z\nIfj6GOecf1eMpVBZOQfEA22a0+vabBpD76jekgj/5uQdP3WanmvdvCtZ+NUl\nbn2ZzuynfW4EK06zyiJsfeiPuz4K8j5MSd1WRpb/8zmT8IJyjndQ1l9h4Orh\nLRfkGSujifrBuqunNtzV8nQAdaLz+HtM2bLUAi2XkUzahqMvj1ALaxlkfGX7\nQRDgmv3fuoGj8djdnSwvbbzx8wtzI3dSdzz6Ku5XRhvTBbLF4kXBO3HhHxDh\njDIitROZU7g9QrinqI2sZc6o7Q8DdtuMd4JG12RuboNS0dC6/TQfCudyCw9r\nk7nuF6JUACpIAolKB1vg2Ya2SSGJNeCwCkCsAmX9JQY3NPCe/tFl2tw8ATY/\n2xxCNwbeDwCRugcKcd/Z/dKXc92lLIEd1ChBRCjf9y4bUQQLwKXFLOnf9rdK\nsSe/bJ7vX/Ev9MnX+RiBz88hHE6vaapG6C7/PMMw1DKXfx5gIwZd3Vc1XwYx\n7/Bb5NqfD9guT+Gq0Wzh4kl3s/JgdR7xqXaF1fLvxlsfbF2eq/VdBoNELa5N\nR/sjzYmH2JYlKgNM/YGz6C7gBNQIO8pGBP91i3ielB75d+Zng+G0LxEaJIvs\niN3kma/DJsJXWYMVX49xEKxJFyYvEbj/ypvIqtU2chHxPX6ghGQzDwj11wnU\ncpvo\r\n=ijMJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFKkiCov8Re+ia3Wwn5R7QV0u+zN1JlCNxutAtj3uexgIgdUTuDF57clS2G24slNcUZhVssRIzdPkZt64syLF+KwA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-transform.20180701205251_1530478542352_0.8088308341966022"},"_hasShrinkwrap":false},"0.3.1-transform.20180701205833":{"name":"@atomist/sdm","version":"0.3.1-transform.20180701205833","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"11e6a26c19d71e30e70e0ddb83357283b63d549b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-transform.20180701205833","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1iSAYglZxHu/386c4LDIsRf0s8t3N/I0swRKlm1teOuHtC3rRGms60j0Lt9JrW2F7McMP3czbhpQrc3h0AUgWw==","shasum":"c3c10f564bc0d8de79e54a810c8efa5f0d3f995a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-transform.20180701205833.tgz","fileCount":551,"unpackedSize":1363549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOUEjCRA9TVsSAnZWagAAbdYP/iqNVMZB2ytHjgDfAsiR\nqZojq8/IL1yk2jStQCHJpMBwUYJbAAQnr909oNuK6bONiYfR/V+clBmIEvvq\nwgTKxds2xpMLJZbXj9uHZoQcVjs0AsDjakXxKvHw5nd3X/qLewaW0IWWuc48\n7DWw6XFVJ923gkqHiZ62AwZC6K1MY6Iv2iUZluhotiwmMPSujK0UAAgukdwA\ngnwTUanb1INcFkfuJ3wG9MZ2Y/6SEDcA00IkZl3fo8PkyQlkROYGFQWUaVPw\n/b4jIDemRH8br/N77NLPYEnI84T7mOBbLeHttzuicfCgQbHSlX5KaEniswpg\nriD+3A/SYz1AaKtfkMCNlvW6WGAinB0/XeGZTju1xOgGMEmTl7hwRDLk4udr\nlSgHRvPY/CAlL5IUMz7xwURAL0Riv705qVnZFsMduE51tCQ7QZAISPhYmIv0\nVmYUC+AhPyC7psVRbV9QRzcpdbVf8lzwlHQDVEocGVDHJrPX392h/UegLjvS\nw3OrezrJTPfcD3rnuwziSsNIBb8pbXh7AjefFPFWLiZWE6D2pB0KA5UW/yA2\nfRjkdSIk4LV+s7k0NkS3MG55R5dafw89+uzzFKurhEz+JkDN2OoG9rSLnfXN\nQtxtrVsPowTc5B+Kjm2ihD2dwh233RSxcIW61nGLSEJLu0wch+0Aodp/2QoW\nq+VW\r\n=smac\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHi66VDaxO/6DsR5EJH/RVRD8RGkDOCfpKB7R6v1w0VwIhAJPFpYk2VKa4nDU+yHRjBDdrnsXzguduPS5+c1nBvkqW"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-transform.20180701205833_1530478883327_0.9688732172332282"},"_hasShrinkwrap":false},"0.3.1-transform.20180701213407":{"name":"@atomist/sdm","version":"0.3.1-transform.20180701213407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"50722745e02d441500bc5bcffe56a0c0b30fb167","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-transform.20180701213407","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-th9/jZZeYqP69eHvw1fwt7IfG1p6FSivphG1YOIQDIUXiLtjXaIQGdUp88Kq1wDXJHL4/D5SqHQSj/eF2RFitA==","shasum":"7450dfcb829a5d8b66b4be4d5f46a803c5d3a638","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-transform.20180701213407.tgz","fileCount":551,"unpackedSize":1365840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOUmACRA9TVsSAnZWagAA+6kQAIMqbZHGTVMpPIxcJdUU\nLUoUjV+MBPGCqWL3WrT1KU9oEXR7kqj5K0E9kH4P3ipnrSvfJliQapS3GPcn\nBg6eZrk2AtWMQkpnDKXdMKRC/uDush/4cnKhZ0DPx7e15vBL49UT6jX8HNGJ\nr1F3M9cN16TNRCF3Bd4VbpJeehzM3rF2K3A5Npx8JixJt9LfWEq7SYnSh8mk\nlqx+UxpLwdMGI9R1HPyTOf/6hf37VexjhydjEepBiy/eraSKwlYIcRkx+701\nEQ4T+sk0FVFDk8DhocO4DmadJ+NWwB2jbr34MdcJunErYnJOfQXzhIQKvM3Y\nKGpMNpUvkTDBMgQNPGiavAiKlPbdUyC50+GKXnjoKGsLpqX+qSe2JSEuJ9hy\nURiOtRpTND20EbN0/BhMPD7yGfWRVFnO6jJqYjnv38b9jeRseWQPA1Q5TsE+\nkSBDjzj4jlCFyO1/5mziheWtZq1nl+4JJOHdaI3gI3srS4siQQTtLSq0VhRv\nzA0dCEL9bmTpEX/sTrwEjkK2ePvVyvH/X8E09RiiGWc3o+5zuN7M6aMAAisz\nxGJ0GTQO1x7suqPuXGOaCXg3TWHVERqYfRRNI4mmleCURcwyfyfAcHIVet8D\nmS34K/TE2276vmmBpOucv86TfKo3AbVU2n2Emt47rt5AYrEAtfj2AuiECEwN\nkx4i\r\n=Tkar\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDkbfCSMm3+hXfr/5DpmNNV7dfNWiZQuuATUkdNj43jIAiAIJ+VlYTMpQbj5MqAU2PesdJyP43PtHxYU/SUhymGwag=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-transform.20180701213407_1530481024909_0.7864450489760961"},"_hasShrinkwrap":false},"0.3.1-transform.20180701215152":{"name":"@atomist/sdm","version":"0.3.1-transform.20180701215152","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e632f2def322ced8a5952206b654fa2256f8776d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-transform.20180701215152","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+ei7a7soipysJk9y2wZ8c9zUcu10GW6o7EBJ+TkVa0Ub3olaHXesOUozuKm+h26HaBfy44645180UhgAzAO4rw==","shasum":"bf2cebcacb718fa010feb8bbe8fcfd3bd704ce78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-transform.20180701215152.tgz","fileCount":551,"unpackedSize":1366009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOU2+CRA9TVsSAnZWagAAqlIQAKMTMo2wE0LZZCOhcdIH\nKdt4CAZAYetoVlMzObhHctEMQHfGlpfE2sY927s8edLdUYAqX0U5bm0ryMjs\n4LD1oO0yVVi2uWhAlzUyAFVt4X9weEX2fLwpuaKA+gLXJxikSNmUEEI+CLC/\n5YGi1zI76SICj1B8a1ipWO9aHnnp05ls0lO9WUSnOsKowTCqu+vN9VvlYJLB\nuicYFFOGrtTZSMVY2mljCL0lY9yxh/R1bcfWZPHsc1054zAdRBHRyJqfBtuV\nrHanVrh7e5tvfQ47KMkMDgqKS8m4RDr9KXmZDVdAbiEzI/aUIeFhvtTFlIx3\naCTsfRP01D5XNkzgE1brVLM9H7T4PdiKyEwMF7PUH7SYVQQsfjEHlMIFkAEU\nt3/TuyCjb9OWiMMPMZdqyNIsbXPBG5MTZ/WpvYwTHdLh10lNeqzIRpToYemf\nXvWUajx7dFO8Bs685wYFP/VrzKbEUyMBgfH8YsZgE4qCDmWt2k1Oa9e08xWj\n8uyi1s8PvkyLYL+XUkYFHM0G0p/pzDILG4wWMHLnGw56BiPzgP5jqJOdcnZp\nqYaJRSwciEINjdYAx4it0mkk+R/z09NJq01tEYIidHI72ggAFfDa9+NBX/jo\nuvPmxLOx2/5q3iZYWiyOuZR2E0OjaBG/ub9vaPOdtlPMHXrwotYxvXN2OTbt\nj62W\r\n=WUot\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjRnuQTcxDK4uyzaEtELK/q/tLaQ8g6pAel4qvcatvwgIgEohU/vk7QOxoMmAAwed7A/sz4YJovUpUCo7LUwO0koo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-transform.20180701215152_1530482110492_0.03194945275225569"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702002619":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702002619","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e7103bc2af646eb50cbdfab3cce982b62998eac8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702002619","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QBCcWOcVqs5saOn75Uy4h1X4uMN75QmuLJ3Z9xFwSE/X0gPnvxpCcf63K9ObwlZ85MJ6+RhARsKuo2tE68glgg==","shasum":"b944fd74632000f72c2deb5ed52f683d1b168e59","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702002619.tgz","fileCount":551,"unpackedSize":1367854,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOXHbCRA9TVsSAnZWagAA9j8QAI79MMLoAXdfj2UUDjHg\nq6CDFmFtqWlpfH5BTqoGOqzn9vQlE+ykIKd9DL0KTuYOnjQom8TnePepfWOQ\n6vLE0bfXUYqGnJe+xQ9lVhhFH7SuOAxvGZg/KdbFBXpycSzlyVabFEVJuI3E\n7fKhPIp8/t+2n2LfbBDmBbqCKPsP44ms8mgIVZCxUDGOFwiqp4NBHzgR3Lfn\n9LKcafV6L5MY8lAczFkNPi8OOSgdxbAdpVC9ddvKjE+l4odGL+kscjrvEoIk\nBKDycHxN56fS/cFBQCD4YxV44YHAH/TIH6sYb/Yg5Oy08UYTgIFEcZa0Y8B2\nBFrUo9ikbf6SgIoTRsrZXS/npsDJPx0AR0DHZ05C0YCuwyA7k/eofRoKUGXl\n4Ol/7d0t9XnDPFV5S+iXoSwCwHsWIHtTaDnboG0QLQ5Qgn/t/83Qy8h8/equ\nMkKZh+vK0BHurAf6u44crbzJPuZjL8qVcba0qSXHTYWXKsqYZYNJ1t5Oya+V\nneqzeHSterYKZYKkl/KdiFJffMeM+tkatQdvbnoa3ZjHt3kn9Cy0oEmS1KxX\ndvSP4lxifJa4eBdsFEisIAf9/AZNitt2Cn5j0gKhaKq63/83fmQ9D1dCr0gG\n06hiv+Qkqph6lJhZ2Fep1iDQ3K/mg4Yo1O6IKeNMcZJ+dfE7oAmAAcmnP+i0\nC5VK\r\n=4sbu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAsHr2y2UZFD82zI2KF4esqhhI8IgcpReR8OpVFB/kLxAiEApUvZorBsbCv6MnUNZKylMgySQw+8fUM9ui13pJtbVV0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702002619_1530491355255_0.5228118484569972"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702002850":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702002850","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3d80a9cd7326302c976f631ef9df329059acfa18","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702002850","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-i+xDLQigk5tMYoCNSQ969cprI0gFHAqjVgpr1meiyltAirgAsKdjiLR3w2xrGmxlGIJ6N/xYDSteFdyU0AMVMQ==","shasum":"7980aeda73ce3c815ee613f40d2b9897e7c4d092","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702002850.tgz","fileCount":551,"unpackedSize":1367895,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOXJ1CRA9TVsSAnZWagAA5YYP/j4gcBWEG3ccAKzZqafW\niOJ5+RKPBQ4MHR8X+4kIMXdvFJsZXHbaqOPWw1qCwGk27BZwOG80LIghFx2a\nzgyJwgudZ868Wmnq49QX2C8HvN4r8I0YrfTdzy4e/SB4eIaAsk6aCyzLZQBa\nMZx1cPO4+dQxvciy9Gr2i8kbuD3VdShablhkjo5FPCzdwqwjW1Dq7f/TXUUT\nzU/bZeMOo13XBMGZbo26EOVxI3EaO5V3luuWDlg/iZG9PzH03lGX3gPNTB7O\nAbocZbSVLgwUhCrX5QnWEtrAjHA8ppy4pjPXzFeE1UGnf7MjrMOoeIjE9U3c\nilDmn5cAlw/tZagZz6cwnsGEiyPQIgKg13uA0ytoweR3zlZ7uPNpkXHHWBb2\nkpLg6SVcPMDhM2uvM0GOskuw2m7NzWWWrBTv28rKz9oFAe8MRmBZ6RLiOZyo\njJQWLy1swoPZofei5uWui4Uhsh6Qkr5ZxtJB5cJ6QhYRIL4oUPBKZfpt3L9Z\nlXR1p6u1LD4ZUt2in7rVYrIjOqtkBXvxY0MwXtRKteJyizKMQHtEB+SNVEWI\nsSwxKNxs6Wvc63++HV/p1QwN+xzAczo/tmpwBTFE+NltbN6lCK89RI9GxKuJ\nzi5wNLlPCieqyduxzcVhtEjVTQ/PgGxxLzy6LQNsIjytsrhildYdAcTSIb/E\nkjtl\r\n=HoaC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3yxtmPEyNC79tV10BUyIc6at7KTKQLzjEV4sFq3dzhQIhALVXO85XwRLhkDDghxobyu10ABuP2i9xSgc1Oe5HDKAC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702002850_1530491509507_0.33663009534221966"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702010626":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702010626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f6e205d5c6d9f197e817a6d9c12e804a3e9b3d8e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702010626","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kdlvU6oMUxFwqhXz77mbuFLa+9mjr1hbO68nuZFQWN2C1Z8+7fzfAVt+AGobzJ6IhljBPECKpcIoCTx5WlYkBA==","shasum":"22da07dbb5cc0a6e03b04215a6fbd19dfc82ea9f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702010626.tgz","fileCount":551,"unpackedSize":1367929,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOXtFCRA9TVsSAnZWagAAVGQP/jzlXkMrwMv9aqIyklRo\nyCCO3oRBYiW4JIh5T+KjezuQb2pPW3alCtsf8ohBZlW5E7FC1Gnw8LpgBP1y\nhdZC01BGU9bFEUg9TYccFFR12bd0OfKwllNsEtuWdI9ksHs/Uis3J+JW+rRO\nV949eWvgNoqy4UhIC9MC6LlPG7eGMM0FwEK2AV32X+qfbsnITJoFX1Hamgbe\nH4CymBV85ofchmFRa07zlG//45jz7rHzXAd75O6HF5Bl3itxydvwGdYBzCyF\nM7grVfHDnMTOmZh1IigrGexALkxSXUjFbqYKBz7YewQgD6uXGPu/sIa/tyW2\nyeXVj4G7Fuji70ZReCSWqvRcR0l6QIqf06hIYt3m4X18kykxUNnl4YGGhXDF\n8XPvD+2sdHA4445OkYG2jDJbPYCwTGfaRQarAMDJO61ZZ8dE20GMPlRx7dcU\nnQxUywP9zqXgFiM9KSIkSjerpyzAVF+/9X4PXn4c9gIuhSq3EmDQDrkRfQ1c\n5Ujr4m/SXuMSIQYEgxDtjRygbIx+yZ847chaeKcSea+6xMUyPS8viu4b9YO2\n3ezDNvZYXZ08jcpVYZqBVkPtuVWkFbj/SXvLGNoLCVRWWQHC3aAgXqth0fMV\nmE3ZDVrwrIn7aLsADJvrIyNgZXQUc2LOGwzp9NQRnUQGAG2JMcu0A/OJmib7\nDA28\r\n=Rdg/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFPEplZ3IS7e5CJCBzDuCaTxW4FrOGlfpvWhD9EJqjmAIhALz8Pahl4pwhKfUBotFrZheq0ZOtEj9ThIsxMlWFiytq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702010626_1530493765184_0.05520495576108053"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702015639":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702015639","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e1138a355fc2cdc732196a13985b2e7c6b74c0d0","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702015639","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Mn5pGOymDjnTCO8Kkd+HHER7ju3y9Cs4LhSg1zWQuncHxxIWCBHuCsIrJJ3+2qBL1DnhjzHkCucjs9aNZR8rYQ==","shasum":"3cf4e61d76a006af56e6946357dca9d3018ccaf6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702015639.tgz","fileCount":551,"unpackedSize":1367929,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOYcICRA9TVsSAnZWagAAOMAP/3NJ1PrjP2ZBX+rcJLlx\nl0SK1vaSWRPBcujGObNyG0LQJNXhd9JRPShbt3B9th9ZU2wueGO4lDWCOMjj\nTIN7aExS5vcU/lCKSwOhc5/tFTUrQbsG6Ay+3h1NCL4zmFvTai4bood3kaLS\n5rF7d+hOSRc3qlrhvyvh2kSiKQ0+ycguadYLTEhbKaPbeEZB6NJumOVfAKao\nxt7jmTHKZAmp0xwJ6SxGE3NRc4sCqUxDvc2u3qMC1QTPzLG7GIo23h6yUUCq\n1qQQNIk16+3X2uJ4bKVkfC7mw2yRWb9ry5eOb8evdZfaqFOIqtFRDeZorhVC\n3KvjP/k2V5ZCH7U2BYxAYS7cFz/5GVJFfgrjQIOlHAuUDtFjym/nkcEAeAR4\nE+7fGibLfC9++J1kzxTQ/vivvNYFXLAt6VXzMXOruyn5M4DToY32uAADS1Yb\naQNus4j6e8NjCh16GqVzWwXq4rF6v0ylTUbttNCC5z0FoizS/THFSgwNzvTa\nX1iopPExeaVFpN3QO0fKdMh0YWrSpcl8Va2ON4kSHdf9wGOaMHWVhu/gsZqR\nDvo2ERJHEnNm29fGbAl/HPTKCPvCLmswIhXMlghmwOulVTXJ2E5lTloKXB/Z\nz0vNCtcHk7I4qXqmEJsO/Ie6BsqFagZMpT7xMx4PUDjTYwdMo6r114+mJDqC\nUY+2\r\n=l8nI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtOK+X2i5Srh1g2N1zW8FOnjQl4zoKhJDAw6ImO6uReQIgTz2AEkGcpvlUcn4CtCHSVA9Ht1Wh6x49qT53vbWnNNw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702015639_1530496776776_0.3104534018909837"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702021430":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702021430","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bc06d6f572a7565f134d8c7217fff4920e3c2408","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702021430","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UlZVtQa6NRIjR6K1IYKxqX6KNe+FPiscvm8IQRzUL2g6ZoN4DW2PGav8KdX/9Z9hBR34tCeGaZ1mZBzL92INpg==","shasum":"2d826741739e37c0ff730cae1684217f472026f6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702021430.tgz","fileCount":551,"unpackedSize":1368634,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOYs9CRA9TVsSAnZWagAAV7wP/iq8E/p9mN/B3nXk7383\nVPSmhgT9GMJ7AaIWtLc4VzVmjIskBvtlE5GW14zVBYu+e0sKg7UUvPYxEjO2\nFk84Ou5JuDbzGMBhzTZTQYVUts8dvAPmp6pdMkpEx3wEBrvJfZ4YIgQlDYn8\n60qRrMrfDkE58CmbQq/08B/8hqtkY7AyPritJtmeY8HG36nWj17LwcWak/p8\ngEFVjFLPDsGc9nCYhMGWk90RWCCVoJiK2WU0dKb6GCBKp9ExL4K62IAiRfUR\nPLiObkmjLotZYggDhNDOcW+sMI6Xn9SDahXGyEnusWothjOTLUfrara8X2dH\nfUSrJ2HTBMmkVv0FBEqhcij9w26zCPgpH/jq+3JCUX4tsBYXqTA/9Rdm3QJO\nduc7xqHjhlzZ9HxvptUAj4yWDrlJNGi7AUNqHNjYNU87YCRTmtkaviOWGEAb\ntx1nCISXCfOW0Qtff1W6P/TJAjKG3gn1IGpUSN/IRyHm+Qq64YhQetdiPCDB\noOUUUvNDIv2XLFMWJCsq7fIftYa6YcK5yuCVIn3oHpO0NqF0uwAtkdU8hGTe\n4Vzqp0rdAbVLF49Np0NJlPlSbof/aKx9Ajsr9I93idaWDa12/mhX76JiOokQ\n94PJmrua9F8LGAFEmAFYeiI0Kf69+TNq8SHFemoMdLnNWjMKBvPEWTMBp8Dz\ntk0P\r\n=2xVx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDujnr6PYttvVYTBq/Se6coRxkq8UvkH2fMd3h/rW6jEAIhAJFD/rnM2J3M/53Fzb3p7Ge14mjw1QuVz612ZUNoZXNk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702021430_1530497852819_0.8796515231391324"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702024421":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702024421","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"40e9d7413e4bb59182e93c64898ea722aa7dc8fb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702024421","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Nd2105UTaBS0z1kd4oVL8tSRLKysC3yFGeJFi7aNuNDWJZTfUqEYdRzchWUZDO++grwiRI+3Ajy+K6sLGmoxLg==","shasum":"d92bda844c5b92fadb875d9a8fd3ab719e438d22","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702024421.tgz","fileCount":551,"unpackedSize":1368574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOZI6CRA9TVsSAnZWagAAiGUP/jlaneT99kXsi+BWWcOQ\ncFGTUHRYJY/yOSo6qQ+Cb/EhNBomVK1d9HSs8QjBUUd2p2UgQCgHpTWFC7GG\nY/EtScv7blCyitNiAD2bC+u8Ts7MbByIf6T9sgLXIXwfVc5oQwbkjnKL57kx\ndo1bhViNztUf6vJKMkgoeot5nfsznP5funcVSLPgmHM2GDXnLLZmXAmBQ6dl\nYhVP+7QCybwe2l3YELgdGCdG86xE549eIwVL0McpQZ/3Cw8a67+CWaTSQ13u\nAVBBDcm2F8viuxDSlUuabgNTxTwoyzyNI+uhSXCe9cd/yiT5dcDP1Cye17pz\nUUimHm6LD/w5wV7DPlFN4C85rO3YT0U7qzmDlJM3HJlWyIV9HRfOVsQC45wK\niKi+QZzPXSGSwY5N9+Mwd7B69EHFLXGQ/8ZNHwAT1Rse7Bi3pw+DNHshqJcA\nsNo0jqDj2ybAKOaMyfPPISzt4C5Sh/3rXfHhVYb432NZ8SvgTYNDrNipT0G/\nPBQzgi/eOLDr5rdkvsVSaYgi/DxLeccvMvvu4OXeOqWNiRtWlvVG4yiBiijJ\nFjRYuPAjcMh3yBsl4FJo/JbNMXU8sXehUJbhczOVFrOALY6gIbfH0jKtK5Pf\nm3zV+/DRmGabqh1GGsy0yJuTAbiwTHk8wucHkuy62MD+SD+AXGf3lOD8HbMa\npCsp\r\n=Bpi2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCFPERhTtUMpJLkyejPn/vFpf/TZkuE3O98BCIug3Yy6gIgH+qOfZuvYSO0DE6X00ov8VsKzxTOxxj5BJOu37lA59g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702024421_1530499641981_0.9287864057810133"},"_hasShrinkwrap":false},"0.3.1-20180702115400":{"name":"@atomist/sdm","version":"0.3.1-20180702115400","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"49e2861ffda51b61c200a72acb702bdaa1a530f6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180702115400","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZUNLjdPAFRkFUHz+0ThJ35KxC5GJDgZKpkUdkvR4opmLO/BTzIbx9CcPtGfXfjtfA9cnWN6tRDzBdOWWp2xoPw==","shasum":"47b7d83e9b10c6ce85833f4b2944763b9c0dbf98","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180702115400.tgz","fileCount":551,"unpackedSize":1365999,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOhMSCRA9TVsSAnZWagAADtcP/2svGi5OO8pKE3qKTjIo\nRPBRlcgEOx+84Rd5DsuHvrzgDx9x8fy9WiHhsF8If4gwfVfWIi8xZ7uC0FTJ\nVRD/+fovCQayQvaoejmyve3TlUPGZ1NugyFbaaY1YOiH85hvB93qxjWb5dX1\nuGDr9RDi1Q0ljMah3vfzcsBkTHrjZ1CCKCCEloe+Mt2PRStpkRbAEyGAOBB5\nHb/DetMAxgULthx8OapiTq3Ikr3bB2jnxrcHxIWwHgQOQHSV+GMIx8zAf0o8\nzHvXeVypDb8UE+ju5PUCp2bIp0m/K3Obn5n+j/1A1MjqdseYMzzKohIMfPeY\n9iKhKx23yxH49KIeblhw5SHbZ5Sxia/cy9+MmLv/SILeyEoab+76vsAh0tmp\noJlTrZb/yrhCbkNxARUxa0ZYysXaDguiQbaMQaUYuv7VyvlFuMUgxMB1RCL4\nmSDX/pNy3YAwG85CtDh1HytmJth20D6LptvO0bZZVd+NkcBW7YYazoBHVxri\nD0q98P7gm8WK0weWUKGxhivJ4f2ddtkNrC2rkioTERMjPAjjqjb+BVMfZ5hr\nXBTdqBVbPyE0Cf8T9UGr3HBPleM+UCKu9CeWlVKompbot7pFDAscWDtuIzOM\nC1edvfnFxBkrPtZoW5zyHBZW4WqbNDKAiOoXlkSQzkV3izIeGRlt+zJqLeF5\nWvJT\r\n=h6yE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFVYY4odDT9ph5/IUvcqaBi1rgOpgAQF97j8+OEuPd+LAiAo5NzQpXIQRK20O8lYVaeO6no7QQEdXcoAI139ULYoJA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180702115400_1530532626793_0.7823532558210362"},"_hasShrinkwrap":false},"0.3.1-20180702171026":{"name":"@atomist/sdm","version":"0.3.1-20180702171026","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e41406b3a2f868f60bdfd2687095aa097940f17d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180702171026","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cJ0reAkCmAU3eIpCTFeroL168L8XRppqQmRsTZWC8UP0k4Uzdf3/lqfIMhkPjhVpqDSFpHRdy3uwk+KbzidYpA==","shasum":"28aa2a2ce126c09d19e20961540bb10b3c1adc27","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180702171026.tgz","fileCount":713,"unpackedSize":1689987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOl0XCRA9TVsSAnZWagAA0ggP/1lqJJOv+qD6ChUx+d5A\nAlVPjoLeN1IBBoF5HjHGK5/Elcf1Hm3kNOm4NjNFkq7PQcY8ntxm7ZhAOGW3\nn9A/tnH5AqB7bbvzJFrUYxXgH127V468xZ4J6uwuAtKiQKAPt5G9U8zT3i73\nxCGtp7S5ZExM7UwIvpm6nIRV6NGaRT9SPx68gtK1HpRkXlIZozJXWIlmusy9\nPMYz/2cXBxhPURduGDksmTnDxgbsGlod+ygBmz8v3074kmK6YZKlbyGHX4Sf\nokzJGgcZw2rJMgf4DvnsqBJhUS7mOvZiGktLKoeXTbOdhhTTpDwrr9NAM7Ng\n9FyVzsD0Ki7/BHToMZ/RWym4H2qlTNaz0dHseraWAG0gTEdJJNfXsSsEuubV\n8KuuYy3fwoVJUuHVyqcEnAiQKC2Ui2BmTEcvqGZ/q7tctjGMnMdZoubXoFMw\nRMSyzaQH8p4SeiTGNLopYUBeAEpRmPAuXE+BeRGYmZayhP8RmYNsg4jDWKuZ\nIrfeheXg8X3lLOSTrYqfSLbhWLZPUNy4YKFU4jvI1aHCKJGwRKvzDBCveeO7\n+wtJDKj1WHSiZ9AmQkpR3x/6eOq3NmAjleTWKNAQnmo+eEfvJDoaAZf3vW6u\nD01vjGxrDqsxdE0p3XIKyCyYjm9yHcMnlhwEKML4DKJY9IJezRUplUTynXhh\ncsq5\r\n=hDTo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICmY+kuDkxI+uN815L+QXY6faXlS2GDj2JmOiHApVpEUAiAxe6TJSvYSrmCHemUMDX06zuCjPvTLTYzWrh1EbJqP8Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180702171026_1530551575436_0.7803386536849923"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702195220":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702195220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-functional-event.20180628192301","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^3.3.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.8.3","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"8.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ac488145b680a9bd82f47b09efa68cb0f9a6828e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702195220","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7u746BEZHOzTeIU8piLh5bulgPg/IsYCp4Rlne+XoX3F8QN58RIN9uYFEjKSl5a/VmWLi3AdXuBi3TJe4AKJdw==","shasum":"b1c4655e87b0b91aedd3440678f5e262d82d7ca2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702195220.tgz","fileCount":551,"unpackedSize":1368615,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOoMyCRA9TVsSAnZWagAAnbIP/1Gtq71Nrm9+GN5roSmR\ntGaAuyvymcLszrDWbyNKfC+VhN/UJbbV1gNpyHx1RslaylrPMwMs+r937MFP\nB8DSmqRckxvS01d6APUjPvN4/d08p/MUgIlfjm3DlciJoUvSTsKAjkXN/S1b\n01YQVbQKL/iCz4cY2M81lRFel8jU7zUDeu8rigwYkLGn2IxWnelDnwt2vX0K\nefdfGPVI2Iu7R7bEfN15qGtMIj8lCAT2Rr/Y0QQNoPOXLeX3MKNC+362eX3w\nmevMejW5OnKqe7sbkLKmd2JTA5uIjf/qfVjh7rKMn3SkFKhsX78rPm8bMR9c\nMRBfJ12cz0F0BKVRWy2j8mbDvdvJFC7Rcs7P+94efVIZXhBhgeCfOEJRmZHx\n0yRAy4gYRwJlz403pRP5UDqLwD8N1sB+rLLsHc3lLLeG36ocS+4mrZi8k9t0\nOqYh4OUhcJlFHGTv7/B7isoB+yoP7bd+DLdbPONUuaNNXVGc0qsXikcqPNQH\nx4d9N+vow74gtKfKA+c8hJnr8hfVLbl6ldJJZ+jFxqb1OyN7q7O5hB730HpZ\nkSp5mtthcUEMCd6tR57B0DtKQ0yZMoATyhwawRkQJ1yDz0cP0Ip+vZeAlJW0\nfZPmFsnB9j2qJOJ2IDjFrNvJrj4Gdf97z3M4eEGU7er3+u1K0x3rEbROP1e7\nNtTq\r\n=aMb2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBQvkR/gGB4FsVraTlXnkKF4fY11hQTRprkCuNNsb74dAiAlTCPUpLPFzera93W2kM9gMRSDIECosx/qNESI92QVYw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702195220_1530561330421_0.1285804827053023"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702202418":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702202418","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"abf7df79bacb9f9ec49ecdcc6f5aecfae2486be5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702202418","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xxjTItf/5J3fgxm5iu83NpxmTntv6Mqe4uGnZdfDErceYwDWa6tE9pxXecsOWnMljlDFvRJDdBPyVcXVy3DtvA==","shasum":"a29ee0392ee48dd1cb5f25f7fa8a10fcdd24a96a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702202418.tgz","fileCount":713,"unpackedSize":1692770,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOoqWCRA9TVsSAnZWagAA/UAP/i6tGls7+PZWynHNhwll\nEOUKhhKOUbOZm9tWKfhbsp6rSE8sEplkp6lsq67wjVwuK9wrmHMNkb/JRlv2\nrIz84ADa8Ch/zNPSB9GTTqcHuw4w7WAIYfoZoNPgTmGPRnXArEwWjIQFTI9T\nlHA7+R5hglOiMXEm39UuUA3WwnVS7fxzDhsHUvZct/kIMGRJmQvIpgkaLvJo\nAb8B6lPy8RJOkcaUtPsM4eq76JdIyFzTUuZUuIi9uXzCDznAT9BPUPg5nOMf\nOf5heFXyJgxBpUKcv7P0PwQ5/DVNSDLJVVaMv+C2KFAV1cAFztnBb1col8n/\n1Iqi6cBVe0c3msslmMwjABoku76h++3LHr+9F2KUDeDoxiiMyyMsIpi2Ka99\nItPzg9sCaIred2ib+hosYbo69CqjPpD4WgK2Z260BdGV6mefeV7F+XVjrK4+\n/c66gPNTw0m6mI7zBjL87V0F9h/cyxIeI/MXWmzZG0VlskI2cXkP/rVpUo6u\nkutHZobCN7vGt9TPcD+57O6Tscg1tQDfhlG/BJSJ8VIfT5Xu5fGVUuZT94vS\n1yUh/G5/2hAO89DFEfpteJgJ7o6Apycg7rGy4NIdF6q+hxlcyH/g5RWms8zh\n1lfz54fSEbFDmqVhY0rRzkkD+BN+S+JJKt0ZOhiZFqpmisycZBPuDw9/vS5w\nGck+\r\n=CKZz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBlA2RIaL1UW2xF46BTCKMMxJ7g4o/uB7nlomxvAn/D0AiEA+i519zG66qaWkp/0GXFfSaXcsRpbnGZC79DBFABvmKU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702202418_1530563222124_0.734333358592252"},"_hasShrinkwrap":false},"0.3.1-20180702210421":{"name":"@atomist/sdm","version":"0.3.1-20180702210421","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"011e9dea5486cc76d1186c104d400188ad0be51a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180702210421","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pmxt8Aa61rdQB3Dx0XjqpOcdQrBoPGFjjrkmcMQWfoVPRmG4SmYTN58DiikyuhQU7fHA1J0kn7rNjqrTPbCNUw==","shasum":"5b686433d0ff4c19182a1284a881c31716495a0b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180702210421.tgz","fileCount":713,"unpackedSize":1689920,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOpPyCRA9TVsSAnZWagAA6vMP/1fyVSK8LjgotngcNK88\n299gJtLhwebC/0FDZ/EIUFADWokKiHg9JUU4dMW/S3P42cWiPH3A3rIKeSIh\ne/HJyKAVRqSIT8LUW/zvgeroyRQ/z8VLDaiwTUWMAHpa0maIOaAgU/1fucMh\nC8h2k1F6mCkTY1o0oZ6zn5T33s8PVdLwsy6EElTK4BO+BBX7TWNt0rMp/PDt\nINCsuIhDf8t6O0suUgjACc0Z1lQOp3NB47w9ivyz/cqIlC6wCamvQkFR8A/P\nES6n0OuqM+7B6yHxWeOMbylI+CoXh3BO1CsX6dBVKY7bWR/v3Ld2LFjkGKjb\nnXEKSjOBmaRLX3A+YelcKhSKZsNi01mv5/AdfXxP0wlqEofM+u3iaciGvjgS\nhJlJAayYfVk6dbHM9KeN/bUEJNttB/QCbmCbvXJQQMf307iE1lg2RojWGCMM\nZ8RDA/4OtzzIS8xv0a/apUf3pHXXCHCUlE2SxjcC+zZpoa0S1aJpOjxuYyTm\nTVyRcjsYjYWJBJ4BuuadmQycmRdTEU1jGIE2wl96x1/KrkeN9UYb0EJs6aIs\nAZ2+zPBayKpzVyMxT2f+PglbpnGdP1NAsjumCWKpZcf/t+Xgn4CysK+VrqS/\nx4T4aBFqfgAiLz4ehc74sOYvB52I96dGdwASqoJPQ4pheXLjAa32QHmrz+M7\nhkI6\r\n=/Pel\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEzBI5eRY6uqiaRwV0I4cxUS+fc2FxXvqpPbOGFhPle/AiA08slw+/mtiioZxJsXpQV9vgOZzK5942ckHwA4wyXG8Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180702210421_1530565618292_0.5730948352738026"},"_hasShrinkwrap":false},"0.3.1-gen-starting-point.20180702213722":{"name":"@atomist/sdm","version":"0.3.1-gen-starting-point.20180702213722","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cbea5b01ca4e527c9aa3379bb48e25d51214e6d3","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-gen-starting-point.20180702213722","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NcefLigW76vBhoQCqHbNUcWnVmeYLb6ur0BV5lDBUdcHyvO8j4mSAF/Mp7n6dpjvFflpu03kMf0crtdVATgztg==","shasum":"bf5b112afcc8d7efe08c774cc1139fafc663a675","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-gen-starting-point.20180702213722.tgz","fileCount":713,"unpackedSize":1692703,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOpvACRA9TVsSAnZWagAA9n8P/Agp1CX3Kr4iWWnNoCS/\nq2/v92BXe+MOPp++1aTvF742DyZtD4Z8oZLmrdSDqhgBm431amb66zmjCTEU\ngC2NCzvGHoLPLZevlgJvXeJjmFhP/5vAzVjSWmMJGz1tnRSAuGviE4mFrxAe\nnCklwsa50HHPwh8TppI1+xEQEpesG8R62qfzDwVDrb8bA2fu/PhiFKK2k6jm\nmDUGqvzcoheVko7L8p0m8XA6wjfKvYU3hbFl5VKbMEqBZJTAbzGZEZGLgexK\nalS5F1yU38/5KA6zzIaeIRL289AM07yRoPee/PvH+prW03jD0wGYLTMWOXE3\nlFrrczFsYEQYYwqXfL8WZT7DeVbKBBywqv+VMcVERkCV0LhtV8orqHZ8qCSK\n0X2g7ppQg/6WVFZU+5W49DiYYLhvl5/TArao/NsrmprlNRoELyUKpl2e3QCO\nKIe/rnfRwILjZXhjS7J+57WELPPjCnoZUQIpKHXTt4/hcG/z9s0lf7Veq9Fo\n6FkenSR+QfZVSjynn7m0XwBVqFddqQKgim4/+JnKwFdPDOnM3tfcaJVN6evo\n8I4HFeLG0tSKvBAFVlDmpHCxaWGArsDCBrqlmR5UaU2E1h82JHpqj/4USdza\neFOXjblXzf6QE/Lhh+ghCm2rYI+3xiVNwwDGDwvogiNTnNvGL/PhfaHD2gZP\nw+p5\r\n=l2i6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGNOWCmesQMPykl35+GVWuq4I73tpD2IJNZfraPyaN5zAiEAmJO5Xh1ljQhg1pGWlBCrq7sBYay3pmwkUTyRK5aSHBs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-gen-starting-point.20180702213722_1530567616745_0.8687117154842763"},"_hasShrinkwrap":false},"0.3.1-20180702214201":{"name":"@atomist/sdm","version":"0.3.1-20180702214201","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ad178cbc4843f2a175c9a2d6617ce1d9815dbd04","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180702214201","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8nKGdqi3GosRMPKGxqF/Ne57juljamRGJFFQCO4YK+Ui5kBL4NuTk1+lhtNCixZsk/2EVYG5tcYn5b34FG106A==","shasum":"90354c37bdd6bb70f50def638188495c4066d1eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180702214201.tgz","fileCount":713,"unpackedSize":1692684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOp0wCRA9TVsSAnZWagAAGCcP/2Qmw7IgKkIex3K2m7JK\nZRsqm/4XxZjXCS+lpFKX0Emgh1cPP1gwq7hZMUkNxm27hfWTWDbOnKmOLHYb\ns3KpgCiUGL6+7flcN/I1mlusWoGg67OoRlPJEh4gyNY5xYBzvSriSo49bJdv\nvIS091USBl2KO5h+77VHUY0obuTj8SigzHeRpKnMREQHHRo5VX7TaqYc+hji\n2Yfl9lBQQ6Qmv2FfNN7RyyJ3oOFYUWDaTMTfWx2dr1RkdZSZlaytxFnY06iN\nmYyqJ6Syr8M5tcLSJggAhTl72mDHJnNeXfbcesbIMqCbvpdVOF+zTNmKN78M\nEpxUITT+ebbquRiyoRJblATxJIavxK9q6H3LxMrBI2uIw1vasDa8qdpB7+nL\nT1dkY4xPJqGiQbMyUSRIxVNheZ8EZR/+npB0Wa1DVCF8tZZV93XFu702pxOS\nHUNwSTHcebb3LREXMItd1l5YOfdvUCFoB9roZRkyVo+/UH7cqeRG2QhRJi7Y\npSMI/sgXNNBaBCKu8VXUSEbCTzpQmG+iDvcOpU83hp6G5O4o+mL5i5kFem3R\nSTFzOHfmnco8aFRzoroqUaTnOkQQqACq2pDr28KhlZzCeochei1pyhyW0SNw\nXLZBeCgrn5altJmq04AuSoKMMWAQATiS5BcbQ1GlrtyuLPc9ZlIezhRJPgGZ\nOZ0h\r\n=rq+M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+qB/XcCGXlNz+kuaDsIPjzD6FKngDlUPZ5ddn4HNBLAiAh2RGUl29NVHAvhZoYUbWQSGocqUVAAPEa8G7cTrBvjA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180702214201_1530567984071_0.49919920080854596"},"_hasShrinkwrap":false},"0.3.1-20180702214401":{"name":"@atomist/sdm","version":"0.3.1-20180702214401","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b50e972dea9e835634ed4afb45fc03e9f697ccdc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180702214401","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DBrAsUtT853aOW9VPQf9ogNkrOBTca7gVEHn3QDugaz2hBnZyO2buiIqc7NFNXh59z3Er2AxDOOp/XmhIOrIhQ==","shasum":"135fb26740f4ce7a76591cfd0ba1acae981796a4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180702214401.tgz","fileCount":713,"unpackedSize":1692811,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOp28CRA9TVsSAnZWagAApYAP/061lOiO3dMdWfBPbmlF\nWM9HYzENdIQSbsEFLPud1v4962lM95pDAMe5K/vDhneiqOWBB1Py6hAAK3fU\n2DRmIctCjt5VV233y/eDn9JGBmShxid6Rlq0G1z9kb2Q2GVgtxjO8z7sCx18\n09Y/hGeBwjWCNO12xsm5+GvDYEskArg71AQtGAB0T28lsEbvYlXC1wWgNiFg\ngFjCCI644Bm2M6UkU9K03uGHd5jsSs9f7eaZh9VVWatCyioaOUo5jlD179no\nzIVlTafGHrLLpXYUknFk56Ih5zjDmLMxGkZQjOQCqvyFRoutcrgqGaipxq/Z\nNIOsZAMrAsDfk6PzUKIIyaY6k/CDshKqtb4u1RFa+Qko9zaQNpZ/8hIdRtM5\nKovKpQ2z0FC/jW6sDoDNnZocQM/EP0tAClTpnejpZ8guAK1MKdT3skwagVDA\nmwPHkJ5I5HaH89z64LS9Xl3+TbjD9P7xWUi715FtH24C7A9Xea1GyEqjHwOA\n+V0eL7mq504bz1NTTrzzAuH9Rwd4z4oNNeznHJ6kMG+NwZXgODQY44otDaOc\nZDSz4o8MBtOdJOhMUZaNCq4hblM0OSuahQWFilXpSPhrwHjzmnj1N9WVb+rM\nsgNpcMO4xhnGJriVM4XyQFC3YCQi+gSUrmYzQElCpE0nbb/QR+lLqcBPjT0B\nuAnc\r\n=kTVD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGA7G4h1/sI4gn83cdZGe125s9uKtUsPPgO1LlTyNkuiAiAuAHdAi7g+0xzyL5CdxoxQZOakiAVHgS+YGzPNpMGrfw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180702214401_1530568124385_0.4306706445477204"},"_hasShrinkwrap":false},"0.3.1-20180703023241":{"name":"@atomist/sdm","version":"0.3.1-20180703023241","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"722eeb49a1aae99780618adb5729fbd872f3dde3","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703023241","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oqixL+lOebsieZiWIB7PrIB3g6gKjRcuz7JXWtMherLBzglQf6mM5YiKKx3uRCQYRbVyP7cLpaecw3iR9YcMMA==","shasum":"89d9aa07feb40c68e9c0736d48beaa70c7c73f19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703023241.tgz","fileCount":713,"unpackedSize":1693247,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOuDMCRA9TVsSAnZWagAAxfkQAKURUqTC3MlyaE4vMyMs\nVWo4IhnC+RQX8Jqrba3hYj/EuriK3LkeJzUY2BU3J1+lI6QHS+R+/Ynodg8i\nGbSk5o5NaPZ5XXKezwL4mLVOVHllNw/DrMIG4aecwUwCX96F3/4XgE0g2D1U\n+RXSKiOyD5LaIxSghFaBFSxnVYMtvX0hGyeBsixE+XV/zAuYBY0klo6tElls\n04RQxAKFDrm5Gc9NniAFL8xUcY18aZ+pt/NpJVBHVxghgym9CEy5Mn1k1aRo\njHUj3ssHRMsVaGww/WtC6Xs/WdhlRsc5V84AAcHZV0KtjLOK1ksZ8LP39uKQ\nNpT7Uv/Kd32gcTNmKisWXzpvarKOZAwAqkcVV+lcAXiMc3IfrYDgr8iUqAZx\nXjzyXcj5N54r8jhzBaIGfYbQnc7lJ0aXLSfvjrd7Xq+/77/ICZgzyOPQnOQO\nUoi2uN2tXyX/8SZO+64hpsn/otVggk4OHcNhBQLCt0WVPOyxM463lD1g246+\nWD+3ULqAFueek+8A0Iry3l46oCJYaXmwGXFl+hz0T76LKt+EUViSQ/81JEC8\n7YvCyMSMf42bcWe6efqkW1+U6ZWF+NARLOMAdu7jnNbCmLS2flPIEB+6Lu83\naaJA15OIfz8PbGct7Y+SQJ/lg/fakzcVTKbAUUM+fhtxGCvxPhEhV1q5mNnS\n91aw\r\n=j+oO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqFyFWtfYGp3FuwK+4+diig3GC187s6hiSn8tYNvLwUwIhAKSMbhWCMSjTNfmCtSsu7MIoa0w/mhMmNHhqY5z7T/6V"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703023241_1530585292571_0.07731294444554826"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703024032":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703024032","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"97a88a23caa7f7e12fd6e8ef2faea9dd4b0a74dc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703024032","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-33WS6WsaxqvUIYC6qiRJ5KCL4SKh9e27Xb7eFkVXQqNbvKUD9qb66M0leKnd+IAYR4diUCpSvnf/eRV8Lr9POA==","shasum":"c09cce6367233dbc10a202c242d53eb45cbd51fe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703024032.tgz","fileCount":725,"unpackedSize":1700352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOuK/CRA9TVsSAnZWagAA65QQAKPdhrJ48WO48kPmnDfK\nx1RmCLHZUZ2VL4GyUC6F0B2KcS5S7KEBDqvfipM9GuFV9asojsp/047IuLJO\ntl6WCEWasmAPnn5uwfKp3klFvu35Ij6qx2S702GdlOw7AmZcIJXlexJTDkJe\nNwj+JLqfGHLZGdK3D4y2FEHc+Hs/BcxZQ9LnvTnLDV4L20hy2uOmxfZELh80\nUjsmqmN8JdKbrUysvIKcrsT20a8ZdMvxFxWkIdTNgWONx025/K+9t4ZDKY+y\nu3mLBRFFIe9bz2zQK0BcN3vUHvWC6nlcZTShu13R1BvttLlU/Uab4WAn27QA\naUsHHiSzQRv78DBygXUNPYFxL56H46k4iavv01DYGEj2rHLy33tmBELjQPdp\nN2p3SCxctcyykPpQ3ylSfbt0kdaJb9ZiteKxaBQtqh6Yah4/VfWygFKXSsQm\nFXnfZo50ht9BSl9T605QncnxBe+A++8d0HkXVPKI0Qbr2H63KPfYr3Mu9emH\nL02aRBZWnXYt4uCRJeDHtc7ux0RqgIH0J83Wu7arIDmU5whQMGYxOq19/HaH\n8pmrWpaE7R9zenKqoQBHv+lcn+UfoaMExEMTcZmdSQpFeTyX9OiVXxs5OjZP\nomhiNDRR/osu+hMiB6Xf6AkbFdUPSaAf5woC20VxdMSbl6bBkHLV1jp7lmTv\nvXap\r\n=DCYq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG/yvz11NAtF2JZ1fSa9EiFQJW9A9OWO43QViXwJkAU3AiAhZfH/3zj5LzVszADr5/XEmFDuGzcpFMnlHJIAoXadtQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703024032_1530585791517_0.7241967795947872"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703032749":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703032749","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"364ba38a6c757bfba16c793defab80f131fd2b9b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703032749","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wlri6ggiT0xIwuXrcXrLNxpBOfoeLTh26GLSRtmtaC0Kwel4fgwa4TkX7FgitJDkpPjvninG4CEEhpTXVASg9Q==","shasum":"241513f83da38a9fbbd2ec698223f7bbc537c46d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703032749.tgz","fileCount":725,"unpackedSize":1700813,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOu25CRA9TVsSAnZWagAAkVMP/RvJN+lfHg4ZpWsr8npu\nCxh7l+29bUGKkS4Fl7mt9RVel3hWaLehim0B7rhzJc+CFssHPlpjjX/gftt9\n8vq0EHOcUPUbkQ6uUC+e57BzhxgKqtgMiGtq36Tg3i2pCVgWANLh1rrWwRCa\nzdeKeSsyKIgmBUAKoaglmyfewbVaZbx3763TE6Q/3tpmuQNuLb0mB5QA7rEd\naJnpxlM2Y2D8HakfzdRx56F9/fAZuIND0Pi/NndT/Eut+xx/aC+nzykrBAfS\nazbUxBWkxsUq6AwDyDBUwVrL5Kdif6o1x+TBoetld0EXksVGMd5aHiyjLI6x\n+Xsa59MmZgRrYnqca0qPppY3MLjxsp4NfrLKShKqv8uGPt0qlBk9M6TAV/L4\n/J5B8LVAXIkzYLHNkj+O71nvjJ3kFIXNHHsly31WvNIKWLGAvzdRCoHIIGIN\nKXEFcVv9OPK9QCpIG8NyI8xltJ4M8M09wdjQNnPKmgCDBIxyU9GOVrELWai6\niwKH13MxHnI2ATwH3ihTMV10FxKEmFxkybGAKyipPK8zjttDN6+DLcZZIKK9\nxTeRnuYvwYM0hpJZPZtPYBQFaq/LJ1dKea80O8ox+5Qz1XMU3AbBSTZAaD9Q\n5qrbSLGIH89Gsjaum4jZ2DulvCBOpYRTnjd7tUXCV1ycWaI5j/Goo9GvpoIz\ns4AV\r\n=gGoB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHHjQuGjTPMOXA3TnI1JlAkqHWhxxyx9Kiudu2YjZrqzAiAd4SiiYkTRS8VevEu/1ksiUHKGPTcwpwntOyeD1bU8Rw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703032749_1530588601721_0.5474545079312243"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703033650":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703033650","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1067edf9ca10cc887bb2edcf75d673336a86cfb4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703033650","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YvwXAOrn7HkNrc7wleaDFTSSS9dLVmxP4s5YDmJl/7OO2NIqQsmoXM4p29OydV22EUpdYzuWXg68PJtbvVcCJQ==","shasum":"fd73985b2d6d2b2f89b624d424909df26925ee1a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703033650.tgz","fileCount":725,"unpackedSize":1701083,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOu/WCRA9TVsSAnZWagAA6coP/0S+lwuG0I8BotPLmccD\nAKUW4paBseb/HoR1dQSHPxHPXNNbsNM811TOF2tfDeKznYB2fMUYApxwsOXR\nqjkh/Y1jix7h4GLXWImdnJzTLR4+++GU6Z7i7CBgxXqJPjryaG7tvY0TZwXX\nGHIMdn14JY0d0h8d9qky/AY1VodAf79nhtfU0yrddIIj1ZSa7Ax0xYkbmftA\nFY3sYShdEJFOmA8ogOeE6PdlFvSiY2XuohPcpzahk7mQHwdv0/aa2tul266p\niphPDLvWJCZGOKtFmwRDstQdAXcuOAdyvUFrFt+sAhDxahOdCS/Fg44ctda+\neJe7iafJxNR28u2pYR2HAN6gV+psxzNb00SthNMruDBolr89jKDNvvz4KQPm\n/BKnzKP1UakEBR2LKMaXz9jRRKrFK3ipFmCMlNYSZIPPLHor3jei43D+pTCZ\nmYTxXpEYcBWBM5tlukl6cYRPretjuByGKf/vwgZAkFUcmAhmqDJ81RdYcLLX\nCP01WJhbLAKuqFtMXXAwwvtFDiwm2GFWa1N7ZMs5cHCA9g8dKR6bZ86Z1hnA\nFERal8c2RxIshf8Kr7reCJutMq62WcKxb7qTJeNBP04VzraUd8Tgao135a0i\nqlLUJMc1ZB+ePhbs3c0R8yc//Iw9fnQ5+IlsQxp0LP1GH8dKOEGJn7D+x3Mu\nC/CH\r\n=5IVz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/0xtmqx6zyp/tZJgc+wCethlDpgIR1hKNs6jie59qqAIhAKRrgkvtC6V4AGOCz9YHWD7xL/mEQ0gv9JtiJcBl8k9H"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703033650_1530589141915_0.17524684513309552"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703035816":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703035816","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b08b1d086c4532f9e4d2ee917626c157a5f91872","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703035816","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GSdsG6k4LYewASEyzT/0LkQYjYWNSJnI1YEwzuLf9Vm1qxZNCMXMzbGvpCdhIeWH7p5n7VUJsHP+6qMGrr7v2w==","shasum":"c42d9e99437e8b002d0df08256986ba6e5064c70","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703035816.tgz","fileCount":725,"unpackedSize":1701640,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOvTsCRA9TVsSAnZWagAATHAQAIAMtheIS0nW4K+oYUsp\nrpTOo9f2FN5zm1TJLfCVjNQb374Qgn9D/lSp2WaITb1ofWyz3F/TvU+rQPvK\n8rdYH559gKmqPDps/ysz1mSHp/SKyxd6TUcuzQ7xE2txnlsQh9V2/j/hLbQ7\ndb3Zd65xl+IU8OCJqe04LVqbNkAOH/wsHqSc0fIxx52KZEVmc6q/tHPYyDDL\nXpEH4pA/Mns4Ic9DapIYJSp7VtNtsewHXVhREnJQdQbNBaUi/6tg9LgpeiCH\nIvZprSD5u2WQ/8l+MqN48APepjmVt2yuVhS/xLcw5cCKTrkVDPNr6PBN1ZAI\n8FlPDvUMGnS8+9bnusFAZeKtaZierqtY/kL/a+FSISuLBRf9HTa5WeNf9N1c\nJjQa/J6GNZiHp94GMBEYYtKGLeJe82oFUdqJeAIshQz6JYspJAzwu0WCHDbV\nFAHeU+9Vu+nGPT3e+MgejlUAr05ZLXZPH+lznAcHVpuVW7u2+0jMi7/mfXfe\n1WFHPoex5CUyg4yf0KTuM+F0citFwAHXkf66KevMWzjT+mVimI2Li9K+cZNe\n47RX/27iuCIWvlwSiXVzyUlDAkUXZOh/yMUxnnm2fr4FMpGHHUk/WpBruLFQ\nG5rC60al1/PJqXLkuxjmFUmnEf0dubG1FCrQcEF3oLf2acaBfvdUbbQEI3HK\nd+30\r\n=q1H6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFnXRBfEdfNIzyAOQ027qI7HD8RY8NrX22xiR+tUpgJpAiEA8rvdwutbS6BHNYfY/PzY/KMIA/faXy1EFh7UIPkO+E4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703035816_1530590444589_0.5224070883497729"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703041319":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703041319","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3f5107cfc4a1fb6aadfd385da5b67734e5535336","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703041319","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0zILUYwbbNEEg6HUI20EVT/+1Aqq2ehKs7cOLaVw6DwPe1SVfv4GCbzZBcI6UNGHzQEr9xuCLJytHTUAQxCa9A==","shasum":"925bac277c261c5e6019260c5d44d1f0a47ffb27","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703041319.tgz","fileCount":725,"unpackedSize":1701897,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbOvhjCRA9TVsSAnZWagAAj8UP/RLC0EjbllKLiu8+uUcy\n9ZwTKVODBp0lGX0mG8HjKXUz4pjaVko3of4akTgr9WhGH4lPZc3BYClppJ9x\n/+IVro7gorqyyC5KFHCkgWvAgfFMRwYqRDt52hKexwX7wXs3Nowhlmownmd/\n6+1eu3821w0XKBY6y6TFYvLoNoo9NqD2rZmr6+o1tPM/ED/bkTB15+kfs5+B\n5LRr/YNLYfFjQ2BgWJXRyBRkufEeiD0u01MTDCopHz+XwB0uePbzpPYoU6j8\nlK4CV/CwaiT1XR8bQG6xEWIJrxHiUf9iRSwDYOrapJnpWKX5pywwHnGuNzfF\nH1I13+/qK5smPIze2XqDfKFHcknapkaajaVXhhiaSwWmfs5Zu6wIFns23FxL\nyzWcPepUo4C7YUT41s5tPfiLD88RjtRbAstah7G7U8Vdygs8LKNXilbC0tIq\ntdlh6uglGwfMMbwT9sRO/tymfthwwc6fJTMVBbOHKIFBsWo4E+W5LztOtpva\n8RddyIwMOpRoaiL7HmGjaCiSLF9VkHEPzKOx4UVpE2DDReKYfclYtS0or+lm\nFcLEoVTof0kszltPUx8h7l6SzMdGSQo5lTXXUifDZnWxTaffyv8uoURzjcwd\nJt80GYmJOeYLwchHhoeiwY8ejEfEluSodEUwXC1Eu0dxirmWi6pNbw6A1KIL\nvsOC\r\n=myhR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjOQlLRCx7e2CjhcvIfFa0+kysRMqz6LbeAmLVbyQlVAIgMOUwOAx8oJX7M0ytBhKygjx0PQNhSdPUKYtAxN1WXws="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703041319_1530591331015_0.1700658200179863"},"_hasShrinkwrap":false},"0.3.1-20180703095807":{"name":"@atomist/sdm","version":"0.3.1-20180703095807","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"002b6abb4141be3235a02d39eadf62969be523a1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703095807","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ELjy3xD3m73ALq66S7hxq09gtexmiXJ8hHO/Cfx9SWtj0UCiqka04JqU/haCyU6N9bSOIJMJUKOlyFdJqlGcBA==","shasum":"acec9646a7326427b44ee95027f37d354e5eba78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703095807.tgz","fileCount":713,"unpackedSize":1695470,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO0lKCRA9TVsSAnZWagAAac8P/0EdBmnKzF3aReH+bVc8\nqXmSK0Sf+vGa4YAbTm/4ckGP1mQ0QpkWY2j55RtHeSmZ3vqirYr7BdVgPo90\n7RGY+6Ej2rnkR1jXZihLQ4zRwd3Qpu+0ApMFWgf075TlSQJIbEb3PMP/CthG\nGUCPcE5Zm10rAUbrbXtIYUSg5r3oPxUC6WdaqMIrNVZJ/gAr2d65yuw2xsvm\nCdkY5Nq5JBeyiXjdbDHqr1f1GgmjklUdhGbndbPEvWK5YLiMhJYQeb6TwknD\n28fG5DJGukdjvwepb5dQpimrjZLaB0ezJJ/7X2CDP+PzOzz3eb144Hi3OLmG\neRyNmXLZRVpfYM2AveJS8Nvsh7Q6/LtGCpYklVv22Skt+dlLgSqtfRL0CCeP\npGwpTyVE3m3GTcOaDmGpXczLOWFFDgQkp5BhtUnvMApuDtqmXOj6P0HF6TWA\nHAdhZ3pnAIcRXKKHytupvWeEXLWPIsK92My35jYezJA3ibRGq0JXwPMuQoUE\nC5Ea7R+R1/GW1sV3tzXaJAN9hHq9pwYK5ynD5n1ZyHgGnU/IhhoCcMM/knq1\nm/ZSy2LJz9DpJEH7k9YfxMnJp2u4HXFTbuxvD8L6+ruguDfZHmdC5foyGTyk\npLRofLYES0ciovDE2lG75o0URd1sOfQPY6gCceyyMa2z8oj2u/EVwRF0X7HS\nt8yY\r\n=ET3J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjC3ULdC+8W137Pib7P8THGm5fvEVbx9/bdikfb2hqzwIgXVJeb42H1RRATE5gW5vzRRh07iPm7eKTPUJ/70+NoyE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703095807_1530612042387_0.21970469954940275"},"_hasShrinkwrap":false},"0.3.1-20180703134245":{"name":"@atomist/sdm","version":"0.3.1-20180703134245","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"21ee1aa59e30debd66c54ddd0162ef547c42eddc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703134245","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jg6fIFzrPSK1kC7nT6klS4Dveu2biv/ZEtSvh2fa9KwN/jOGxY1bmnHF6Dcpy1z0caSor1Z3LcIyprOYCzbVJw==","shasum":"2027a11450ecd22dabe01afed32bffe0ebced35e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703134245.tgz","fileCount":713,"unpackedSize":1695891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO33bCRA9TVsSAnZWagAAWYkP/2Q/0BxoUQonFA55ApQq\nU/5J5fhfyW8DAzJFrpqgzDxSmksZZHRP3vFw23M2mfgL4pFTY9vhqHjHh6/H\nbiaVJL3AxYvTLDe80KN1g09VE9aobM+c6IASiDHYyVAKgk/6tk1nxCi4v5k0\ngBVpbsmlaewOLCbhZHkec5auVOQEqod9y4gRq69BY9jd4rIo1eMyPI1SHkLL\njhvgPxQBjZBcjGdMNOWdfyyJ7URbWUkBz75/EkVkJjsfFVhiccr9KfQhXprg\nwskOwBeNWxv3ECW8iKUc+QJcWh3HKchZcEExhjZ1/k2MC74XQR04pIoHYKVZ\nqCo1Kpmr8+igD47oGAfrZV4f/eO/1nOcTyb4+3Io4iCzl2QQl0LlICBOv0h0\nFhBBIi5e6ujQgX8Fhp/By61nty5kLAJ9wdssErsp2odL5bPAZCrduBsio+0f\nIv/WW1VD4f4OKj7YAtf3V3V0SK2Ooa9l6JRO9/kHhvBR4V76md5/8et93NJG\nZy87P2y4OQNWVr+HxqLxvuzGktk3Zjb0mBxJ3CEyJdeARBJhRDaM36VqWTzE\nQGBKZMa/NzBNUG01hAi91u+AlgR18EuOLpanokt6NMNDjJi0eoVvbJGn2I25\nrPhRdGxZSuXf7SWlPgbFF/QOmWr0JCMIjoxFHXaTfN8JIzIowxZ78LsKkQAg\nQ3lE\r\n=rpBu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCHdFMDbIH0mOW07YEWf5xyr1NQeP/5MN9hgkdpKJ2hmwIhAOZvFStNcLO8cU/Sm/zPzUf+7mTDf/Q4bCqkBnEYJypQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703134245_1530625499063_0.6198532363562104"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703140257":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703140257","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9de424b0f04c60211673e83c496487f6ce093b0b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703140257","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w3+sbK3W5vISJj5t/Py4qOfB0aZwZ+quEKswEloMHOxCePkrTk5oM7CUhx6xsBTERlIMsoviZYigfKaTY0uOrg==","shasum":"f4854eeaa75f1ca4fd5852fe7c58683280fc5983","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703140257.tgz","fileCount":725,"unpackedSize":1701789,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO4KZCRA9TVsSAnZWagAAbEAP/A6Krqm5xbGgMdsicfPd\nKSrZtlHd9f3DsqVcK5sYsser3kilpAyG9Q7d5Eb2jSY0ZocMVXI3NN7W21jU\nze2Ya1IjvEnIG9yU8wtLiGS6XqNp8Lne0UmQ5+Qdom03asWNloe44i+jDRIr\naoCb7bItNlMuYzmClpvq9bjspH6AaHCqBR+cmOZZmZx4FNuf01QODS0a9bnf\njt3g2d6LVyQv49WUK3IETnwvO7XIeBchnMWo/CtHAHn08+Rsu8hP7J9fzrLJ\nTzAOfOV2hVFQYH4Qani05nxLJ9nv+iFV18ma377uYTkrPsC4+xIWR0LCYQce\nj3mr5uBhp64K82+H4LpBXyqy/at8WRqFsHy9XGXXVGXSVAZv1Vv7/48bH+AM\nb4eJU2HqwFUlFhYQ9N0VI8b6ddZQF99UVnhctq1/xieMRzRJlIzLcUji90GH\nKdyyw+EBBpRhr31unMeFZKOMGfyin/sOB0/a8ydBclJj13nLY1dmKGTRtzBc\nKaXCbE4ThD6scLpWd5bd9hasSBEV2Cu0NIicJQdqINWf63DwQs72TYXdycXc\nDNn2ZG8BtI/gW2KUM/Sdzc4C0Kbyx7zC5qfqRgieA647mjapY+eZRWCeGt//\nm9GEDEnHNQGUZNiYh5rlCGOMjXZQWi5aTi6BP8i72iHQNlMJ+22cT2Ei4ifA\nHGC7\r\n=T3HZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDg25YkdwXL4JMI1NtqnWKotIG2cCPwWeNBOC6oMPzWvAiBWm0VD+5VxbtBnYnEu8EFEX96WtOpSF46UO1Wd7OcBfQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703140257_1530626713726_0.2853553482417299"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703141847":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703141847","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b587a5650a4b68ddd3065c8024a8dcc6e7083431","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703141847","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zd+lYnBWBxxa7z8EZCXsY3hAvqpki7B0zSspIF/ujwUx6BpE3gch4HzeOz53FRpleTnhL5rjHC1qWZL0+pHDvQ==","shasum":"954cc233aeedd0a5ec18608f2f6a44e5f548237b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703141847.tgz","fileCount":725,"unpackedSize":1700973,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO4ZxCRA9TVsSAnZWagAAB/kP/1hTmzCeWEc49hrStyoc\nAggsDdz1jXuN/t5mZWkPfFtEP5uBJzrts+UB7mg5owr1zQQWMiY2L9Fb8Tjb\nRwYmffILWA0UyT3OOnUNL9H+Xt3eZHKsYFaZc097d0RwXqT54ljJbOiSKnYn\nmlwN6QNd5jUgsZ8Oa4vsXWGObSNVGYk/Sqb/Yoqdx55W02qowlSl4UQ1AZpA\ntqRRgc+xNSssQHHxuWVsSJdEk+6S710sg8YFA6dU63O0Ghcfh/OWY96zvsFt\nBsx9fbBw+V9EppLa32lEBvMT6N+B4L1WJxpDCaEYT9ooY0uyBU/VUe0EVm0I\nKCMKNi/gvu4pV4sRpHdDHTMSOO1CahjjAbVTjFVRXM9n2KSO6BtyJ/Kpo4nx\nt9YVPWxoWoiVnWKx1kvYUU1E3NbphVy6UGQkUQ9tO/d73x3aEMGucgyzTq/X\nSCs0h95OUa6O8u35Ruz22CdH+iIGKxXaIqF1EDPvr9mcXxjKOnpgVuWTBKfP\nerhv4PqA6o+KnC7vjccNOF5xjFmX/9KP8LjfLCbs2+IT1KDjlII5kQk54FV1\nEb1lZTitHsb/ttIHp7s5qGGvCMOs3lmvA9zUNlbbTAeGMr8dp6oll+n8/UYL\nPfTc7t903B31NSAC0fT14/IPPlk6EclQzf7tYAfYu7O1AShYDt/9CSScWwHo\ndaGH\r\n=gAtO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA4H/9PI6Ir95Y2UJie19WOiquGYykjy72hJb6jLkPedAiEA77ERmyEra4VUIfC2se3BNtS/j6rO3408iOccjSnSd5Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703141847_1530627697103_0.9628635288070144"},"_hasShrinkwrap":false},"0.3.1-20180703145217":{"name":"@atomist/sdm","version":"0.3.1-20180703145217","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b3aec73a1a2851fad16c5ff4dafacdcb235a0224","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703145217","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QEAcEqijYKp3Y+rY8XaDceo7WOKMLuDdabiDFoywIxioUefGnvNVmxkRxLhR8/2TJFSkzwmRLyRYOul13jH2cQ==","shasum":"0facc0fe19383b2d4b32b50ae6a2a29da2f15ebd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703145217.tgz","fileCount":713,"unpackedSize":1695848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO442CRA9TVsSAnZWagAAnnMP/iYeMZEQiyjfhIDURkez\nOGYf3U8CLNAPhJFqECTJTO07R9ThhJVj1HbmogzJKD7wRwJOwSWehOVhIqZR\nzYXs6iGdPB1EQ29z/SOrkq9nnt83XGW56t4mAZ5OcT0GaBoMYNUWR47OKGn/\nu0QDC4iMWGeIbPLfci7UV4aRVoheDy6sHos2CV6+4ZpOrX5JbodDgOPx7BLo\niKpJhgvOljvwv33A2uLRcNyLbK9Mvn6YUBUzsI9fsF1+7HHZfOe/5oxYyGA+\nnj1tKW7zVaz5xr5Vdo3r1sS/tRUyfXcw8nBC3LcGHZ10Z59EPwcF6zXCXhMc\n47hdZYBgWZ045TaMjuYJoevHcL5n9iIj27eF7v2nxU0Tozc9PJvCCP4n8AqO\njQgw6Nx1mSnsYvlwzOkt6dbHhG+MPO+pV6jhSp1Vs99F662DyFaRX8KG0NlH\nS1cBLAR4SpJyxaeSdubFEowY4u2fZR9wQWC+LuNikNXStYj0ZFOdQmQhlMDX\n6E6HmQKoRcQ4afOWwjHCaIZEf49XsnB1Cy2ETCwXV7ptt8KMWaggaWwRj/md\nuyEY0EU3w0Nm0s8VxmoDMYvdURFxw9hWR+sAcNmuUVitWhzGPpmi5i8ZdJ2a\n/8m2r1Q3JaOzQC7bfrBxjzCRV8rNEYyPahflsdnNiZ/wTDNwcQJIxEtCFn4w\nbqbK\r\n=+4Gi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA/tyWiX99xNccm+f9UVzkH/hgO+2yXA2kzbAX5rOeCHAiEA5Dtf0vDOBc1ubH0zYi3YbEI5gyVY12IMvXdUxiHvhy8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703145217_1530629686601_0.2778583096033904"},"_hasShrinkwrap":false},"0.3.1-20180703151658":{"name":"@atomist/sdm","version":"0.3.1-20180703151658","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b3aec73a1a2851fad16c5ff4dafacdcb235a0224","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703151658","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fdVkNMgyNt+OuL+jwkfLEecejYHwU8ZNVQs2wJy1jdU3uwfwC7bSbKwmRI59Iw53KarAAAG0XWkEUAixEwqZ0g==","shasum":"155cfa3f89572efaea7cd8f2ba13552c1e0aa300","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703151658.tgz","fileCount":713,"unpackedSize":1695848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO5Q+CRA9TVsSAnZWagAAWpgP/jzsXpCUW9RdKZtYeFV9\nxOT972O+LQTcmi9X6JSPOe4scI7LLucP5DHcZSRhsuowQwI8PIQ96xKA5e1b\nui50BnYln9V/wPy86usKzFeJ9AyyLKE5RtCWTrziQn/RYBTZ6R98MdfTxzJb\ntKPnPraK8WiTSPqMgISCAJxQjAyp4H3b1D4Csn2tnh7OKqdMQYBA759v3Uw7\n07k2q4MitknH/EKG8/JHtP/De8pTgyZS9YMOD/hdTNjbVJUjXiIb+lHdvhKb\nFY1X/0KP5AzzrNRFpHnWwdlYYc2AbZU4HVFEyTzcq3zLJ1BYdn7jqU9uod2B\nafWlyOa3fGxsr2yZgtVLV2dd3vuVZwkVV6PI6vopX0tUpAV+shSkD8tNhO/q\nCsjc4enA3tGXjlL+WcphQX4gBPGnz+j6w9U2nGjHxBuGHksQxCmiAh/aZJyC\n6Ox+Ya2CpV5R9ZebmeT3hiy2ALlZEck6UC48655xpwbpQpvyEWiuvE+3KnUv\ndJF6zzeNf5mNGrNxBgSsifcllHOylPWjcoTnFKz/gyMP+Xc2Qe7T+tQVYkgs\nBh3Aq+pstmWZ7dDSVdsnkpY8LIngL+yycIZ9x83Xz0nDLEoKJeZu6FAGstMd\nL23IQf1EQNflIzyoWBBeAVBlUrLGfEseqWBH2EaFe8UXyB0ebG3Gk7xN36/G\nh16t\r\n=nllK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCQqaSOuFOEH23qiA0j6O4MzoVy0z83R9PD8GgyC/z7wIgaFrbc+Hl33Hqe+6InenXJmNHZ/EsCIOKudrdANlNKEw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703151658_1530631203080_0.3967224720285989"},"_hasShrinkwrap":false},"0.3.1-nortissej.deprecate-status.20180703151900":{"name":"@atomist/sdm","version":"0.3.1-nortissej.deprecate-status.20180703151900","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.17.4-20180628204109","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d54f1e79ac96a4194d0be5a0179f64d11cda9d9c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.deprecate-status.20180703151900","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0GzVPPKaU8QRxRvu2KXptSYCygHCTomRr6woryd0ff+AZQRdFOGjvcesdCsDQouoIsLsesiut8zxe/j723GD3g==","shasum":"fd6aea2b9736945a843d7e5dbcfc8974fc55088f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.deprecate-status.20180703151900.tgz","fileCount":725,"unpackedSize":1701260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO5SXCRA9TVsSAnZWagAAsU0P/2I9BR8kbR0QLhnnE6If\n8n2JqD9H152DcSs1s0uHZLSFHjHA3XLKYQzyuy/sDf6deL3Y4Wa5Par98TlH\njVCMWRKhMHT6oPm5/bh3wagKC4oCa8DtSGMWn2yJrY8wqIROjN73i3rCgW9j\n9tBBit1CJM8zZnONWTJMF3CQdScNcjeXmWHCK6zXf7jSmWEfukDIPAUCHWLi\nebknp8+H1boJGvHom7quGDDXtjUWjBtJIILbB1xBrG17ev+sKY5dA8QhhZVs\nTsyM7ievTsGkBnnTgLWEtnmZeedh6xv4+EUvc2ZwRCnbGNZYCZ3TLPRZhBbJ\nwHJ629IOMDvtjbn02Dg6UNTrUtHF1BDu5FTrHMi91+Gqwvm4cuMofV3Pirw9\nKsiN51LG8Mrp0/SL+Ven4xTfxmt9w2c3lYHBODi83enzJhSdaMW/E9Xzv13a\nPFyGkLmH4gijEMJDIhAWhJCF1gFIdIaWS/YG4YFiWblhuNGwe9e8bfuLtTbW\nt0u/OaaAxi9HO04xNUEQerEJtzzJ4K05fB6O6F68DM5SEWzfF4Y+ESSda4hN\nlPaYXgfjPEcAL1gFHaIMIsqvamftJErD7wprChUI5I+dXQJkY2oFf43S+CAJ\nSFbQeUGZCJUfhtR6COMirDY1SXx1HoIXVmksaLjo6tav/epDuYoc1wZpVhCK\ngYpY\r\n=cTWD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAVkTA+NO55OMvGqaHUD9DKjb2aOMNm3XG+bbZdPqR7ZAiBryZeN6EEhUb+B5ZK4QcXwE/P6BRZCjsWThvq7I0pHng=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.deprecate-status.20180703151900_1530631319378_0.3426363432694832"},"_hasShrinkwrap":false},"0.3.1-20180703151953":{"name":"@atomist/sdm","version":"0.3.1-20180703151953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"54a07f64db96664cd9034494bc3df3101f483b19","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703151953","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7w39W060cjyT1Z9bykSlUZk6p1kG8OVCh2v40DMmnN8pkVTD2zJqSRnaStOvx/ffZIqPpPAZPFRuS033xrXniA==","shasum":"52685aeba29c48c7c36b5a600f15f2ba9e9bb4cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703151953.tgz","fileCount":713,"unpackedSize":1695862,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO5T4CRA9TVsSAnZWagAAIigP/1L9u0h7kd8+SyyFHwVs\nIoyMTF7F8/QoTlkhukEhelCSPAO4pbNjQO1XFiOMeZfSvXobFW3ZAM14XTNd\nPMhfGeASfAwFCFYEAibBygjKFQKVYlVJCWBRntjtRFQqa1wg4NOyvlLrTWbF\nO5vKiwqP1jafJzp1KCljeHh5kHN1AgYS3DUhpQs9xKtE7fFMbG4hS1zlDIYJ\nG6KErxVddBM8P8Dk7YCKJLn/lyo1u5XsTfjelxsM6pXAK6jk+OcgtKrPWa+Y\nqSbjnzHXPM8hh5mhgfKAQDTWbFh3836oS6ihTQDN9Zh+E6W9jD/FSXcdAR/3\nYS85WASwNGFQ8oEkyZ89ksmubTLJfTbgL3+8lzSo+A0NPmSfIAfsEeJWyA1G\ndgq69qx1YcYQuRxcrny6vZHGUUreOuAzNruvKtfxCZ+1cT27ROpeqo6PdCCs\nh+t4qO4+sPUebvQ6eaq5TZDugRGTtfBfkCaxQk0OzG8+nE4fgNLRT3opvqp1\nWk1mclNu1xt3Bh8H5kgMmYiAIplJ8bQx+e8+rrezfg7edNVj0rmXIzekHlTL\nvt/o0eYyBCMPxpWYrX0FaTMQ5mFdCGZJ8Hy8TnQOtkm0FVCpiJCgRco0AoH9\nt6boyZNBUVi/8IrGKVfnMRjsWSJzk3yL84kWkEztLoB4R8W5R2IhDarADTUZ\nDwcb\r\n=ve1r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEKI2NqDxthsY7+/Mh17Lk3gwwML3xprTOdCtLpmxjgMAiBC+jmb+oT9Ze0jAfJ+o2gvf97jwwpskV+s4naBeoW6Ow=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703151953_1530631398307_0.615735701579704"},"_hasShrinkwrap":false},"0.3.1-20180703152732":{"name":"@atomist/sdm","version":"0.3.1-20180703152732","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"54a07f64db96664cd9034494bc3df3101f483b19","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703152732","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QIvnyTX8fZDb8zNCe9bXRLLcXwRxDdK98S9G2pH3SLXvM/UsdB+W77DW90uKPnIO5Awusxa0eBpZ60R+Iw74FA==","shasum":"f300bdbed0911435e61972eacae58045f23dc0b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703152732.tgz","fileCount":713,"unpackedSize":1695862,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO5aRCRA9TVsSAnZWagAAAAwP/21/mlAJ22X5UYRKi+AK\nz3b2LLvklmfXSXZDrlY7VS2i0X9FvhQ+dxEkfC6GCiYxBFCnb0Svjyop9pCO\nNqpHDgSEtUOgu6858q3qcDjdlG0y64ymvlHhjN6eX1fQIl9oiETp9qBfzhnN\nLhj8PVq0njAheMoLITSjK80Otx6NLy+KvBd5BbIOb1fctxWfL70X4CbvLYJH\n7oGus5jGwx3Qsd7djG1FGR8qhfOmQsr7ElPRyQGbbu7S7nQ0hNDfpGm3KkGC\nA6gf7Y24G84ydjyebocyVCm+sZAo/hAusl/CBxLw2LpUuCvEYoxXqz7Wv9jm\nYT4BfwROGCa/LqaVQmMN9u5rKrr0ttd8bYSI6vf/iFCuAG9qGkqvrTaAvL25\n+vyTyDr2fteLpx5EYCXdmzkTJeCx7FyF3uo/4ZTwzwacy9SjKEv7QDAFjjVL\nfDiQqtDaiB+1msC7Mg7WB31s4FHm8eTFY6nQoqG2qMTLd4d9yCsoKHM0UeXX\n6jYqTPbPdbIl4LxEwRWlQ0IX4rvTV0WerQ80E1nKOIRAg1W3ZSXUzN22eW7V\nrELsMSWZdC7bxcbH89ehmtL7gMZrwK78HZ8O0NzW6unbfVlqPjseVpZkStDa\nC3BEAjvQCfUykt3Xl/bcCjI6HAjSSJ12nIkJ1PV19qkxD/Oj2RhTHzUMBnkK\n9M6q\r\n=Y+7E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsn9sHdA27rkpBl8evgkO3JYywvrRpnJlUa0C8AMQ7zgIhAP92/i64NaUyA5RBwUzkZvKSj2AQ+fX4LWy1y2WpVmKm"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703152732_1530631825246_0.7720596832350162"},"_hasShrinkwrap":false},"0.3.1-nortissej.ES2017.20180703182321":{"name":"@atomist/sdm","version":"0.3.1-nortissej.ES2017.20180703182321","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2cac1ea8e081893af55beaf2a376e44a760bf4f8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.ES2017.20180703182321","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-amzTxNaszB5g7dIdUW8nheeqynWD9uY2JlpL2MbMua5hm/bxTuXXtb3lv6q1nqy0JbHA0AGJ9/rNHGl48SxvRA==","shasum":"9b9800fc0ad11eabf792521148817cfea0c9d7fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.ES2017.20180703182321.tgz","fileCount":713,"unpackedSize":1657030,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO7/ICRA9TVsSAnZWagAAqeMP/jf3VItIT6LjKLrkPO8d\npuHOJb1JnTNTBZGGmJ3di4QWPBVerEgTNa0PQLtopj/nizgBbt+YlBDSM8sT\nJK/nvdfUjYV+4D20cgB2sgWXnqEd8Xk61/Hvu5+HR6O/kUmtoJjPZBHL0M9+\n3PT30Rua71hp8SwnuZ3m8ftNjLZ8NAAjr5tf7E5oGfBYlyuvP9El+rjVFImv\ndhf7rsbmq3fVyIZJxDu0ZH4j+YHFgliN93irvePKhjVf7nQieQUe9qkRTuvr\nX2VtY35zKxXNXqDnmZQ+WNZ0bOJp7qCb2laQSWTFUO2T+1KxypHMlEp8W81n\nVU4XCq6wKqboCr/0p0D9iD8I7mfrP52ayliluFe4LY+b2fqP9DGN2SvgPEfD\nThMd+hkQhU137ES2aJHnwpPnWs3fEyktCqp5BorvDdPox61/M9aGFSd1iqYr\nrNnQyQ4OepFKWYADAz7NdOFhWPRv3WqdAxa61BS+t5AvwgNBI42kXjDjcuYt\nMIrPGdI5+CulZlQzR0/ToT0RFKJb3IfpXzLtkHWoRGkelLwlr0qT7Ep5+rVv\n7dUMT0x3MiG8T8PWTTfzsIBR+OPhTKbo8TuB9ZzVIYZIMqmhgB1i8ZPHcr/C\no6PH4TXMoA2ZNPttoOFHOCG+F4Emxxd8jWrVAjxH40qEOXYQ1OJJHCDFyoUl\nct/o\r\n=jklN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCqwGAXfHicGsfoZZYRh/XfrgYLSW7QjGBWMg7mCCTPPAIgOZ4vsFl4oNw2pbsIlR35ac44cBv7ZXaIYR9yxPeKHYY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.ES2017.20180703182321_1530642375672_0.2620019201861703"},"_hasShrinkwrap":false},"0.3.1-20180703193445":{"name":"@atomist/sdm","version":"0.3.1-20180703193445","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6932e01c840251b6953df291b40f965b546cf414","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703193445","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TNGWozDM6ug0lQeMt16aeIrPhgcKellNT4EdUpwH0bRX3Vv3KWPcI5M8GyQ20gmTJ4AkK36N9L0ty1Md4DAHlQ==","shasum":"251b5d5b27202eb865c988212bf09d3d7a859e85","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703193445.tgz","fileCount":713,"unpackedSize":1657013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO9CXCRA9TVsSAnZWagAADBIP/1GzcvbK2nAQ2AE3Bfqk\nZcx+GFejh4XBnmij+yHLGGeWVgdx+i9WlZ/0xXOSEaxdprCsXq9NakCaD8oi\nTNopBJAG3M6nHiMUnd8L2e1F6MVdziBlDs6mbFSOU+D7LIb+7We1naWY577x\nSXOOWYQI/LlGRS7vzOpSrfVvbGsmZiyfkFZS1d4v0Nj3eZlKEv4dccnCxKLH\nGHcifyDngCqiAKC+0/1ZyxlzDBaoVjqpX1bc01K/r1MVCyY/+tQ+XXkC8DYH\nbfpt6Fx/1DnZa1X6vFyeoZwjFlQje7HNannkjItk8eW4RBV53a7+QDxTUQMM\n/m0QomBXJON/W/MeGkEZHmYpwuOhmtJYQ0qalJAL3P/XzEckQvKQBX8GLTdq\niSd5zCuEi3QlvWqArjUEQDsl5MyPLKOoZ3DBe1WdDX6+SiBG5LPYN2KBBDbr\nsIE6K3CPHhh3WEoyLIH3woH08v9SiSv/rlx6Wkqm0tQJCkGnd2DkeEEUtecd\nQtsuWLj6bsPnC6c6fsqrGAjGK7n762Ks9YTkMh7IVc26NuvFAmjb81458ja4\nhuLJx4zHhAxdbg3pQmqvSJX33E7TYbvYxK6ANzr9CrZQbwl9yPxNVIZS7IET\nIzrgN9aeYfJwihs2zFJtUsS1p6fhZuMraJmSiV2xa6i/4Nvjibg7UajZ4gPB\nOpkj\r\n=hNTB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDTMh/rXZOL1OB7W8T5apUHgs1KyNpCcER1vkWEbOxswIgIa1gVMlTgGQgFMRFxCcwgrGzdS3KaXoRif9PdWfxEuw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703193445_1530646679366_0.43683288494029915"},"_hasShrinkwrap":false},"0.3.1-logPushTestResults.20180703194659":{"name":"@atomist/sdm","version":"0.3.1-logPushTestResults.20180703194659","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a63c1b7630ac6fe72af37ab232648639fa946562","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-logPushTestResults.20180703194659","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-exfpgIs1eWOb9tEuMT/PL/I+RhslBwLZFor8Q0OOKzEwrwkKDoLonPGbKwnb+wSV3rfw85fREUYAGAPKiE1G2A==","shasum":"0b98a597a72bd0c16b586876e03465bb46294446","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-logPushTestResults.20180703194659.tgz","fileCount":713,"unpackedSize":1657471,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO9NoCRA9TVsSAnZWagAA6ewP/11ApGJs3BNWRT5fbA/r\ng9htZmaFx63OmUo8g8FLL0d0kdU1jg3Of5cn5dXv0o3rWKzDNEg7YdGJImbc\nk53W6LEUgHw+FC/mb0q6OHBapE8VfrL70PVfVXiaMRXbKeD58J/aQAWIgMva\nH6tMzuE/nKg42Ixn9EfKwMXn5KM0KcKdlnaRNMvHbsKV3ePDomFjIIDpOQPm\nYE4Mp0eAIFa6e5LWShP3bd7fLh1AY+CToposteDdMgo7q3pR8egP3xG4vHoQ\nUOXC3w1iEL6p4kmnzoynmQ306pBIisHznhUK06PR5MO37zvCso0yof2CiJeu\nFh0ZFsNOBgdUzO0h+iK8M9CL8Llda2hu9BF9jX/tgpDDCzwpTOGxOkSmjfRZ\nhYHtQBKCEwD/LQIzYUhh8LW+dHJczEIfivVeCBBE8ATcJK7dAsX+kkdKsfiS\nZDvQ6fFHhZSvDIyLiP/iu3fNFC7JQDqXQQajOENsbKDog3YRYUGWDC1J3Sv1\np/ma0fNSocy648uNn5+r32Yk0rKRq1R3PFmfNFGUzg92xHFLknZn7NtJY4xH\ncXNOGlE3Lh/k/oDWV7g42oQj45Sv9TmaBCAQkaQzBfKV1u50h3RP22tLVt2y\nSppmPpJdbU91c4tNqCryyfCs+6PZns3i4G90E2+rJDXXpp7yNX/QyM5sZZEW\nO/yT\r\n=G2vR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWwE6ngXixoPLDTZg9dmZtjNhdMqQSqeCtaV0R8PCqYAIhANkhHKvMxiLAPMLJo/J/RtqXYNAJYoy/juoXgJcLRP/p"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-logPushTestResults.20180703194659_1530647399854_0.8288317001681731"},"_hasShrinkwrap":false},"0.3.1-20180703195415":{"name":"@atomist/sdm","version":"0.3.1-20180703195415","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cd1c53c9d87284f131ff98a1d4cc653ae5cd6bac","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703195415","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OYwBFWOlwDc0PdauuI0ZaoaaKHJSmCV1r99dKE9RuDpw0FqX563PiHbpyn/qsg8/lsvbhct0g/FK88KALIl/tg==","shasum":"5ee7745c402c171829be1ebe61b069c99dcd8d31","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703195415.tgz","fileCount":713,"unpackedSize":1657452,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO9UlCRA9TVsSAnZWagAAa/cQAI0Rjo9kPeey/cpfsdxf\nCnTptnajymujWEugXoUwHmhnUxm46L1pNH3vwKSOSzyWfBdH2Ba/J4z0qprA\nDqYlE7LqwVPL5ohi+Ji6sl2uG8lQgZfbyIvmwc3LnV8n+0Weq+JrtqgvtNdy\nYYh40sUAMt0DV9Ko9tV/wjygOQmkwGZKjsmrJ/AEJIVdL+tPXSitzW3bWwoc\n/6mq9H3F6I+QmraWtb4t6lt00LUKhcvK6JqSP0ujkuBF/NEhxa1sdcGFHPuI\nCgPRZ8Vx2yD6RRGa35HYulBSWs+aQ8GFWCYxaoWzd21fex2tpaPrWeF4ybrc\nORXovxfQyEp7NOPRfUW+SzfnsjYMmagpsWL5xQVs0oHMUVSfh8fXVTSGMaLK\nw5uu+XL59DVhQGMNYdoTGXp/N1IbRqrHZcVpe7qjEdWP2ZYnJg8qeJ5gyGms\napqoSQMya1OIBcE2GXvWq8nz3h06W8JC4T59rCDw9Oz3SoimFiO62kY+8ghu\n4INgmuvNJjQRqk4EgwX6t7Ntd/jbr7SDiQUWldXEY+01Jb8CKuaAyPzO7mfo\nYN0X1riv7Ywe+skV6P7QrAuzD6v3KaI37GECp42ylZBLsfNjHZT4FKVwT0WI\nFaRwL8JVWhHjxQo58vP3TEhIERWubFJgiDKLjBmtZKGY+1YRURtbggwsgU9K\nRyP5\r\n=D8eG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPx0r/+x1y7CtaFOXKXxXjY2sqxIkI0jgzz0ZHQj7WcAiAQ3ls5umwiuvBiyd0YqrbcL8XJO5SIds5UVlMZ+wmFIA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703195415_1530647845772_0.22004768784793027"},"_hasShrinkwrap":false},"0.3.1-nortissej.log-caching.20180703204622":{"name":"@atomist/sdm","version":"0.3.1-nortissej.log-caching.20180703204622","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"96a482831870b5d361cd9878417aeaebdca68ef4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.log-caching.20180703204622","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1yyP42t6z+d+h9RN5C3K6yAOAXMpG3MJ1iFGnCq7U928xTaTmb+5AStHRl/aPH7jAghTTllaJ9KSUq22To/PCw==","shasum":"48d21018592b8d4a5f870713615ceb7063e443db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.log-caching.20180703204622.tgz","fileCount":713,"unpackedSize":1696180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO+FECRA9TVsSAnZWagAAppIQAIBtQ1hUnWdnS9PK0pOL\nE2xW03zfHaYoMztoPEZunsyaA6B8s80HkkqkJtbR0UhnESgkC2z1YPg2GSQn\n3SXRxKnbzCdbHjQ/gS/DMObbRGcE6hgZSKMcMXNCaokpxwQr0Yq2Vnex8x40\nAOOdIabBHj1/c1P/yKr6oHZEusaDUrk+TsFUVfoh8lXIBwFHnAMYFTr0mCDi\nyb47W3l7UIPcj0j1LTOXigfYyEy9LPizBNPWYGiPZ3gThJy1LM3amXd1TRBj\nA9/n2fqLOA88Gcn5EGTXRKDyPLVmuRMnirj/mf8XtigEn0lFSvExLHcWSjSW\nKMeOfY8xq+j9e3SPt0lTK2aSRdDcFN2gmj5Zpb6P0Vv7i2FZ9CqpPwGDgCna\n92kOpwBXjuwpNMB9IRe1WH2L+rPTCQuyW42ZbYmsNdeHmmLAxWstXXPwBAdy\n/VK4PLXNGMwk+Ud3LaoXtO2EMX3VxyVQa80cID8fMeQqN7c+8WiO49BM6jko\nIcK2TxRjyni9PnkIdCmXqFLFo0zIR+QScvOVd0KpRqT3KVll2JHKheZ1RqM+\nRWjf3tqj/RZigz6591+dQqA1pBwf6DEWv2CdTgjj+sthfZcWmK86ocFmQlCc\nJ3q9O30q7byVC4xifQchdkTXrzQjOD1qPc6NozH1R1JvQx6OueG3BrF8T3/j\nE7jz\r\n=KY4J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjvF/iWHqe4iK08KDXEtlhNW1ijrLKIOyWje0QrNGawgIhAJm8ynUUYQOg2VDKHwqqR/j4XSXAmMPdl2HfbmMGirTb"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.log-caching.20180703204622_1530650948816_0.35703301459434544"},"_hasShrinkwrap":false},"0.3.1-20180703221152":{"name":"@atomist/sdm","version":"0.3.1-20180703221152","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7c1e982ec0da96f5a6f88440f3ba471c8801d659","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180703221152","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Cl0Ap2JJpX8U8fzsfeDztfEWty+wv6O5qE/YAhtLkEkngS1kWAfY49WggoxQ93aJ7ZOsYO6jG6eK6NinZszeTw==","shasum":"b642c1c4fdaf00aa5f0e1172641b2710f4dde54e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180703221152.tgz","fileCount":721,"unpackedSize":1664855,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO/V9CRA9TVsSAnZWagAAdmMQAJdkoSLVQ353QdUf8Jyg\n2HZ7fEbRhJfuSW7Yj6LhlceK5PzyWY2EfgGwRRdRaXZOyYoi7fNdgUA/Zcyu\nFjznIC+Xpfroyh7/c0nioEFGIcDHk/AG6H3lo90eF3lzRqTQKEmNqBeE+2YU\n6LZWJ+xsvKi6nh4pGyF3s5GvkOmSYQpYpu+/Wa8ZAmsCRyFKbicrciVrrHdk\nveGPnipPEj5Rz1LW1+hG0f/DWdcFIlmW9Trya1dVaJ8VJKNYLDEFaZbI0EnN\n7ufZCbO+VMVXL/h6k+QM5zOfCKmWa1qEdBTrk6G8ZO6nI8WsyGXTj9Tre+Xk\nJ+/cWTmZYBcDhXLD2+JEn9/2lSqkphTUgeZJraL4XKouUORxNgUHbxyfnq84\n0caovpGs/9fXGns7NmQgSgLabekFFm3pjm9RUWqPr2sxVM9fOVFULVvNHkrW\n72BK6SoAsFtylONG0X7nbcx28BVkEKE1d0v9L1GnW/9I9m0MXeHpkMkOyBQ/\nd7vMXby6axHmuGt34GaqI5E8Uw9QF0VWw8e8D/o6FupCraQNiLWU/o4kWOFQ\nexHVN15aS+jeEsZp5oSuhfdmwDqLp3n9lNyRQ/b+K9NwX34r76H2z8JHaXXt\n5dxY+QYZnqqPQ7gWAGFKocwyEAJS/rD0fXY/hdpO5bDpYuQQ4iSa6WhJ22Sr\n+nm9\r\n=BDWA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBxgXZsuGo1v+fca/+bQGs04qqfMLZT1xaVy8zvAOM1EAiBhrWBXb5zfuLkSqvpvzvyx+OE2EqwqlfAOza8fsufqtw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180703221152_1530656125349_0.956709757219596"},"_hasShrinkwrap":false},"0.3.1-nortissej.manual-memoize.20180703221210":{"name":"@atomist/sdm","version":"0.3.1-nortissej.manual-memoize.20180703221210","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels  compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ddbb9ee37d4aeb9fe8ba4c0ef5107307ebfad579","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-nortissej.manual-memoize.20180703221210","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oALETl1w984WDgMKC09xVs9tDeEN7NzEvn8xsKZqe0tlWO1l1Nkry5AX4p0whpt02hfTw3kK88xE5fvy2fz/jw==","shasum":"caa267ae40e3ccafd0606fe6d5239ab32ce9b683","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-nortissej.manual-memoize.20180703221210.tgz","fileCount":713,"unpackedSize":1657657,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbO/WECRA9TVsSAnZWagAAezoP/250SfcttsirYxKqsS23\n1t7mvT3Fjf08/fK+rmJpZD1XBuoPkeSC56xLzr9lvbFbWJvYsSaJdkRpE/Ob\nCOx1ADCO0mKYD9RbWpnynzivG290qfNqhUdWmK0tUExoOIepZIIe7EOQjXam\njGTW9BFXLaPgyVZFGEMyxtBXjabthSSlmPrEa4IiiBbWRVqHczm3I48tL2s8\nxLUokFqlFrrTS6Kv0JAITODiQzpJ3SUYoNyQDpFAYsDBuG0Fk9wNvDR2sNe4\nmXUWnsckPr4ZRVZYGyMpMYjupQs8jc0A4UxLgW4/l6aCoMg2STiwZZH8OVrJ\nIRd4MZ96FwENkE29t9GMbbomg3fKbOsQK1UbeRxYP5FSsYcQ102wBd7K4kyH\nVKerOSDTg6LAkpLaQt5WSSIz22BkWOVuFK/+jf3dBtUYI7zFE8Wh2VRi5iHv\nBqvyVc5sc+7b0eSaJoG/LK1A3G5tdo5HsuP1vio3HEyL/DtDYf9fmCA99nGF\ncS582LKsDAjHJik5tmyqvZRHdyoZGo3PpiBUesdKsvyqkb4vwV2maMj3Pcfz\nlopJA6iu1/ZlVwy0D+/6c/Tc+Ojq4mq5kJUxVuCSU/uorYUncvvaCikvneMJ\n9ccuNn1AMXudVR/Pw2Y9hcCpqWMowozhIzTZuoovm5KVfv8P9PRe6TNPx7tC\nVW8O\r\n=l61A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBEZXVfMyycaJBkilS1IgoSSBGQlYWLuUeoEnZQLR1UtAiBvbhbGfkXVZnjpSnM8C5luW8dpbxOJMyxISpeCHFvapg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-nortissej.manual-memoize.20180703221210_1530656132732_0.6825705075359156"},"_hasShrinkwrap":false},"0.3.1-20180704045853":{"name":"@atomist/sdm","version":"0.3.1-20180704045853","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5bdde565562bc36018476fe431aaa07dabaf4a55","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180704045853","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KWa89QYBeQoAdKqQa+va8npvPcN0sLY5045UhsgSQLZHE4HIanwNEtiMQ4E2EWsVhHjurFyhUab5RbQJBMMBsA==","shasum":"8730aaef40d81e071605c35891767df09d32865c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180704045853.tgz","fileCount":721,"unpackedSize":1665348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPFTECRA9TVsSAnZWagAASmEP/1gyGq2YzYd14VULYbpT\nbyaMOAJ2U7IimkMdD69YveKImERsmvybhCIO5UjoUs9QLSNY7bEpqDXPyP/J\n678exTMKqnqLCbBMXUcRQ6pJNfzBvIc2HMcwFrMhigG+YU+5oV2pxnYmAbg6\nTWiXHJxi/h8KZVZV5rFfenIqVFZ77ux4kmet+pxc5TMWCSswdsXW/l2JHhgJ\nVs9sGmSzzVfIw/TRabIvVU7NDHMgaUxQ/kh5Qnw4cJH9oU3jIAQD7RYmGLen\nKW66zb9se5sNLhPU7dZk4JqjEbLae4XdKCXsdVSyL000QMMptmXyJUuziD9f\nDmgOSeUNLMiAy0ABPkfNXDwnRAOojdsXhw53D+fDSyHN7tGXhG9yrjBGiJ2U\nzxWEUIwgWRla/7V7Q/gTDd1Ku66vZbd4sAhNjCsCWUBSeSxMeZtFWLwDOW+0\nAV07rhrf78DLDbHlfjfnXOkcEyCiMUh4ywljsZgtWbtenLR+6VPiN/oeJOYP\n63UqUaY7n7RgDcHCUg4uq1+imvrLgHzyarLNLOwuMfGmSkVe8Rv+29di1pt6\nTnZOgm6WXIj8F5+ZaqophkUTsX7TLy29h3RusXyhvOqFsjZxFjdZ4e37p2Cc\ngCTNgPDbfbG5LZlCDSRfC0jNqvka4PJD9RjWAD1ymyCFTi8Uu0t/j9sYr2mN\nT7XI\r\n=mSF8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGoXwerlBuuoYLqO7OVjAsBv0dIji8FurIcSS2WCekIRAiAR+fFxLUnE/Qq+5+kCS4EINUxmdBhpm12v00IYRWXYUg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180704045853_1530680516823_0.40200180983515854"},"_hasShrinkwrap":false},"0.3.1-20180704065423":{"name":"@atomist/sdm","version":"0.3.1-20180704065423","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"01d9ae308bfa93369dc448e5973079a66003e47b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180704065423","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-x6zAAN6qeEXXSt+TRcs1D+hwhJvx7y0Rxl9rFkM3go7V8kNJxnKwFdtWVVoaR9IGaK87hGHs4TzTykLeBZtvbQ==","shasum":"606b628e71c307b9cc2b8794f39d4795dae5bd00","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180704065423.tgz","fileCount":721,"unpackedSize":1665528,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPG/VCRA9TVsSAnZWagAAG0cP+gOZCqEsKuPqg2Vq1PYa\nAxLJ+UFWhMUh9EiuvQtcigNA7LarC52GTTf+w90sd+jDJzIJzrBsVHlqdm69\nzNQ8o49v+o3nhB4mrzTIOCtv6qgC5pVyciDYEJ2zOV9C3KtoajneVYxCpZhT\nceMBqLr2Nbul2f2p9anOZMsXeRR4ScC/IBmqwkOQ6eTpTAyJFvtRRbJoiNt8\n8Kl2QoGGRhKlpwAR0x/oOuN0hqwlt2oeUD13yD7IOYAy/UHG083RSx/4gZZQ\nuq3KW4aQoVmD93yx4YcvODOZnTusecmUSFUdK5/TC5ElbgDgj3WnZQQ5f7qy\nehECZJQw+xR75n9PsJqy/qeMi/cUs7+t40zB3tGdeFR1QDZoDUtxRrUwWTwQ\npM97rrmAQ/KygHzxEZ0IAtv1j5whmlN6mXjfUH+KherGZjPpMhIJc76IeZm+\n/M00hbgiTTC6zEAHpdInYTM4O7MzvLKc01pjKm31JulbweSvILMGuueCs1TH\nX9JZ7wnXMtcCjElBhYw48DF62O8VdJc6tzlvd/Mxe0Ne7G4+fQ5560KCRcAr\n+QWhqakq5goQfpSDm3wvod+0XvtrcTe79H/EqspnuhtmxZ5+L4YkPOuUdzWL\nz1dsAJWIczZZzykgXwQLxZSCQmx3DKCogVMchmJbbjakjI0zg+yXwfWs+we2\nuUcl\r\n=zfA3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVm4dL7sk9dd2Ltnjmon+jOt9GSeRCrck73+G7mI/EfQIgaSmcxwIKUkLdZCYayrBuUpK5qSiXzyZ6WiPVr+4p/5I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180704065423_1530687445704_0.547345769383911"},"_hasShrinkwrap":false},"0.3.1-20180704120133":{"name":"@atomist/sdm","version":"0.3.1-20180704120133","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eebde3375fbb159e694eb26a15ce949793332f1c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180704120133","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sJp68LiSStvUqMBntgTv3OHkxye3KMKdhz2Vz2jiyAmq54gQQAJVAlrImQN8Jr0hPG+95wnIoXdYcQR8KtYLwQ==","shasum":"08e20a45d5ada4d5df8b45069876e7548227f7b2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180704120133.tgz","fileCount":721,"unpackedSize":1665173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPLfdCRA9TVsSAnZWagAAuAAQAIZ3xrcke/VZo2AbYgd6\nmibz8KnUfVJU23olGiZrScaZswZ5JLQhNNJrva3b6n2xqDoIGjPWE9l9T/uH\nMtU0EkeXHUiAVmxSNuVh/sJ9XbRzk8uoeExiUdNgNvQYrogjGd14gDtgkSX+\nPGDum/zTsZWU1T3eGNKwZkJQTrJRzOBDF2iMSrEw7yKYgcz5ZZWvVhCt894K\nga5m4f2XjKLLdLO4nDLCCwENjzJJkBhInHZAoq+gdIf3DQuI28WjRPrcso5S\nOcxuLMnlXoLnk48As87NWBPi2BhCovNfDrMimdafKWdig60MKiKRCj4DpaHO\nUnVdZomdZAV/6Hy7Dsk45s9KeZS5O4j/EIsMrhyubWiIyjDV2Ix40ZN26r54\nucdW7Rx6AUd5qNztiP5n4Wxx4LU6iRAGIm/FwCueTD2N/YoDJm49P+Ibt2VU\nFcmYRQC7qs21b0uyGaG6NsjLSzjzk/IhjFNVs6/M4NPOwRg16JceLQXEn7C2\nbfuui3lc/mQA7KTPjkZJbr516gPcjGjo8aPC1yz3BWWtvV8VhENn7a2lWjmc\nB6ZmO5DsSXKaKfLLA9PdwHM4R/aVM+rRmo0sCVjbhNq2aea3RG2UadmxeU0/\nGGMWuhzzpmSF56mMl0t+0NM7Nwj9vosLGEsddUNLXgxBbXhDAroJsfrNK4wI\n6nDK\r\n=Wf4v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE9jUsoFZVebVq7ulzP+KdfQ84cGst3HNnecq1Ol6VP2AiAGsYwEHfrNV8rfc9/0iVf+plx6Kn2kaD27PBc5yfPdBA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180704120133_1530705885662_0.0023980568807071556"},"_hasShrinkwrap":false},"0.3.1-20180704121313":{"name":"@atomist/sdm","version":"0.3.1-20180704121313","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cb618fdeeea8b86f6fa3a9b3def7064f072355cb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180704121313","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mAIhZY+POGcronhgjc8xLDleAcut3qTVxrzQ1iwOwXy7MpigHZN/MGM9jbcv5VJ1ay9Im3tehMVsSIn2maep7Q==","shasum":"c46a0e424d6bfcdcff395112ea54a283a0139d07","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180704121313.tgz","fileCount":721,"unpackedSize":1665163,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPLqRCRA9TVsSAnZWagAAja0P/1BD5VnUC2GURVTQ/yxc\n5COb9IVahsBexQYmBFr5KWG5vqyAUYUpIk8BHRxoh5VQr6VnuRSue3XkWlzV\nZo9DJSeroZpN0auyiDbaw03gPgk7nzH5vpsLRAVGHOVk1HmiNUet/rzxWDqd\nNsjIfU40p61uGQkzkFG+6Zf6A1CBOJ70FB3TVsaIGJX8KbsNDchXgWHbj0xF\n87OprTf59uoOo7w4PwnHxN5D6f7e/sxfdhdjS5rI5lh+XZ9lPEawo6bhKV0g\npfi8SKB/W4isMy+P3GeSFaYZKTRRjpWQxOu8w+mwEhH5zJGgy6L2KMXm1s6i\nxeipNz83k72GQ88QWapDx1zC4Caea7AbbMbSU91KzjcyOA7nnHr5yH4MvmLK\nz03pMGrQa4rEd76hQTg2JkZzkBjlE5pLi7W5swp4EeqR9FvpUtGE7iI55heS\nlDwKjx9fRDOnwQV9yAQkDbn7niXcZd5S9lkuBx3YFijhi6K8GhxaevysC1xu\nL8eVEMk1FzA65UMHP60vtBPfXSzYi489F7sLb3zMw5MzgeCfr6aM3wLU1SHR\n2s168xbnK4acfhQJBBMT8d3zRM61B7T9ctN4IfNzrxtcgyeQ8XZlEyW4KJD+\nb5tODzIioIZVQZslD6XNwkN9T7tIsEYryWRw4CGF2rTotKEzqIE0LFHUHu2S\nWKCQ\r\n=cJzU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXhqnQKijCvL5S/+OZlIH/S32yNaknsWrPI+lXAERQBwIhAOrkn9E8OdUd538ZdLoBwKJckCTEZ0HmIT4U9vxl7mxN"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180704121313_1530706577228_0.011999755478560026"},"_hasShrinkwrap":false},"0.3.1-code-transform-registerable.20180705002613":{"name":"@atomist/sdm","version":"0.3.1-code-transform-registerable.20180705002613","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c74497d3851bbe88ac07d812598e974b7d76ff52","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-code-transform-registerable.20180705002613","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bNelXJAVV3kfgsfhSygcImCagOO2u92MOKVfAh5eullbuEmxdlFOK1hl0z/jx9HwT76BKF8ndPvUreQeeuj92A==","shasum":"bc7733a57cd98f224f38a6e8e8f07fef92c7242e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-code-transform-registerable.20180705002613.tgz","fileCount":721,"unpackedSize":1666501,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPWiuCRA9TVsSAnZWagAATAwP/0QXvRoMK7VEO33EhBCU\n11k2qO3yfULtCK1X1B4sRsqCVMybmtH5AMkQkur2yCohq1I0r7UWbhasD07w\ncymaWBTrYoK6kZIDWDYgQsb+38egMtUffbcfKCDfkxH2+SUlmqNpSTm8lioV\nRupuRbnHGArefLW0Nl1UF2N/25uFqBcCfBp6wofmXMXjVsbHYFuENU1szyRd\nsrevDd+9BuCV5wE1latga6IKVGvYA4M1HcVAe+W3Y+2uQoSLiE4Z7NO0hqeN\nCsM4uyjddC2DDA0jaGgqx4QId1m7jtbveruVmkeuTvpYZEMr3DxTpjfhe0m3\nLBfXpVlZ0xxLS/vZEEufxyYOtEk5C2/5KF9dv1jRU66a3IPWWPX4r+Mq+Oyu\nL2hArDWWUFWONGefo9uuEnDsUSP9z5U0FmBYJHscAzybNsP8oflKdBVEJks3\ngCG2Uws4p5s5qv5Mz8WZBr5l6OKT6yVyBpHWGktSrx0aaDz8zbSX21HBTfky\nOzvzJcgVX/6loeAt9uihwcfg1z9aelGF3Jg9YlUQ7htcMtf5zKkK9v3mGO2o\novXF2AzUKoC6hq3QhWENiVU3PwI5iVHHwZOvNoMbI7IiQkX+iprbvwodTxjQ\ngERhat9JZvzuuE5krLBHmFyrW9JHICZ+jsiLUNZ/GJjREHOogp6aM0geVHNG\nvTdA\r\n=1Y2Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzTKMDaIlaxkXDi1F09SmLpTywf/S10Vx6TpEZiAmziwIhAM4fzJz26gDp1TfM514dtxe6sFj79qiMLNkrddR1tf+S"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-code-transform-registerable.20180705002613_1530751150661_0.9805966835971383"},"_hasShrinkwrap":false},"0.3.1-20180705073411":{"name":"@atomist/sdm","version":"0.3.1-20180705073411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fc9099909b7fa814a6581dc1b397d5ad16affc19","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.1-20180705073411","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S2jlvJnm0eRyOGE55TUXJCycBhRbTTS0iQ/IzoHVEj/u9fBT+UitG2/7sp5vCkUzt8racgnffvu99eUNpDFreg==","shasum":"e596364ec306acf4e8419b4791f958e21efb8049","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1-20180705073411.tgz","fileCount":721,"unpackedSize":1665151,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPcqiCRA9TVsSAnZWagAA6MgP/jekP5lVuHEmIW0JPHgy\nfeze4hZbQj37s+oBOV4Q9rzzxIWyQwnFn9uzIBLkyX6eK2TAy/4RAL+BSnxc\n5LoDSRP1Ch3cf7h9oOcNQOLJ4TSpmJTL9z4JyEjovxWZ2UD/n8s1rgcN0NGC\nAglFoQK2/hm1qVicP0YPPRN0xKxMTO+GLn8+fwc3UrKLVAASdTDqYvcCEsvw\nP70+H/xim+RMDKwHKgFg+s9bV/AJcGDs+zLZFQ/M9DJgV3IhLOAioqO0nUi1\nDCSKJd/MUnbtbjkACGm79vBLuaOAHu4t91pcgTrxcZ9Rvs/PLZ3IEF45vO2i\nNtl+Qc7xyQkQjTQ42eP7t/byN7ZJVfAEdTRTOnZWvcGmzeLC/GSc1PY+cit4\nG9WtHJ8McdGdcsMf0e3MD0vgCFwd55rHo6nrAAv2hDEeEWoqA5QSo33IHxfY\nmJLJ3BGqDbN1F2GjgRWy14hHeB2zPFsvLS3jgmhzrZIWdv8iRcWKx2mH2vny\nLos4ArABcmjd5KtL9ZXSrTiJVv2+1ldKYXxWLtHmB06njhskOFQef2o3hFZz\nb61pKycI3HdnG/TMnpo3NzWwiZJJLQ1aJFO7P9oFPyocDZp4HqXTmCrC8SDZ\nraxKPyXJxwKwCTVNhbZGYa6E9OFUW8+mSUbWOWe6w/K+rXKxFX0PGqLdlWRx\nwJmd\r\n=xBTd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4Lvzen90WOM5dkicuRdSeT9IWMRmW/a9anlZmk7b/BgIhALX5Ob6XMeGuz7fTIESpZ6uFCf64l2328rkdXSWkUjFD"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1-20180705073411_1530776225931_0.6503793258606341"},"_hasShrinkwrap":false},"0.3.1":{"name":"@atomist/sdm","version":"0.3.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fc9099909b7fa814a6581dc1b397d5ad16affc19","_id":"@atomist/sdm@0.3.1","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SAHblq7Qsr9jlH+u1d0GpDPNMDNIzEpcjnsY7ws2L/hSi72Avj2MY7pBgPqT1IaCt3/cyByapYHBckA7HWKM6A==","shasum":"b304eb8f5ca2dfbb37d6b03bae343ec033fce8d3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.1.tgz","fileCount":721,"unpackedSize":1665136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPjzzCRA9TVsSAnZWagAALwsP/jr1HAPYY/MLW2/Y6ezT\np5NB8xaI3a4ue9Udm9rwQWSbhh9eLoLnebpx4ynykEIB/wXTE1EcxwyN6A9N\nLBdvnOD0tOTnQ4cEOKAGJMt0aaK7pU4rBx3ljZG7J67tJd4KjUBpMMc/oIdb\nOQYWoNcn/I6lLwXjDn6j5LzyaCZS8cUgJDm4fyD+jfE47g0Wcx2uU5efsYV3\nWC7NnCvaLkNkP/JRAtLSXvtQVz5y6FSpb4F9jJseqB6D9H5HrjUapPZUja8s\ncrYIshMwlUuNrYJUr3+kRmjOj6js9OJOBSP3+HzZiinrbwzEpPo49/wDA6mH\nROw5EBY30DUVlW3FzIEs9jlvjQg/7ezebGjg/0Qcn1TLBJaef0wXPNEQvPRg\nvTTETuozlGzTaEcImmLvh53CF+kRP5tFOdFLfFQ3cN2BFBJ236QN62A1kwMl\nKxgdAptpk9QS0lFSobhr9EtHBfDMSDH1hCwoAIzF60MlpSxSOO2QDOMiX/vO\nyg4z8t3yCjWsKEK8L6dcZIcVV1esGSDRuGSJAj3JqXvOOYz6akSYgBg2By6h\nFzaWlPF58krt2d5H85fNb1oFEMZsdO+9HlpjX7PKiAak8ArqRxJmVZno6TAi\nVtF9jCudGwM3x/H1MxHVk320VLi1yTKkRg7m7NDCZpsNnYe5tSy3WKsz1hI3\nVaoH\r\n=rJut\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDj8POtJ59I+hvvyBI79ecKS+DcxljLTIpxpmmevrFuvQIgZ/+dZhHDHLWegXmM3/+eFDWRw8tBxCL3wmI/dcalEWs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.1_1530805491111_0.45523045383303473"},"_hasShrinkwrap":false},"0.3.2-20180705154718":{"name":"@atomist/sdm","version":"0.3.2-20180705154718","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"905cea74aefbee77c3ee570a032d3123efac2d07","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180705154718","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GiJ6mDAYCeflszJaGuzzAyJeJTmgE9q76S4v8jqCSKtkYs0UPxAJniAqt6hj403ZeSOiSB5Ynal+4HUyKaZiNw==","shasum":"d63ecffab2eed072ab415680e76ba9d31c76f583","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180705154718.tgz","fileCount":721,"unpackedSize":1665230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPj5OCRA9TVsSAnZWagAAyMEP+QFjuDeftqRmPBocwhN3\ngXmahTvHA/N0IHxpBqHELSVvf+A2TQ5+PkyTx6JjnFiaEfYJNAJPQKfizvKz\n97n+obgjzg95tLxSGdtAcX71APSWFT/6ShjpiDRdJ5LR8q+RxzBlgebOMwP5\n1z/QcylNGQrvCWLB+9tSA7VlT7vlOvrUvA/pgOJ7q6zo9MdB7adCF5xqNcT2\nKBaLzzkzc2gTQ+/3lGglMQVyVKR4MlIb57IlI0nOjj0PR96GIoP8RSaThDHL\nipFs7vyyORXxR99lUU8fJ0gDx6/YSWErgG6hA3YFxEctewWoc1TH+1rQjKnL\nwP9yCzDSIE40Xdud9NXtNo156pbVFYFjqm1DLnktWNRoIkFIw3oopQmJJDBb\n4u8QKxA2/N7H5qRrwLkE8KdYVWB81i3/MvQhGQzbWMn4HujvEED8WABpu7hp\nUCN93QD2AN43HZi930ZvdGVTVtrKPoq9FZdmhDNP4cDPtAh8d/kTKvcsru/M\nmEimqiuJ39mfbP7TmrYg34WngfndtGY/diMsEJDjZ/4RfJPD6DC8yed13LJ1\nTcFWID2BBuEust4aJalS6IlN+OiMpFquMnKRo3RN5rK6W4eKeRJPApuRqby1\n4IA4GK7Nfl4NcakMxEQlpCBeAuCtbW8Nlx+fed7559b8ACg/cBrW48iYDPkW\nBQVv\r\n=BjHd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCP3AA5PAzfFympzrAJEWNrq4GhMW2IMvkFwxeRDGIayAIgWkinH1ngyty+JKZN80BBydjVGx+zW+maEVIzAOMZ05M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180705154718_1530805838518_0.3234171003716044"},"_hasShrinkwrap":false},"0.3.2-20180705215343":{"name":"@atomist/sdm","version":"0.3.2-20180705215343","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5628e810510995a439ef08708d2cc4d583c1f3b8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180705215343","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GGv/sAabxyRYKtx9D2Il1PGmH9CbPtOBaLqaUSqNyCORVC3yWfMihROfocRl2Lh7Hg792ui5yDC+Sjw/fXiIuA==","shasum":"3dedbc99751b6442286d36beef0a3598b85173a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180705215343.tgz","fileCount":721,"unpackedSize":1666710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPpQkCRA9TVsSAnZWagAAzl0P+wZSmavWnG4gQ0RVPVBJ\ntQbNhk67WUhZG8OGUSMVxzVT8n903iYbdSMd9ZT6zV9EJNntuyZfaaIfJukq\nhF0zUVelB+rHiMjJaBEN/QM5zfTFUUf87TwAkXr8yXMgoWRMhD//xKuTikNh\njzyZBcsQJxIcUPIieSCCzLu3Rbma1M2elSCNpo23/xaYU9zdA4mhj416Mxgd\nbkU/q0H5PwQ5y6vyLcfVEyMjX9q6MZSzovPWqoyu6jZYfejJ4zk1oogzbZbH\nyhdCDBrdpvTWIwT4Bi04WCzBCadv0pHGSwXt4imBwKY5iLTcWWiki/gTDQMU\ndsL7ZitHuasKHlFkAn+URt3A6cjhKQpbCDon2HfSfH3JLPR6/VCkKvBTudDo\nEALHid6ThFtJ2/dP9OHNcTNzhJvXY42XoNRAzQ7qDqXVQH0cCQJZAlD709Mm\nksn8vgr+LcBwMCtrL8/bIxfrCRSdVz2/4LBk5xz8nbEzG532tpVZzqmsAq71\nJBP5Xb9wcONqHGdwUZ7Ttg0M+7m6ouCxjYFVLYCgFo6c1A+fXh2+mu9YqnKF\n9kCWhO1x7Sl4GoSoAHH6XRFI8vE1gt9HUeyf0YaTtUBMSO6KCZBmFZrZNyV9\nz6FJ7ZEhwv+TKN7ugIF/UPTeWGXnJjpDX9SkK+9LTia95lVbGWainRjaAFnV\nxXPu\r\n=Rziw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFyPDIglv/QCBGsfwszpbRTFyLa6W4KFDvvXmdMcCATzAiBY8ekcsXuLhfiImPuItcbiG9y70lXOuLyNr01feLN/WQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180705215343_1530827812539_0.11752495305476174"},"_hasShrinkwrap":false},"0.3.2-gen-magic.20180705234214":{"name":"@atomist/sdm","version":"0.3.2-gen-magic.20180705234214","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cdaf419acd6e424e94118b58d980aa5da4a74e82","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-gen-magic.20180705234214","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S9Ru82fS0DzYbyS+x3x8Uo0xS2aM3Bf2UcC/xpGmAxmyep90jmcRCAnasMxyuUs9TPyMCqIWtnTwLM01g8Qp3g==","shasum":"f94b7ed8f192da0e9c8b72afcd9fd94c83e1bb4e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-gen-magic.20180705234214.tgz","fileCount":721,"unpackedSize":1669059,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPq2MCRA9TVsSAnZWagAAPv0P/1gYjmyRjETab31dbX8C\nKXtIuPChchVMRAANV3pnMINRVcj0o2HX0Bs+1j8kxBz2Obg8BtpQxla8H1OH\nCfmh3tHw+SR6V4csY47/HE7HQ1ekrMDjDQRmzWPMx1JdtbOBJ6ppdGc0/1t2\nk4FzpkKtG1JLoIYxM80xhmf/2E4orvYziz8GJ3pKIRMG3PEAWn2olm292OXM\naWvNfGiZ1RKrXbMwL0V/TXf8Eq6DexVrswF/tasTZepp2Uu1pGDPZx7YZKrH\nicp/02BpnUW1LbYxNV+t6nD7GAMNiueBJp3Eh2OgKH/6ikAnpiuBbeI4FgPm\n3L66AD252q2Y/hC1FvH3EgBImMw6y2MwzS+pl6lNuGAZMnEnV4/RWON4ROR+\nicQ8QJt5ssIrkwlBlxyXSje2LqxhPEdNiinjVSYuQ9HLHCc2iUDrF2q8jg5M\nwfQLJG9364qbyVqxb3SZXxzYODhlNZu5zgQ/pZIO8Rtrmf9o+JywJ2x2MLca\nz4dCj9QiFdbxf9alivgUjXEVb6BHQbnvQORl8Kg4EG/gBebIapVi3bJrp388\nEctn9QXbILSgeyBvfVHp4NKJMzPBXaTqzP4QVUJNS9TShGhQEJhLKbiKtxU9\nulLx57ZcjSRTI8RBsrjUxVpzGZCJ5/JUozCsFTRUxm5yGh+Ry4kEr8g4Q+oD\ny8kr\r\n=1QTv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFcCIldTmkAOEkn2qvwq2UJ1ZuBeKlSWXOPn6BbM/h+7AiEAjsIVE/8z4+BEEW8DSVrY1ubu4Vkyi1e1EKNTV/C/96Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-gen-magic.20180705234214_1530834316343_0.4087548738563045"},"_hasShrinkwrap":false},"0.3.2-gen-magic.20180706003901":{"name":"@atomist/sdm","version":"0.3.2-gen-magic.20180706003901","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e64366250ec64f10b1625dd42029a2b519a800bd","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-gen-magic.20180706003901","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bUHPzM/QsQ7RJYEoEWQHoaPCxWXLdbaIK9K2mdBa7IjauBBsbavVrssKAyHb0dI/2Cb9nncm4No4IxhX46ETqg==","shasum":"be9ceb34ea8703de267b255ad007b19c6f57772f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-gen-magic.20180706003901.tgz","fileCount":721,"unpackedSize":1669041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPrrcCRA9TVsSAnZWagAAyRAP/3QlVRSWCfSgktLUYFI8\nLTDg4DqTCw6arS+Z5iG547UYTStxn94biG39ltQ0fRnjzmvmnWbfDTIV9FyL\npJU5iWB1nt7HQ+L8uSJpmjoORG4zYt2pcyddhTcOfSg5tKjwatIn+MYWbTm8\nrG+zR4lAJmNHSKhxsuc7WN/X9rKHT9TU/47dTeINK9ClZ0RP+GkNymKbUnxP\nhqGSRNxCOL6IEkuaz4ZROc1fJ4wRGikX5OTIIEgkFAfn8wDQSTS9dS7iZk0b\nDNPasdDGUjT5oZ8qyOHbVjjnMgQxONmbLoYxGjNUTrCLlHUTBconX4nY6VbH\n9qOT9hovyX1IrU+OOldg+GUvcN1RYyrwCqGLTUHJfe0+BNArn2hYuZHSiUTe\nsuU6k+Ft036Xv34vXvLWGEtzkaenFNa58A2HyTrwR9L7HoXJ0JNM3F33r1yD\n5HFnf7r3VSWF1br5YzVfA8xs5c2PfI5t9TSNQ8VsFJBSkK3FmpZp90xftlHh\nxeGhf810RFM4zZ6yIgM0/9T09C7N7vLYS+NQSzYDORcDQ9uSLg2tg8zyoclS\nNN2+a/7lnznWVsr4vR6wfJ+Pj9qLGoXDQiT+FgpoQmu7zI4rgNIwvuBzl7qg\nELkPeGT1K3piT0EC/GBkRVbc7BorKuow0h3oL4TfyCXq1lag5nSDjnjvkK+R\nk0ej\r\n=V6Nz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD2zUjuDGQKZ52rD0lrI+5wUAaiRQyexLvx7mEounN2vQIhAKI7/MjN3NbDXRTCJRqOZA9ZM70wWuJm9mUwVjKW7N72"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-gen-magic.20180706003901_1530837724521_0.7690639030805404"},"_hasShrinkwrap":false},"0.3.2-gen-magic.20180706012312":{"name":"@atomist/sdm","version":"0.3.2-gen-magic.20180706012312","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8c1c73a7399f520283f88fdc5e9d4108aa9210c5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-gen-magic.20180706012312","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vL2noKCy6BDzNFpNf5LFfPhzexQIjgsZqsTrU6W5+E10sd0kr43jVZmcgTCe3o+2UL9yNCEep2MjWVN/Ho+XdA==","shasum":"c892be73317bf8d9d1072ba615b8679087d32e33","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-gen-magic.20180706012312.tgz","fileCount":721,"unpackedSize":1669490,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPsVRCRA9TVsSAnZWagAAfmYP/0ANoslGHjOxVOscjfTD\np5LeIFVigCChYp8YoqlhIJX8TVwMX39NCnxNQO2fepJ86/lc7rkRRp8IKIMh\n6So2MY6HG7tdcIXmY3ESsSGfs/tajPXNjAflWUDc5FvQfaIF7Jb126FFOf12\nPojBnGgVOsSaM8KF1/ZVqrd0HURL0tMEEJWOkHefV6XoQOFZP6lnWSQsPGm7\nTKp0h2yQ0PvJrKnNgYh/pZez+uwieIc0nyRU2GpjigwzY9QY6j1BfUj1KwKQ\nJ4x/VxifkJnb893FsNnfJKkATVQP1QKWd9niL0uI/NwUFSZn5RHBXVxKJUZc\nKqXqhfMWDaId0k61rN8LJEoVNlSLIhAPe5eo4kh0r/+uw5jKJWQDlRvVxAaB\nc+A6h5uTb9DC4xvGh31lLBw/8OAw7+7+01P7lFWUXOeSf92WYImOU7DclJ/G\negTaVMLTqunJKZ7AL5Lw8XDHuSJcEFeSIid1591bOoNZWyGvKwhMSxTVmNdg\nnEclaCC7W5uIcxeZXiThbhYY7+ABVcgIh9oiasf+HPibR9tWSZcTU8QqML8e\nDA74ex+vVfl3tzMHWmppMHCsTmQxf+TzzzoEp6WSCUaH5oEqzyB6taSye4vw\n6V4qntA8DeU1z1o5ovtahS4TAKjVlFMsievwqN8lHgSIlZsUlAtgrdhE4sdF\nBgqB\r\n=1gUD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDOAxmx3Zd5Mn9pT7HjW6UQatjasf2UGDr6O9ajU7QWzgIhANK4ssXb51acn4HJEqFrvt/3CKkPEttAacEGT15hYA3a"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-gen-magic.20180706012312_1530840401056_0.2517853535239374"},"_hasShrinkwrap":false},"0.3.2-gen-magic.20180706013226":{"name":"@atomist/sdm","version":"0.3.2-gen-magic.20180706013226","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"85bf0a5f976dd8d4119fdd0370b606d586e7b5c6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-gen-magic.20180706013226","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jrB9G4ybj0kYnfwEfdOOkbR+/d5BVPJ7c+QZc6ttnsTr7Nr2DCOF29D8NFn/hI492K9xMX3enO/q5IISmdqv7A==","shasum":"71809d0cfbb55b1721c96928aeefce697f54a1f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-gen-magic.20180706013226.tgz","fileCount":721,"unpackedSize":1669784,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPsdoCRA9TVsSAnZWagAAs0gP/1Vpc6Y/pBwpuhduMAVM\nAn7YkeqZOYuNHvVH2NiGrYBY22TTGOm6iLjbGdyekaFQbsCJwGjsR1B3emkS\nCwXBV/EFZ0DehEqyI05qFATITiLyFMBrOYiGZlEBZQ4Wc+8sSqkbKXbWkjeJ\nSII8VGBz2VWcCTQNm4P5LUHc6ZkOTvXzHqP+hFW/se+oG32PxCRu+KNohOCi\nZDmx91QyWaUgVb/2RtIeGR73RN9M2z2CP62/RDWpyZFALxVyus+z+yswCfkv\nNZZauhPX8H3M/WfNq5TU6L2SU9z646FSHRqL7ySLPKwnlizBvOvgoizmlDa4\nkYcBk5HHHnDzNm1VMARQB3+1eD0WJTevylEqvdbnanBuWYfrTVLMpjFReuS3\nlMY+Dc939EXPViF2Zo68doCLg/TC+XejWyMFZnk34lHcZVaXKIrl/EsiG+zA\nSujnGe2zztK5SCye+8juBqApH9aVX+iZmutAQgzWGD9ws6yHPSm2IB1C2v3n\njg46Kqsuru7LfPOYumwk/jLGum35ZjWAEIgC/Abp9uS5qMabsIfFz5tULXPT\nqGGMNiWivm13yZjlQD21kvYN4mNi62MS7kx09WZKAs4Qf+Jvq8GFQa4oSZ7d\n4Vfj/Kq/COJ1QzQPQ0ROXPjNvbfrPxl3774FDtLZMWauqiOHldgb05anTjBX\nMPi+\r\n=0xBO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFijVD1sCzvMF5r+gB24UrjYEzOolC2I9iB48ElgM0IQAiBk4q0SIQgoi0Uc1CXPNqtVUcRGSG4tMkSnBeOOCC/IjQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-gen-magic.20180706013226_1530840936053_0.2677285829155198"},"_hasShrinkwrap":false},"0.3.2-gen-magic.20180706063739":{"name":"@atomist/sdm","version":"0.3.2-gen-magic.20180706063739","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"935c4e03365c6b5f6807f2bfef028a5ef7360bb7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-gen-magic.20180706063739","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4xYR365EBkTW4/uvYywh5BnhGfHfJjXS+HdkA9yurJ8wudsBd2xZRWjWx73S51oipXhuoMyoScXKDTbb750Rdw==","shasum":"3b356a1ef28946bb266a7c6d3882afa546a8a9ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-gen-magic.20180706063739.tgz","fileCount":721,"unpackedSize":1669905,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPw7rCRA9TVsSAnZWagAA5qYP/0QQCoeaQDxK8V37jinx\n8/W3AY/HJxIKlN+yp0Bg4JsLQyq56d5DOfhKUa8xGmLDgm7gbKnQD8jrSaH3\nOdOiqnFLQYIlKyPyiwTe5mAWpC65NAeqE5H/L8aMJbPVIZXrCzK5vpLTCj/B\nsr+vPL8C6woYFtyBl1B4IaAW1Dc4RRnvpL8q9zoI3QOjQfoWDtfcjCkuiuTa\n8P7B+lE/BRCqLRAcw3mbNKkWJkUzQC+wOiX+euQ1rq9u1TMQCc1gf8JLnCIz\nhvDzWdQxcVZ1uRW1foFia6dMElfnebBx2aiZxFmy5AlzIt937IQCIiSsWdRu\n56LxdALtnE+4DMZgGFZ85lWPIhC/QFlGszrTkReBMDnW/gkS4zuH58fbgwA8\nkdO6Mi6jWEInNxAm2QlCfbQQtrX+7oQtjltzz/NFJTKVn/OJjMO8sg4Jw6NA\nf/rk/ztjBuUWYu8eeUrI0w+4nrK54b4sxXjkyvbrV7DkjOreY/h7v4LShTDm\nAxzZ4pf42IBWCcKZfPSts3ay8Q92yqgrrY81gt/+A+V+a/lJhj3t/wuX4nRA\nAD/mdLyDAUJK1TpmNK2AAYIaEBxmOusJsVfR9DgvU6IroxgSNPC0gJtFuxsO\nEkygDFim8onoAgSLPQ7WY6K9x49Yi9FFXm6FyOrFl+8NrtXXnsMIg6Hem+PV\nqN9b\r\n=yJkB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDSYzSrujyIRtH1wGihvwjVxCkJPtpqukHh0+L77MOtgAiBXDKjkG2DmwBafrqonSFdsaIJn/edaNCGDe8ayszG30Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-gen-magic.20180706063739_1530859243417_0.8724890207847835"},"_hasShrinkwrap":false},"0.3.2-goal-paging.20180706100341":{"name":"@atomist/sdm","version":"0.3.2-goal-paging.20180706100341","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"de394e2adcec95ec0d38d8e79ace54ba4ec492b2","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-paging.20180706100341","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ulRlvc6USmwgjAMDrt2JfmrDbgqucMwhSmHH2IVxLWYyJl4f/QDvUAFqDM+3aXprxORYUU+LBOVoRbZYj6PqNw==","shasum":"e4f88643c3642aa51d5249394dfa782b627832dc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-paging.20180706100341.tgz","fileCount":719,"unpackedSize":1672949,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPz9QCRA9TVsSAnZWagAADVgP/39jA5PXhG2/LrniVdwK\nmkB1pqFWJNBdnYcGAZJvVyn526CM52XP+j1or/NFXHPjoKOraCPdYseXQ4Jm\nnbefLuzc0FW5ex9n8jPczpFc3C/vtxZAdyjxe6U+YephUbPP1YS2toXZJnbA\nlXL7geAn16lxg/e+MDOVp2UjaorHVLN0niwVV+VcNwcSP+gjOXTFWR/QdBHg\n18aWOXXB84VYgPkoizQ24ia0EG8av/kF1MKBRd8rCyKr0RXTvVQSkd6cbahA\nxxANyAw2lAfgfMAk9ajayJMd/+oIVgJyYjZOTzBnmjCnzLqij0wadAo/wiIN\nv0Ddts89Ke3FwOpzDAfuF0XHD+81ZEDTZWxQIhHPr2W6Wyzpqe1xlSnJOcgt\nKbPc2f4aitId0b/gwR0wy9r+3mv9DbGM2Dl3HkL93B0Q6rwVIx2+Q7/zznmj\ninR5udDUV3BpT0CTqEaACPkEiQ+nHW4ivrfqOyEpz9b2lcSxIFz3KMxll+LX\nOXoBLX2jgUupZo/DEvi1CVw4MtC/9Nc2NXVHE2GHwY9JbDWyLY1rjq+catOD\nDjmAAcNFRZa0i3B4GkN2HecQg9T+opGxK2/ZoUTXmXYEV0tZvJkI1WqZjnXZ\ng34wwESc3pBIYfF4c4NflYwG7YQyoQEBRnWCKdF01wh1QdeyqQs176aCgzpX\nrVs1\r\n=pHuz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDA4dFAXVI76fog3ZA+sJSvi+vuOYqn2QOv/t5y78yjsAiEAogM6UarwL73ijooW3ozl9VT+YH02YT3xnpVPKVAGwVk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-paging.20180706100341_1530871632480_0.48440834959962675"},"_hasShrinkwrap":false},"0.3.2-20180706100354":{"name":"@atomist/sdm","version":"0.3.2-20180706100354","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"39a6ce95d8164b3c6df186fdfa37aa67a91aa59f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180706100354","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-p7tcSJAEBq5m9YG0W3l9xAE6jcH8zGMg0iz0JEXdRfj+1IFHkEouQvl9Ocxmlk25KJwfHFtYGvtSusjnO122LQ==","shasum":"1917b657aa01099af7df027fd4d54b4006e75125","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180706100354.tgz","fileCount":721,"unpackedSize":1669895,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbPz9gCRA9TVsSAnZWagAAWMIQAINVy67yKxhwN2FgPzRe\nIKPADkQcFv/xUPaZXWK2RXBVUzHgMvpXEqJoNDEp2TUL7I/NDqECrKyPnQ/a\nSYwmM6w466pSw+1pwu9KPyAI95uAmTHIAxk0EshP+dZdgljq82PIEDl1F9s4\nMylfkgV1FccxIjh4vgfAWsvwDPjyqVbAzKA3oRAGthTXFnRK3sR2kxQmD/rX\nghUOyUt9yrLPSdCRw8zhZ0+rOHnmC2wGm9YoDQShGRoUOuACPkaLDsx8IEzu\newMMffuSDkz9tDI2hL3Q2OCE7WalOWGdMPsmzNJbCctyVP204/hXbVpbphI0\no7G5QVL5QwejTbmI7eF74PdU0zx1c6DAnFZakIBbzCbGyb3OYj7t/Tn91Ti6\nWBTbfeLwDTi2fPiS6rHXF9cfnB5ImcgDVL3Vr4uocmsq8uNBmFyspThT/E0k\n9yGHP+vbmCbGW37pd6IRz7kNfpMz6M3OqftcpcQ5Ze0M69uxTl7pNWXMIHG4\n6J3F6VmpD2WIfAo5cXDTtbdNJFXgBMasqvFdrSneUnI+8m51opEiRE5f//ij\nLJVL+DseX+NC0qjENR9zjaD9pPoMzyTQEKk17CPw4Vj7KMKpCirwCuqjdT44\nGwjRxXPOk2Du4e7ZVT8G3Urt5r1rFUDThvsClt215W3MxNE3UyR6kFsJEb+W\nvVy6\r\n=Crq8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDG63PG7GO47Xl3vvmNWLjWXtVAAjzd8Wu9MikY6mtqBAIhAKdQxRggmp49omJSsgPGGelio7LlfQWLJw9+U1aWQ0JK"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180706100354_1530871647947_0.7596298746213475"},"_hasShrinkwrap":false},"0.3.2-goal-paging.20180706103636":{"name":"@atomist/sdm","version":"0.3.2-goal-paging.20180706103636","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"52adb71d389eb9a19dfa0fe134d0c19108e185df","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-paging.20180706103636","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xnxKbDkRH4K2amsDox+B0mm4Bj5nOparraF35T6OurUXa6aMCBEvgEgphAX6taDlGRSskYmYKGj/Dj2QqEdq0w==","shasum":"28a7e5d195a153c89a2bedd24de3df8c46d6ce0a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-paging.20180706103636.tgz","fileCount":719,"unpackedSize":1673187,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP0bwCRA9TVsSAnZWagAAwo8P/2QMjIGG2UQgycHFuj/Y\nSRQjkn0GgIUC1mihVHYuLwlqNI5E+vkNJfHtIpMc9Jcweiap6dciv7hGpFS4\n59t09/75c3ZdpZU/aTGAJbzGyRADINN4eo/3j5YkzVrRv02SvmXtYH6oM1Mh\ntbnxhPWt4Q+1gfNbg9uFOaElP4frvB/eAfZjt54vRNftP5fSntKuixYo4suA\nNWG6MOJMad/R16a9p1/trCIgaz/748kzNx6K8drt+EzrmFBut60pkg99lyCF\n+mlCp67jLZ61k44Vw556aYF4gd+FXbe1rNwsnomJiLKmaQfojbrdDRp0E4Cl\n35CGT8RWa4jGm0MYIQkfnFAQ0geG5Ha+/u5ltXytIHF5Li0t8wTpObsynERX\nn2Qr35kSPO3kARSEJFoQzCs5jSStMOUjVv1uFAiTtMFIGJUPWCpfkEVMt3pc\n2rLsRKn6OxAzw8/n+6HekzMg/pjjpZ+LL65GXXKS2qEOXFtaN1wjzgI11Ols\nIJitqGmvczudNVI/fso1nMTrczyLJYTvtVjGuA7GEZsCXx95ZU46pasjcoVt\nFnTBhjmUGbhu5KHTFDwEff1HXrCoc+Kn/P7k5biphuIzs7/FQbkZxAzrUg6F\nllXFd32Eu6NLB7T9itannscDTAqsTxWPgZjdOwQeHnccylMfIkdoGBCElrpG\nM9EJ\r\n=CRm5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDk/cDFZuQJ1/3JeYiDABIYEurvClkCc8q8PvS89lL/tgIhAKt35NPj4KLMBPEZ6Nt8A8DlY2A7NhFLRbVhZYL070XC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-paging.20180706103636_1530873584043_0.9881458295674075"},"_hasShrinkwrap":false},"0.3.2-goals-dsl.20180706160415":{"name":"@atomist/sdm","version":"0.3.2-goals-dsl.20180706160415","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ec7716b36b799ed0c80fea31d4ea3cf346179a7d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goals-dsl.20180706160415","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MTdpDNMQOTcfcTYkiCMezgv7nmbXweUAMtKPvJwbElUafQDx6w/VLMAx7/bNjAMU26dZmk1tRKqZuNe3JS3u7w==","shasum":"9dc60b9d7e74e1d4546af443ad0946a108c3eaeb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goals-dsl.20180706160415.tgz","fileCount":721,"unpackedSize":1674478,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP5O7CRA9TVsSAnZWagAAGxcP/2dhSDDMQPdy1D4LAaq5\n/64tNni/owdilXBfOkKOGhmGbLvBrjowsUNIpIwvO3yKCfoC3zhuNkFFkuUd\njWX8XL7wBmKWYBb8kUyXtzqcleW/6MY5mXLUZiETrPs5Qt/zVpPWFpIZwqxT\nIrtnOwQ1ylrMxhr/KPYkSnu3goYJN4Si4lx5DUa53OhfyUlH9e1/H5Hbd/E6\nLtB2U2gAHsYqHAgS7fRJim9xb67TwHZeKTwcENOCZTFvkfxb+eJt3baeAJka\nbeeuq0p7H8SCAm3QQ+NIjTcItMGesxYE2IrP1SH1rd/ccPx78ZovwW4wPS4f\ncTO3kT7L1iIQX4lhGEcpJOERJmahSraRQ1gOfJiu42J9ZJsN1+7vZBEvihdA\nHgdm8cUkl0bgqH1UZT3yZAO7ugWtKyXlyO//IQenp6riwX6BHg7h4goG74h7\nXll66OJegItlYQO5UYcOKMjW4sOnFpGtkCCT58H8ogoDF1ojtWDzEZgot5vL\nZfqjV0CzvqCr5TocEfHeGBhZ2pz+9qjadD8FvtHcz5Myzi8+XLMn0MC7l05J\nmaWkwgv+AuEVW90AcuvaMDRXzPPSwHsmvgJdhV8Wlo8QSG0jBqJjZMFyegaW\nLbmrBR3hGcUy0JVfsqRy7kwFeSezAvsAf4Opoqx330tOt8Vctqrcy5VeqURg\nzCWM\r\n=hWWJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG9XDbwISVFv7k3ccrc2wPZcYPttOjxFsf4xuFEa/DkHAiAUQ3PrlTNyE2C8iQ3A4VQdSbh1eR+CS5Z0wJG2fPmIUQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goals-dsl.20180706160415_1530893243543_0.7411066560712425"},"_hasShrinkwrap":false},"0.3.2-20180706160956":{"name":"@atomist/sdm","version":"0.3.2-20180706160956","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"76d312902115ea8e0d1286b08699bf74552a7581","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180706160956","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/YG01s8MZZw78qsrjsbT07TU5GHg/cxZWkKGLF0cV39lXwJVLDjH0mtGDb+5dfeWMA76ThxNHM2oKali9kKTBg==","shasum":"b75106fdbd7659c2088ed732d3eb82b11365175b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180706160956.tgz","fileCount":721,"unpackedSize":1670092,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP5UTCRA9TVsSAnZWagAAyewQAIIRlMR51VX5rUMmKu42\n4OkCDCJ4tHvnUHJRHoX4+P0pFldku3LKe7iCDVNJPehmz+HWNPqMQCcLfb5d\n19eyK16MJZ04HU0dVa4al3m3XOk5z/OSa0XtTUDqHsZjsEkUmMn7wHZrYMev\nK+w0I49UGaHQZ06gP+yBIM/LlYxXpvwxNmvuAIU3x7Tjgu6HKhlwNDiOA8c/\nyNoFjGzc2AT46UdbzXGB9UfLf2LBt8uGFYLqZNuvPa1hQYTb4M0YfOQC6WUF\n0Wu/2jmQlmsJukv90d8R5Zw1wq+mwOFrRbApYjnZ5oKUO5lJ2ewQa1sUPN7A\nfITroDb4kq3nt+w0K0S0l0zNdyUcfUKsYsyS01U/EjJjx9wEZu8RJws6b+e4\nHYXZXe1LbzD8Y8NBTiGXk4mGgMEziNfx04yF446Ca0Z41QooHoPpoml/4WAQ\n0mFJkYPSKJ3Z3B9bXsdkNMoYuNiUUZafaDSD2N+yM7dm9hLNOdfqlAdj7/mx\nMMP/fgRX8aGdQb019QxdUnzz6VlWielJH76ZmakVMEcGXolT4UfsqisEjv1Y\nUyxdDhohJ7tFDuia0bCtoFmEJu4j/e1/4cttk1FGntoxtJjTTK17Ub1R3ZET\no0LYzu8jPWfImLcewfOAt7rxngthSJOKjbqdBcCLnzaUKZzoEoFmnYW1ynwD\nOOEq\r\n=RquM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHzgpZRfoJSB0szne62Cl20ZEhgIcBEoG0SpV5gOrah9AiAz1fj77OVdDVGjPlEpsKzcHk9jzGnXE6NwAVCtqSreXg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180706160956_1530893585355_0.8776199892078655"},"_hasShrinkwrap":false},"0.3.2-20180706161628":{"name":"@atomist/sdm","version":"0.3.2-20180706161628","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bdc92453e31247b563dedd48ea640cc60b7d6e6c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180706161628","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-meeWQkfovF4rdB4Sn0cyRWzX3J6k5otp3SlWQtC54V+69LQ19DO2yTMBfX37rSDGnCXkhSvxhY2SIODCwoyjBA==","shasum":"90fe2d82b89915f36058dd6963ac54fa5fc4e599","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180706161628.tgz","fileCount":719,"unpackedSize":1676557,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP5aVCRA9TVsSAnZWagAAexkP/i/Dzd7gGXpG/4112/eQ\niSAR3kv28D356sNgEml442kdAwNMb2jsYgHQt5sOKrXgH+ov+mKt4K26GVjP\ncaRUjyCFYwaAnCm0y324yCSI41o3K+c5oj1IPXP1AZaGizeJB/Lc+KnqD8pm\n/knuM/A/2f0jvbaVS4BLcdBqbhOaeclBFqiPosY25ehDvV0LmYJpvOXBXOK7\nHL08jVAcM+w+X+TKH7GkcyfRkNZvkZDkuqklkc1kDKpR3iWRhUoBrc/gJ+Qi\n04hnnptC2fdY6ky0d/Ua8sKRBTBpzx86uJSDZA/1N9p0BiMUXgt4clKTbBBU\n9vYScC8O3hvrGpeWTrVGcKmje6MFBKJg3IxGZH6lWHAuxsV7p7KylfIzx0MH\nFDBxW8XhoKZHqY+pokjB5qC+/g4xuBkQRVcAHLpd6nqYnTfR0YqnAXO6X2ty\nq9TgRRacaEBYGgKf7lTM8Gz3Xxf/kvhGZGMs1B2zcQ+frSnYTZbSWo6ua65b\nblstm4uqI4k9w3yl/FRrXrDpBwiTEdwHLGZOrYe7rWCzt7KW59yGh7bS4IcM\nZQmzkUjc/z2kNN91Rr4y4Fm7VWZ5oLBhz8k+mJh1q7FKAi71uR9igglzlc/M\nbNzkRsb7NqjcUTg225yK5+uvtYHTqO1RlC6HKyCriprQpqADCuhsEFn9wtTJ\ncCaF\r\n=NfL1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDT5QRqGDambnByYvmfaalCv7Mpd6Ik4XEDxPOo7zTwdwIhAL0/Fivj5Se882ceRwc+aqTTzf1ana+9ZzKWqKyCsdu7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180706161628_1530893973083_0.9111344058139397"},"_hasShrinkwrap":false},"0.3.2-20180706165913":{"name":"@atomist/sdm","version":"0.3.2-20180706165913","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cbff17e6a45fbfefefc1b3f9c6b4ceb5450e67ca","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180706165913","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZdD4s9o4oP6N9L7C6f0pfcCHt9reRnD/Z8a8iAeuqor5n3c+vT+DvHaGzFyh429+4pEDl6aHaS9ohIWRet7PJw==","shasum":"fd192680e634db6048e77218374bcecf2aa6a4b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180706165913.tgz","fileCount":719,"unpackedSize":1676645,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP6CiCRA9TVsSAnZWagAAD60P/RMxVFC/qfJniVoSbgMe\nGGeWBRFbmh6VidPP9YlOnHCcWZivXRpM2b/9JGjt1UbC8mqLhTBgET4PdK4D\njzvOWv2MotmNyE3k99aVSbhwhR/qQk017c470bHFMpImAdeR9fwRz+vswHBO\ntHaj85NxJlkQ5827Mq+GWcekU4iYSDkgBQBc4PVBP6U2f6peDwvHNn53Ow79\nPpxP7bOikqFrlZC8ynkLf5JBmy5aE/xKl30ZUcHMRapsBiakR9sFTQJdnMsR\nnhAl7KGhllQL5NN6vbpXIpRCyTbW5G693AuJ8mbucyMzVOqyJX6CrmW49YjC\nQEu72VPIMXDvGk5a0X6JMmd605cmcfYfwaXqXa5IASH0Z7eBQcEQvJzoEXkR\nLhK/efdwrwoM035nYLOMARX1UuxDrLeD2ugEYtSXj4E3GkHirHTVB+IO97Ma\n0nukTUbjJ2rMMBw5TSEJOjHpOmwA2O/x1lYFP0xZ2iHdd3xvl5oiMMfoPj1H\nsXL0vCDCHY3beQoC4dQvokWlBVgDLMy37ZB6NyYbePpb+IzY3iIDzHQDhzH6\nkw3F/m8jbwGO9imZcgGb31NaPfPQ4zYWw/sDwa2SpWAxEPr4tG7qLKM2TiLl\ng6ig9QGYnnFKmpxJFfB+CMnUL2qNdxqcyZJ60Xp+KBaLo9XHigbYfSsx6CoZ\nqpd7\r\n=3KU1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE7qcUXlyhwnXFwk1fubToZkqTCF+kkatYGv9h8HzeOuAiBam1hWkT5f/wYW0s2jdEchPliPeNlXrRTxNMb0LSIXIg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180706165913_1530896546153_0.12869962132017387"},"_hasShrinkwrap":false},"0.3.2-goals-dsl.20180706171459":{"name":"@atomist/sdm","version":"0.3.2-goals-dsl.20180706171459","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e1a6f31278697c7d7529c335dd792a8c53eab1fb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goals-dsl.20180706171459","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bSt1ObuXr/FAEHfANu/h8dQZ7UPMlrjWO3BZcECF57ZAWSPqQFFHXsSNSt/LGH/tVE+zNbm90INkS/BU2Gwhtg==","shasum":"8777aa425bbdcb7cccc6dffe82eba3ca05a175e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goals-dsl.20180706171459.tgz","fileCount":721,"unpackedSize":1674449,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP6RXCRA9TVsSAnZWagAA1LUP/3s7g2oUKHAv3q/tip5U\n4flwoluwQwWRMJr5ogHKZ7b8y3/1PLfuFk+Wx+WKHFmRX0t1H1NJyuTM/+1n\nS1fHCE7WURAkdhHePlgK0I4PeaMSaNzS1MdsxiWWKTVnll0uD8Z3hZWDjXEg\nhn6Yf2DsmvA4ek+6/FmErQ8PPz29JPLkj1JZmVGCIv27EAPQIb/I4R8WhnO0\nUbeHNjgpOWogyvp1Dt+RI2gC5zIslKixgAY6RKZLHbIghOpQwcV1Rqecjfcp\nvWepibIGY4J2hufBxvnJbb78GsIXjkiQaITW/psMk3SQpyBkF9xbK3VWTx6I\nSCnnBbzSzojelC4m+bNHMItwR8mCR/Gldb2k1P4c5wxj9sz0HQpZPUi5T7Ne\n9r7ayAud6y4r1soQyqhHPy5ULfwiGXVITf1EBd6XClb8sFbbc2gjdfodKGcB\npwQpL4kyHaHUP+IzyZGfLt6aL47Kz2GakjUizUFCnD1sWlWlNDrW61PGoEMI\nuzmdWPpIadAWunVdHXF2ArvZ73aQCLiHxO0u5OQJAhYHsLn4daaKI/KH/sY9\n0wbJ1A+XGKFFcKM08pY1mMgwwwdYY7mW5/2n5rcut/g1m+a9Wa1BzxO+Vc6H\nCguXcL2joKnzcMxBtPaSbrSc5KRqGCN6lpj95ffOvtAg4dsdbUI9C8lFW3ZV\nfM0b\r\n=yUMX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCgADw0SK5O9anQx0N3SmVySbEpaIiA/we3R2ssJxv91gIgEanenod7r+/7JrdRxufkwFeXDN1176PTpRsO7M8Khbg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goals-dsl.20180706171459_1530897495362_0.9499236106620326"},"_hasShrinkwrap":false},"0.3.2-goals-dsl.20180706175123":{"name":"@atomist/sdm","version":"0.3.2-goals-dsl.20180706175123","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c9c5116fa58bb020b7e10bfb0358522aad4872da","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goals-dsl.20180706175123","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pWV7cVdBHkxq2m7omrg3C4gZuGjCMIeYOsuocv0Ex+9iYuX22+pTTZUcGUavvVi0v8BggMCiFdq3oWQrKk4IsQ==","shasum":"2992df109cd2939782ea1c61be75b7302e8235a2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goals-dsl.20180706175123.tgz","fileCount":721,"unpackedSize":1674440,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP6zoCRA9TVsSAnZWagAATH8P+QEIaceKMZBGNWaGrG3D\nRpvo1YMMEzEoJFdvEdKbVSRqV+PceackUfBDje9JAeFmtI4n/9wZJoK6Jvcd\noR0XL+wOHf/Fckjdp/lzDtHFt4LVNlelpZqMFTemkQSA/NU7s8roxzLgyIG7\n3AN+X2aZ5GFefFmz8zVImZ7bSz8hKJFlcHP3cmSqjF9iNRX1UyU0KK1qQ2+s\nmKow+e7ZpYq+jDIqB5l2oSmWYKAoH2hoylGUfcQMjNv1f4Jd3l9niTm78mmr\nLywddWPAlbo9bguPLvUM4BFMiZF5+5R7JL1jOvQ8GGEPWAob0bS7s/hZC34T\nOx6LdOA+wE20pBcKG4AVUHssB+JsQ1a4ANTjdtg+jjb7/PEK9Cirb/uIrtYc\nK4MdgV7dWOORFY/cBGUcDVCHM/IBoxd4gNIBh1mC0XyF5hvMwze5uUVfUy57\niiam/fFfLyF9NcWKtFllMKkGr2G8+oBUfx7Y/J0SBKv1BNkolledXD+l8F0j\n5Kd+0oHMqKhUAhplK5S8fxVG4PVNrC+EyIV3b5FcWEjJCn7Y8HtQ7x43J89l\n9INf0aFWVVyAZczMLMpXVx791DWfAfeY9LbMVy+SnbhKsdssElBlEukmxKRW\nRsZntsGw+Luk6n0A0kAvoykpjYl8R3wbW+CcOkf0c1+tF3UhknKTRELIyXQ2\n4c7v\r\n=vuak\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwBFVIP4HtZevzKcWdzEekL93sl+n3/+W4dKqal2lVvAIhAKDj5Wh82EEmNMHtEmHuWo+UZ1PgEYBT4RAmyoM3U/ST"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goals-dsl.20180706175123_1530899687989_0.9998826324761869"},"_hasShrinkwrap":false},"0.3.2-goals-dsl.20180706200437":{"name":"@atomist/sdm","version":"0.3.2-goals-dsl.20180706200437","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"92a8a66213a389461fe9b284691075137d7a09a1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goals-dsl.20180706200437","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WF+0//Rgb/FVTS4c3yeZoKOEAotAdi55hh2vV8UhhgE9txQAU4frdAcRiWXYxWLDFwXza4bFkfKl+OMbiImNOw==","shasum":"7b5cd10a5bcd0fc50e916866f6ad31a7af4ae74e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goals-dsl.20180706200437.tgz","fileCount":721,"unpackedSize":1674744,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP8wUCRA9TVsSAnZWagAA4PgP/0Aajg/IplMFpZuaakFT\nt3LF5SOeqSWXaq0CQH6keL2hEGsRCnzHr6qKEolUT9PnEEi5Ji3uF2KXjxot\n9+mM2CwzhoMd9gPmR888yy/ndsdnF6QKAK7cBbK70EGDl96bNoRvNqcpFWny\nPMH7Z2JNouOxgyUsZLw0ARDjdfZFZHkjyLg6D9dhMzipu8hps84G1lqvlrLN\nRLjzR+xUzp6Xqd7E36bj6D53uyUo6JsxV8JjIXMvHy8Cfv67h07ymfSp0qX7\n/IuR/wHoIYum427hJsRfAPXhG5pYSX1sjAzfQFHwR159jtYaglEx/gH+72MB\nIltuw7RQczP/CGcazkSrgaqkfgYVr6askcutjsvYeCFmHTwI86YVM+MQWM9D\npjgqIR0qhsX5DrpHV2GdL2ivs65+Y078/jtSYJmkVR0h8hBs5iu8XbKTZvly\nPJl98lq9eTqjFR5z9fsTFCwu0Ulf93FRol0UW7tF2ILaei8NExCv0mesCKI0\ndXa+2x5Z7/iGO4VI6dtAkYkCTrhYZsxpKkIPynufE1XJnE9c3KBESY0j7uhD\nZxGMLZMFOpb+EjiMSgWLmNOcMFHcxSWfiCNa/PvdajvrJalbfBQDlM4Z82uD\nhcqPu2ve8Wt+qRvhpi/IA8/m1GRxs2EWwj4F4Q8kufsQFKjSC6WaS9SVi+hB\nQQE3\r\n=awOH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVXyPJqJm2twvuhqALWIVHDbiRhnDfhRYW7B05KZSPOwIgSJxdeZLWO1DTLodPYikMXxQoRldepHS3evuLiyJZV9A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goals-dsl.20180706200437_1530907668171_0.6283872049760892"},"_hasShrinkwrap":false},"0.3.2-nortissej.remove-status-field.20180706202026":{"name":"@atomist/sdm","version":"0.3.2-nortissej.remove-status-field.20180706202026","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9dd160aa0c616ee9839105d41967530b99dee3bd","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-nortissej.remove-status-field.20180706202026","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-poZEf7tj+gNaQbdrItZyKYMguCHWg7KDu5E7GvkaCq4vNGaHGkMvnSRet9ZtNl5+YMo/daZOXcRoEFxnaI4szQ==","shasum":"69cf6868fc55603d8c35e22e184c469c40512497","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-nortissej.remove-status-field.20180706202026.tgz","fileCount":718,"unpackedSize":1670035,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP8/DCRA9TVsSAnZWagAA56AP/jgHuGLC6T4mM0sZ0fWw\ndzvb8BhuqPrQSOEAQZ0goJNhfD2Trhhitb+FWcab0VzLe6lDUIJoNwsmVvbq\nNXJwyLAMn530xVPPye0KuYxBwxE4U7SjcIAH69rp48hoiAt4UKT9jISgy60T\nsl/7sOQgoGLM0jpgqrTcd/O3tkQKaWwohrr/+l5/bABDUtHGuAFYRDgbtDc7\nXkZFLblsx7yTyABUL7vVCSduZgQfj079DMVOZ9dJyBf/ghf6zGBdvjFhMnBP\nDiaTVreV2OEQ0CzdXCzYobTk8C8P5NHe8oY1FpKZ+S5qBZ1erVXIUqw7SWJA\nvyp0CR3URoon2AFLjZ/ara9UJ0HvXjO0gSpci/lZSrtxE3qEAKk84qfYgGvY\nl32XCyOMpSxxKuRZbfSoPQ9/dvW7kXoLWdRjzjn+ZQjGFjtCrliwQSfuwZ2u\n/Dx/Ci28PFRJqhwEyprNqQSLbRbKR70B88z394KA6eCwaJh2ne2EddBRroQP\nZXHbBWgNt9HW24ff2fE5s24du2/LPUp9gRGYBPIhUKNqXLxPtZ/Bm4WHHR8t\nW9KFIIq0yqJC5wD4/Uis9C7AGfbvNqpIE6AZOlJdAMdT6kk04o+I3Gk5EFkE\nYfSnAIu1LSBNTt0wNHE4XgkYe/QCk4paLNQguw+By6tLOpMyZFi8ieavBOvf\n1Qzo\r\n=xh2X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCuxOLY7eVTzDRKuPEh2ICfF18JTaCB3rxXS6OOwf2vQIgVaBkFn0o5h7rFhqyvi8ZAb61UA+u3s/P866DhvPTNAw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-nortissej.remove-status-field.20180706202026_1530908611068_0.8835844454593922"},"_hasShrinkwrap":false},"0.3.2-20180706211438":{"name":"@atomist/sdm","version":"0.3.2-20180706211438","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e78bc6789eddf4abf766e14e470bd0ac7528b1b7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180706211438","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m0oMY3aRpSZ7pXVE0YNQ0bbKB4s5oWDiRCKsgy8QPyt+dlrTNKXe+KnjMdAzT511zJnDrdQOcK4S5JENp1lJMA==","shasum":"aeff935422f9f0d8db45fed6dbfc8bb1c2286bc5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180706211438.tgz","fileCount":719,"unpackedSize":1681484,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbP9yWCRA9TVsSAnZWagAAXAoP/0PBPWnJJgz0c/XP4Aiy\nKk5xHW1OmtfJEhfRB3/d0y3e1TSJ3RMGsUvRNYlW1HUZUGVKEQNQZ9vRlNIc\nvEap93gr92jZVGromGEPuJKQnydISx0MAxts3PCLMiqibLlMed/em/O/+TrF\nfDxHbszl+N9FLH42UUpm4BApVqKey3jIXA0+usJMDQRTV54c08B5Va7K2Ndx\nR/IovrZLi+0w5gGN1o3Yjh5TxaAg/WGXpVLay1ozybcqaWN/otoF1b6t+U04\nwxdFHpM6tc7g3n82DuQWr5l7y0PMfW8EkFfCiVR0J0IN4EXw3qUuBkbgn/2F\npQZ33zsCfp45Hqce8GokVxYCLHuR2UAz1dlI36rXPhXLgRPmpF8FVYutepEF\npGOiJcH2YDIfRxqyEkp45+rbkZGsNpjqUCe0VlF9eyWJSJX0O/UgyBqL/yWY\n0T5o70Ak5+xGrq3q2TaRJI2roo+DUzWwsIx2tNH9TfAy2gWbQc8pnLKNRfPN\nahTIH0y9BOLdN5KW8av/tNXuHL3UcVS2G1DPjg+vV9m6Al8bVzXJUsFJBQkY\nW47k3kqqWyKtSPwHLkFWpcw0K6qgRuLH9qh/an+gZ40qlBH4SEa6Vqe9ht4j\nDFjRpgsYWPCoJ3GgKnpBpHSfn5KcpRE+mSdw/6K0QQq1Ibgz8kK+DGXg27//\n7IPR\r\n=lpT7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE5zWx4kzDc2bWB/fxdpbZ8RzoYEmvkw7BmcO5RRjn0pAiEA+4O9OKxUOxmgrwjhRR6LUvgHZvM7KUCctMO0sY6BYQk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180706211438_1530911894055_0.12520424161885635"},"_hasShrinkwrap":false},"0.3.2-dry.20180707081308":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707081308","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"39ac9aed15af42689f879a38ae31d3fdf3542c6a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707081308","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-J+Y+jy4b8MkgQxq57bLb+dPd4gVRxxvUkoT5nmdawhxYbVgLeRNS32TFVRTvTROS1LwzhG45Rcj6MZTm+ItLNg==","shasum":"fbcf919a1c6e191ef6fb0b6a79e35a6acc94c44b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707081308.tgz","fileCount":743,"unpackedSize":1698225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQHboCRA9TVsSAnZWagAAKSIP/RmgH/wLcKiWHubHtunG\nOhH42JclnDLb8MOA0HFwn2Q5UlSSoL8TZHUVSiVoSVd+6hQPpXh756EB74jX\n+MaluklA545pCjzq0Fy5MXoiMFotB23z3e0oJ2wCjfa2O48jcjA6ncs/aSr0\nVXtJICwF6aJyADy/sKAubmo7ySlVo2yin664QnlPRKpRpi1iqRQ5saws7KCn\nEoVdLtF8pKjx6bqSCCLY22oo+VJlGGhT0US8TBD0OC+UXx7v4GLTPIRmWBIY\nB2+Y7f4WzqaioFGXGhYgaN26NMlGau02f7VRArleb7J/7354IBesTqBamUV5\nGTBuZ0GBdv+w3Mt4FVv8JeQrJOwd070BT1AdIQ8IMNSvfRPVRkRo6PgwCFI4\nC5/G1/OsooQjPmCiYmb3oIq6XEhc5xywm6Uhfi1ymGQRT/jEuula1ew4jncE\nVmQPGsRmBotnXB8Dv2UV4WmXBAKMYW1jKNh7/KOVv45A9sIHosQ3bU5ckV/u\ndw6XgMcDWbKoFQ3DBDGzuco/aTbdOUxuu431vqZUF/8p4gbeApBov8jZxwhR\nLFgxAfaYeu4IxRPMMfnMVjP/HOii9WHSJSO6conr7E6WI1JLJw2V2HzAk6vj\njg89QtaGEaI7Kdvs3hce/Qt+jMHrlTvcK8xdUlaEuGv3GRlIG3lNKNsHNzBK\nMLhx\r\n=8lmk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD/VhYg2hCgjXS2Ulb+0nwIvieTMb3lTHPgy/w+oA12XQIhAMYVz46saKcQhDuzT8dQicACsQI5BmGOWHSAVpqyecBq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707081308_1530951400509_0.906802362658143"},"_hasShrinkwrap":false},"0.3.2-dry.20180707082114":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707082114","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"09097c33077e2473291916b017c0190a4510d408","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707082114","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C/0S79ezx1X0lSR/bFxDDrXSHXJEi/7kqDsNeLKfW0rPb+5KTwFv4622YCnL7++uIeIBtYlUBDCwcD1On5dlkw==","shasum":"5f593a3fbce14a1a4006ea5e8206f7fb7be25c5d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707082114.tgz","fileCount":743,"unpackedSize":1698237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQHjOCRA9TVsSAnZWagAARJcQAIYlWONLWoH959bcJJQH\nw1JjwBo3kej28W2r+fU4oxFu/S043XheVFbWiuQsLamrAZuVl+2sxnAgQX16\n9Mze/kDiUduyp0d/lzBxusPX3qst9wRuwD0+LrxDPeYGkmBBNkY4oo0hl1bK\noZA46+EGa+9M0HwOGP7O0NrRHv00AaGu3uB1orjgRdPbiExtzxu2BXZjpXEe\n2BVvZsIU75b0zaPnfkouNjiWDlewYAeB3HFKTrCUwA2H6tNEVFOuY1LGuCaY\nK6CPWOvQ7hMgXBOvUl04TUx9xoCCzdUeeDFJw97ghL2Qievg0VuL31ca0H0h\n44UD6NdpcUQnDpC+MZwlG+tepYNJf4lTS82TEMvOH3TQVF0HtIjA5nP6H54t\nZnSzOhHDU9uQjH8opuJfmu9OFcCzzyMUXHEKL2pOonb0xe5M3vUuGknfgBfO\nT++mXgM4IhIfrA3b74rE3wrXb1CZr6VAzHqW5WZ/VhlzE0VlYOOfZNMz99pW\nY4qv3rmByIeES3XvqCI3oYoGTrDXhfUpGChgkU9Pt78SkZpfmRBv02fPwNsn\niHO3T9GYrw8c64UNWXc7YcGefafdV0AEGUH5BtDr/4M9LOqsNJ/+TsFwyJsY\nCk1CTC0wpGgnn4D44wnNf7oBH5d1DigDtfC67KKoDpNjtK06mbY7vD/9DmAR\nqwKm\r\n=qvMQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID58tBPXojYC0ueUPeUFiGZ/0H4Z0Kf2EdPkvC6XlSnOAiEAgGyovmyEe+zQ1VTgUJb3vE0q3MHiRGzNf/oN6OS8kOA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707082114_1530951886623_0.07421065986395425"},"_hasShrinkwrap":false},"0.3.2-dry.20180707082827":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707082827","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"269311b34ae92833fd8caff30b4e77df69a72074","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707082827","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xiNcNfrsV68l0mRjbjRt+rK+ef0uh91UHoAPvPydHpNqiwyLwu0Z9FkKGi/zJklUW2amUYnxK3Cno6xi+DXbrw==","shasum":"2769426eff9025cd8828d172891d35fb72afcf7a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707082827.tgz","fileCount":743,"unpackedSize":1698592,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQHp9CRA9TVsSAnZWagAAzacQAKRjWFFLxg3YVW6Nu3Sm\nl01enWfM6PydimbudvJKO+wPRHe5JF01CCqjYve/hpqMxgKxodSp7b98oMl4\nIczcVkv0mVqzse+cvBnsPR1jyRzdNdDQQx4ppAitPoZNAIijf/18DCKM4ECM\nwAsMqdoBo+KAsfeQgO6HscrKbaG+vnTl1IRbqZZlUBeG7x9wHfJjYlVaG764\nsnkuY3TlXml8HxFC+rLTRJOy8axz97AnF0Cs/Ul9P8d6lsefI3W/1/Qm1Ba/\n5lUDKtfr+ZwIc5zifOvC3Gd7lJ31O7iaSBrGB1IdbVkO+/yKOvdf/0bu2IFx\nPtliuGXkd/7uy+Po0VmCwWHD/U4YkvQrjewPay8vEacKLMlQTA+akMtlq9iE\nfgVBxKkpg9CfHysVu05hVBk8g3VSGEl+2w71bK92PFKCO4hoCizlaC/9WaUg\nx+u1VqV7JUbRdG1I02A2bcVEW7fX0YtpXiHoyWhS2H6zvUlGPxcBsZXUslRH\nFy0bi+MMTpPfXl2DdKx3roBXQyLVI5aVI3VU98Z7LUQyTq3HaTNLxnT+tr8J\n39w2MS+VAmSHOBLCxOkLlFWgDUqof38cY38lhe3V0m/pNYTSLtRMVAYRtBGp\nni9mPIVLrWOsVSYYzN+fPaWbdj9Wr5T+0SG2FesLkmrp2Pe68YK92FLKvdHO\nSeRE\r\n=ez97\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFbP8fDSd+VXrx024DWrloF6adlyllW3+FQ6rYKigYKNAiEAjdqmx1f5+CpARKYN9St3PZ70vOLlA3eXDVB9Cyh7UVo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707082827_1530952317131_0.20780105558379636"},"_hasShrinkwrap":false},"0.3.2-dry.20180707083101":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707083101","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"240a78f812910fac9039b4860553dc59d6fa9c1a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707083101","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HwZsacN/NsmiV3aTJDyktXOhe58h2CLX/yYWoqFCYuWYb8OosyRW6lx7NCMXgrg9D6XsA++aZyhSLuF8Hu0ETA==","shasum":"e361d7e08afaaccca13824353f2e7642ebdea271","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707083101.tgz","fileCount":743,"unpackedSize":1699073,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQHsZCRA9TVsSAnZWagAAQm4P/0UtD30nm5Crr/zagvaa\nXX1w3PpYkGm675Y8cJzgDYVzEsSCnA4Y9sRBWXIMZNPAxXB81NuFZWapUgty\nWuZw7ZWrvFtEMjr0VgJgq3dXKPPk34c+05rQq3Gc6scMBgF+lwXWSpa+Zl69\nG8ByP8kQJvxw2rn1gkQChw/fP0qY9LD61HNdTj/+5HXFjPe7kMFS22IkPUzo\nqdh9guF5GuliXUch65nw0j+0fbFKr0VMsHcYesI9u7iYD3aZ1AbZaNe7xoZq\nUdQF0hjQWkplJuqRK7Fz0uZlCaz1LbJ8a+5sxfo8qyuyqe+lLRBEhSmHWzg0\nrMzIcP5uYuT6o8JCtceRIBH1MHMiHkmS67PV5ex7o5BbAJ9KmFYbi3Y+Aftc\nH7Ew7dbqjZ9A2k8oh6YNJaD6i7K1r7ISHwRQErrPBG5SK1lv48X+uu6NuI1J\nluHfgq+a+h/USbz61SiF8IXjUbdExxbvG/OfYSJ/mUk/sN0D+19rwdkDhEr5\n/VVWaKE+ZT7I5OUmtwF/X6BcF5K5elXSUzLbO7XcFz77sdCYauEMHj7EU4fL\nNhYUjU5lzEQyzfLFZ1VDtbsqo4+j6WhnWiPG2VeydNdqYY+eutLTquVedx3c\n2mW4bt+1L6epQRicosSXZF7fhsl3Tr9/kBgY5HlbJ58zkqV0reyFQgmKa3NY\nwSZ5\r\n=ILQL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBLmO1MWOa2lURo6LTncQt3377OlOfgZp1Q5wpJeTujoAiEA5+nP4iO+8iEE/Vkvv3lqZd9JjzsfVkH1tJDMYskJ5Ec="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707083101_1530952473634_0.23017164583280292"},"_hasShrinkwrap":false},"0.3.2-dry.20180707083834":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707083834","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"686d4dc6599d00471786bedb3771f08b54e4d651","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707083834","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Sh5eYqtoI8wjz2+S0VyGKTB5AwzxWz/Nu1BxqN3pFLn3FykRibwsY+Aghw1XZYfC3qx4X52MkZzUzUDtKjweNA==","shasum":"3af760f650b454b1c725115629ece79aa5bd0602","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707083834.tgz","fileCount":743,"unpackedSize":1699306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQHzQCRA9TVsSAnZWagAAnCwQAKR/n7mDJHzDYRkhJwvd\nmhRWtLcD84EBMPzsB6+DemZGn2A4kd3RAaDEg679Ht8d6kBOxZsaiHw+e9Gt\nMwYuzH5IjZEWGL0Q+zOxzby0apqddwhkwMbJ1FZGvTZ4ZXC1tqTcJ5j+Gfr2\n6zq26BbK8FuoNq4StZP8oKDzAKzn+/EsazxYiESFau8bMBE3ZekPXEfpQ51r\nyemU5c4HnaOrIJDTAL6+SgoIQU2g8cTXDKzmmWNofhLxPjzAmH25gwCeTFJT\nhr0Qr0a3045Ouw8FaeNXyTPEkDfqX3Y5/qkdVQQU/6lYGW6Dr5Hr6M/OR3ve\ncMEVrmg27y5JPVkLYLbpzZEqnXm0qsi57cvhDetT8YC3cE7NSERNvDWbRM3i\nPgaCCQeR6A4L7JYUcCx8QBr8qu0DmLD4zzPUwZ14QvnwdoHJ0W7f8YECGL4M\nNN5GauNQkG2rtO/AuRReM2tPpscEnn/7rozA2PxZMLWUvQkZiuWxp0ghpbpT\n/Qh43fEKBjL/jLcHNObqxwLIKs3wP27ksYBR/EQSRVIYD2hdb/zy/UUgrj5M\nvuQvEB16wFRMyxYFKVIOZTeb/5dlJj+tcqc7U9wQ9ROQKNLWmIZ4jqB5tQe8\nER9hAvZJ/avaNuGfAIjd7o2i5tNCQefCI0xR8sG/QlXXbr5VLHMKtqCbELHO\nDxqi\r\n=ErB0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDyqW7uHsbw3OzMjGuwAPN++JH4yD+nhXn44wlOYcwtIAiEAs96JrblaHNo3bjdggJ1DGill01+TxNkoxyGa164+sC8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707083834_1530952911900_0.3477235325730066"},"_hasShrinkwrap":false},"0.3.2-dry.20180707102223":{"name":"@atomist/sdm","version":"0.3.2-dry.20180707102223","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cce22351723ca16813ac57cab6bcce630ac51f77","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180707102223","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qhqgNqcrnHwAPy0eZAwlD05WyBhMVJWWYMRUcrIKyCxhxjIK4k4vtVrJEPaQStvwvPzIDx2U/jC3DAeAVKfnUw==","shasum":"ba0a239b4c4f84c063b21148a882f78c7d5db4d8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180707102223.tgz","fileCount":743,"unpackedSize":1699458,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQJUwCRA9TVsSAnZWagAApvYQAJPBxCWbtVrQJv2hNh+F\nsneSg2F72uJgeRCWrH8djhap5HM3T7cST29akxr8mAHzKydO8xc90pxJAWL/\nZMO2D1f8312a7F9wjPheohT7Ktx65U8J3ZGlDj++z7rtLIPDckMeWdMCiZX0\nAENlYAWT1LT9xcJmNQhKe4kuhJmOBejZbgEFPyXa+s5SyeGVXA3QR5t6yjJn\n60jSFNoBSVDUuRxCs9Ra59WSanKeXoA1xMGc3vpKZ6tIxPO9iI1PVhAoEKki\nZmzb9In/MAm/U5ldcuPUf/s0bA5otoPKjrp9XTXlDwCjijHNJE/gwHSP+63g\nnEd76BOgOuQYLjRorbXLOu4yrebT8n6mMQFT3sw/q3FCVJ4YRio9LcEJQNzV\nkStKyI640nr7e/Nh6uRoCBWvQ18Hs4gvvQjOlEE741XmsdACm8ftINArk2cg\n2IpFehRbBjHxUk854QjkCFdNrG9XQk8Di0XSvRkNjlxw8L9WnS0whtncjXBl\nufW6FsdQlFFIKTj3Pimy6yndeDVR8h3mipKk4Fl0aAxNKxGUWWa0aqatxhju\nImrQhVQ1UbikQc0KjjTIR3rvV++tCToct/glsh9Iw3w3S8izHIGZ4DEm189W\n+reW3t83v8yZ7Q0sLEHkb8MoU0+Qt6pMZgV67Joc02WGZYzp5v1xdGRRcma7\npJnG\r\n=nCyj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAOhf0dT2CX42pIGYHHRUpCINRIjLgusOzV+9YBsPCPOAiAPRqJjgvxV+qExZ/AeG7qyainOKG+7xiAyClFriXGh9w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180707102223_1530959151874_0.28629085899796025"},"_hasShrinkwrap":false},"0.3.2-dry.20180708020623":{"name":"@atomist/sdm","version":"0.3.2-dry.20180708020623","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2b08c690e7e319edb7ab060f90092b34fb114d2f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180708020623","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m+U07RZnKMB6M5fLhlZNwd8nmeR7UmZ4Mc/4yT4ktc6vg4RpR0AJdDDwGvZsewDa3sCGqo5/lQH6rq8AHNmCWA==","shasum":"33d39ad63ae238d772be75627b6f3bb00fc3565b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180708020623.tgz","fileCount":743,"unpackedSize":1700526,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQXJcCRA9TVsSAnZWagAAuE0P/35L7+6P62lMCtywuHBG\nz3rPhXI1wGkAA82lvw/VsIadV1jZfUpFldrA/HUQez/mQEkeXzNUXgpFQ+3o\nnVrMSQtmj4aV6npHvivSEXXHDrrjpUrOdBRif8ddjj1zbi+8xU7Tuf32KThe\n4BDiWTmBieVg4flvDC4LRGEPbQkMovl62cmgxmuBYaeoWW25mNkoVnQFul+J\n+BpUQ5L50CgW0KqbtQiB2RoYdfSyicPALC847QQbxcOeGhDetEZJftQwISV8\nuj1hcKcHVxkWEOa/KRmTK4NJJfeDeirEGGXvmEBPBS+iv2NWOWgKQhu9cplm\n3r23WD14avdnlUb2ZB6uInwb3kbo+Tw0kiVpwXX9iZEsOuHns0TtUOPVybcT\nQeag4tNq+3waWBh7C1PjBtJmoGAUSd+lpBewlG8FGuSf7sRJ0BKSk2QNgmPd\nAwRC0ksiM7q51bOrdEgphPrjnK3jwn+Q2Ez5F2y45U/DhzXmJyERAMRcLci2\nRsKXTCulShxmOmxT2JJ6XmKqVrfkn+dufV+p0t3NK8qm+S4g2uxdaRx5IRiU\nUy2Tub6OlK+Bx1Cow+JY9Di2Lvl63Zn+mFBmWRL67GMuVvs+iZj+Qff8TDLK\nfVKSH8qURYgRpHXOv7dWX2yOIg54xIU2rsq3RARAdlqEsLp97A/Cw5ujQX9Y\nBDu7\r\n=YlIc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsJjXtmJzT8LvWMHJb2dwpXDkFDwn8Usl1EWtF6CpaiAIhAILarFWy6g3ozihWf2UoHTeRQn90Vb0aBo+hTgNeVDnU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180708020623_1531015772404_0.13802163392756928"},"_hasShrinkwrap":false},"0.3.2-dry.20180708023417":{"name":"@atomist/sdm","version":"0.3.2-dry.20180708023417","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0eb2d000b5e36a856a4d81bd44a3761de722835a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry.20180708023417","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/cB5sSzjk7v65pMuXNzZIhVSqyF461lcJNvjJi1zBAON9zjXgL+YI3xMCVPMAlnT1T8YYv1egi8hk5njQbjJ2g==","shasum":"7d592db58cba33888738beb8c9475803bbae2e08","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry.20180708023417.tgz","fileCount":743,"unpackedSize":1701405,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQXjlCRA9TVsSAnZWagAA8RYP/0/tGwEBJWFRG1Dj8Fzb\nSZauoLKVXNPbx64PZLriAbRzpRRA/k0NohSQqOL5X02/oGM8u3vcclIMDyV4\ncZeiMYTkNgm2n4xJEA6YueQMKmUtpQ2hXewyM2ic732yaOvPOUdCDmx3l8lu\nVWsRNFUzholShfeWhvQ/xzaLIWV/AxU0Xz3oG7+g3X5C/SFYdPuNNdIn441A\n4qiuhxFexPtk4CezaNZ0i4wJMWHsbWVRMjUNdb5Zh99CMKsFyJrFKtbmVMiu\nNu+h+8V71doF+IWLZxiZE9yaJhjJcGqeTEmv24X5+xX8QqFmrHGj1keYm/HY\nxJ3NOnuWzVhC/pH6CPVe+ACJ2JRIzCsGZGC2SS2DY6139Qy+1XJxF6+VDwoX\nQrFafvFYKwZD/+TRFp3xgqwRJ/IjZuP35Y5HGqZCB5NKl1/6m9OJK2xxjjFC\nN6MZRm5bR2n9SNLEf2p0BHASvmb1dCZR7DeriE7uN+FgVrgKYCnsLEu1L+H+\nXS3ArbLPpw8lG8eipOBKImZZ3DEzLtBInJFAHtj9TDFEk0B4/UdnqXgkFmjQ\n1QIx8sNKyitwNBbHqZlnQVpgwL+fU82u4YJVVubeZi+uEpAZ1pUFyz0Bzhs5\n+XVDcS4mo9SPHvn/wxX6SaDxsOahzRRbFao/wVzZRe+2z1Dg3PdWx1+PwkHK\nn6pb\r\n=0z2c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAfIHVMTVBl4MPE/yi1lb9kjRC79pusBuBlmsmoVOfSMAiEA+2BGLpNqoYvafsNBpmvjTd1+wFIjQ0ZOxz6pAvRr5sU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry.20180708023417_1531017445809_0.98799848484481"},"_hasShrinkwrap":false},"0.3.2-dry3.20180708083849":{"name":"@atomist/sdm","version":"0.3.2-dry3.20180708083849","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b8aa7ca64164bc1aa26055095be4e0c878412ffc","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry3.20180708083849","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2oGm/O9F/8wVYUwdHlcnWTuCn7WkWt1JtsFhtqiijxHinf+arxs9yDiwyd485mO8aMUFTASLSZraWRzfF/+T0Q==","shasum":"3450153eefbc95d68f30100e381fb6e2f261aa73","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry3.20180708083849.tgz","fileCount":751,"unpackedSize":1715783,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQc5aCRA9TVsSAnZWagAADZAP/2E17XHyFGzsKsjMA116\nm1NlJxQAZey80WejN0ogcVn2eKQStQfHZQsgZV4hakjNKbuZTHEZZ6ZVel7t\ndWf7hFcQWLrehysLAkNaVOSPrRBRP0sGw4C0+T+6+laoV0xNRJN5Jnpx5hhI\nrIggSCiVuUCcwCDScxu0hyWsXsb2bYZ9KjiYL/+ED42j/cEqCWcVu7tnQSq/\n3QcRyzl0WHRwN0p/OPEvkehs6VlJTeN9vhsHPx+3hJeQHxG0/5WeAnCszlGv\nP75obZRWz0nW/58ebWJjVnr4E6JbELvhGZpANL3F651O1LYMficT02vKj8ay\nbh+vWK2wxzZHrHq21cF8ibrrp2DiWPRdLnBnndlsbGGXrJn+u5iEaoapoFsy\nl+kVjCUQINqDUk4V2FyUGUXWMZj1yMBumSdT86yVnbwMQ2RfY0dloI9hXpnY\nQ1Bmcm82ZIYLj+MCo7ci+wLLPvwZuXBWWx9dOl3EgjPN/JZgd5AaYelZZ2LQ\n/NCGLzoyy+Y6gVApjKN1wtOBEB1Wy31LUPNszImOEcrnNH5PfUzz8beAvuAz\n/BwEfBFmpLDqrUxbBu8xQvvxTG+y/p0/WtHTzSbrkXyHj3KtutO+Ry4aCeff\nlDJv5KUpkFVUARw3aKvYlWpbuwJ1tqVF+0VfxT46or/DU9TrrhRWZ9qEq7pU\nEU4M\r\n=LcLH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH1miyLdTpy3PKKcRF+/+HNpv14xlcANyoDAtWud6M20AiEAm8frxC0f4kraAbnWYw1+Xh75mVYIpTrYF77khBa8lpo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry3.20180708083849_1531039322750_0.2714173364671779"},"_hasShrinkwrap":false},"0.3.2-dry4.20180708130221":{"name":"@atomist/sdm","version":"0.3.2-dry4.20180708130221","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1909ad1867d8869070b3f712be68c98742628645","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4.20180708130221","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oNrlAtUWjIMWQ8czIE2XBpvAiVW4K9HYzyApCPr89jIWeoh7b5AI3zQsIovzPMkVEg8rRKCM4wuUtEjFrjrK3w==","shasum":"d69dcc609621066776968c9d23b5ef785f4b68d1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4.20180708130221.tgz","fileCount":743,"unpackedSize":1703387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQgwlCRA9TVsSAnZWagAA86cP+gKyUFBH8HwGnByjwJxd\nTNYLYuQyzcOuKRNNnW9VomNiIEydQYGAQ5yOQlwj2dleW/wmox3K4D6ot46K\nVTBbdqjqIDsAzGPGDtyU96EnFEJkRVEnXbJ20dnRMXryfArGuxcB6CeW/rH3\nqEOT3PIPyWeC5BsaoZHR0TO8OTq6Amza4BaS+GbFpF/ywHE3rRhaJiokTDZC\n8TeAnLfqnTKX3+dKnqGqNBI0QExhKEqclyoBmQUHaWpss/+sB3ZnS9J2hyMf\n4B5Hp1icP/Hs8FgQ+rJ0ELkB/NsHhAVSsyk3HZ5AJVt6F2Wr+GayGHVK1AfS\n4pQLNGCxPH4vRUE+Tn4AJc8CX0mac1NkWVIm26IfhOWIQeimT1MN/Ccg3NwL\n5QgeW+R7Zy+ROfwXRcJmNwjgxKNVHYLOcj2NnKrXcoB7pn7LHUR8Feu2vhJ9\n45gUiDjvysT1f99YBPVpVqBEp2RR8fBDFBPKdw3h6D7Z8ifXaIpuUMnFl4xN\n2cXWGhypOrjfIf0suQOQIez886FQw8ag9zUK7TgQl4bZ3ln/Z8p50HZFyw+q\nNMgSSiW4OqFzKm5sCk6qwNQIiCmUyU/ho7TZnmdb1BUQ/vP8UYcowsPnEqcM\nh05/SF1SM2JQcdUgLSDpavEdHd5qUMxfVHi5pnOIacX8aBlSrTPzZIu3sCpn\nWuB+\r\n=Qc0M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAkgGBwcCOAUDqPFCRov80t0Zfwm05CtLOSuXtej3/4LAiAhfG/MfSHXO+AhmZbzY+JLQWQdSbjvEZ8RGNGfMIsq1w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4.20180708130221_1531055141591_0.5837035356561975"},"_hasShrinkwrap":false},"0.3.2-dry4.20180708132247":{"name":"@atomist/sdm","version":"0.3.2-dry4.20180708132247","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6140806ec3b087a0adf8f6ed458d0f04e906a00b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4.20180708132247","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BnenXwC58ujFWuklNCNCnyTjpoeRDSzd4wD1Xu7TFVI8s2zBHg2meZIFu3ods3qrdGytJP2zfaeSDpPVThoIgA==","shasum":"19a4dae3ba67d7156a43e12c1b265a697c13801c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4.20180708132247.tgz","fileCount":743,"unpackedSize":1699872,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQhDuCRA9TVsSAnZWagAAMnsP/A9mJkeuUj8F81EehJ0L\nD309TW4PeG9othaD+6Ad1vcnApkEwxpKUDrGsUSb4hnlAjCPKITDyLBfyvA5\nw1nFzPvD7G4xDUT/zSQewwMvx/LVfKg0v+S4XqEW9KrURvWEzI8+zgOTY05F\np/bmrX8w0Kb5dxdqRovZW/Rw23dv+jIRzAL88Ph7xryXlrrwmvwvCnpLCaky\nJKhR5t+FxJgnSud57L/1bKUlIGKFgr9H+Ldj7Yyy9fsVq3nBjS7eem9Nic73\nIQUIZISeOHA2dBz3ofZoqmlKmOnWtt0FZBPycTAAuMRgwwW77+e/b3WxBcnw\nEyWVwSWukjBA62rGnhbwC7bNF+vnjXQBfCBWtHRgJ3ahJtTZp+VPJkkYBZSS\ndGm/6S6pOVuAdMa25wmxVy23KzSosv5SkXn8k15d0d2Gym4VUaNA3/rHMewk\nzZqonbopicVXqZv+s10nNva4YooP3kDAg1qI//jnrFGypt9mp8rUv8dueYrW\nktoD4nNKO5Sf1lUfOeaHbGjBjI2t7GUHkOmzhQptIjYZSQCGPAIW1OvO7CNL\nw9EOgLa90R2g4qMvMtkMV7HeraaPTh1ss3E1T2BA6KdgrDts1ZJA/0mNApsr\nxghbGa5kPksqfCSOWvbXC3TAYh67Vkiv+WPslqSBxLoeaOzcmbu9bL9fScTp\ntZZv\r\n=Lc7G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAJyb6kus6kP4wn0MjL6dpeuceD+e2dHog7ACWswCEOmAiEA28ah2PDx7/7QRz+JMLivNZuE0lOUHlQrM8AKg0seCq8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4.20180708132247_1531056365739_0.005409485885475629"},"_hasShrinkwrap":false},"0.3.2-dry4.20180708135227":{"name":"@atomist/sdm","version":"0.3.2-dry4.20180708135227","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c3a5bba7067c03072917eae1b33795b9df17a4b8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4.20180708135227","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WiWQ7aDpdqS89No85FyqP5TrvboCimeTGL0o00pI01e+aX1eBjZO0d7Z4q0VmtT3KrEiujI8JmS4VLDYs5ygaQ==","shasum":"46f4e4b032dbc1abc228627782b9c1f2e5c688bd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4.20180708135227.tgz","fileCount":743,"unpackedSize":1699638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQhfcCRA9TVsSAnZWagAA/wgP/3PLTcCisysm2A+d5fil\nDuMiQNwEpXODFj0qTHOAfLW2VNYaAIDhW7fBOHeCLQCeWoW+9VW4JDQt2nTu\nihuGXixcB4Q5+20RNADJJr+SIkzWPPV1CO+TcdDpEoTOQBESeEk9ZyMYwuni\n8YypTUZZfWO0HuFF2/JBTJhDwYWaa0zRU6sr1iBs7XAjdeNAyA+xSCbr/5uQ\njsTXigMg205nRQgPMha8NAjYyh52H+cUJ1OSe2yIiMBmdrbuspaKk2kh/mwS\nMmjUUSWavFj0ITJb6HpevuKC8vEYNbTehBplVNvvQhgqWljz2idiVrf0jP3i\nxJD1cMPsbvrG/yAAQ3IgwDrfbSRaI45REd2KM238286jCPGZhKEH65kdmeg/\nU6iGnwJQGSa4d4r1by2X61OJW1eeL2dMXKBZNmRGMYoXPwmjwTbTYMB6t1td\nrmFw5sqr/w1ZZ65z1Rgj9R5hQJe+x02PIWbarUhBq5qgUdp9hh/dQqz/km9J\nH3oZNkiOclfm5/V6j/QqvfCZ3wNfIQ0R77bwG2nLYmGVAF52w5V17jeZeUs8\nRasE2HHbFZL8ksBg0C5MEmt8xGcqRxQhbCj1Cxxb4Ijqwyc1p8RPYjrIayYt\nCQ3Y178bCfLCsOijNZcKdJTafa6YLOihQUqdQ7o7Zs2kq9tBXxz+GjNUjpn0\nQI+n\r\n=yBRq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEK1nWskDg86pbVtKhmNE95inmtC06ioKhbX7EX8TlRgIhAKzfe1CGwRIIXc+Imslnv7F3CK+4gWmhZPgqAC4TCxgC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4.20180708135227_1531058140620_0.5150884896416914"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180708230924":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180708230924","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2616afb44366fd1b73ddf666b355a00b62ea7590","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180708230924","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ITn0bzPqQjJfZQR8FYzmwx0xsFWRSca3kyRzjKQPg3LIEVZ8f0IV6SX0zA+HYbv7LfaGmxKVo/a5ZfIXQD+MHA==","shasum":"b78d46c3c1b1eb113aecfb6824d7ac15e36548e8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180708230924.tgz","fileCount":743,"unpackedSize":1698957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQppjCRA9TVsSAnZWagAA0FYP/3CW5/UXyDf/LAcyYuR7\nMUx0y9KAkDMAzJ2jJRwtnaA0wQeLEEbhSSMdNFaCEBgezDytEYzhYGUB3Vlt\nUkPwuHdcBB+BReURn5PTuA067+vGtDWllywOJQL6W9W7Gqza/paraz43a+/3\n8/DsnnhLCtyCTaxaJWxCeHSuP8gxvhRp3umYr9TATbpvcNrefkwhHmQiEitA\nWSfI76PdC+MSlGMKfux9fYBs3luBgWHRUKVs+1z4zYKbWvYwsm/yshJAGlUl\naC7S1VlhsrKnufiv/wb8vjOSQdSo6nc3b36U1K0v8GC5xZ8Gce+/jaRfE01c\nYWTa/BYaVwvwWE0aXq97Gf4FbFydh/BwP3St9qcU2IytzKcdNPyo9DkIefIu\nP9uLzz0RLEzKCX1NfI9Z+ZcV6U5NRxhmugGcVdTrMdyDSCdWHs8+Tf/Z1gHT\n7cYnc/LzjkW5CLSUAiXjaQg4kBChKCADqDS6VML7k5vSiqu47v1Y4k2TALwv\ndopda6YlMz+lJLnJyW+zCmwEJyuVtLF4Sxe9KCz1Bes2XRcqS6bTlLX55nI8\nvGlu9800+6benU1dygrD96L0tkChfr51rhJ/NkGoHd4zdCVzlp+5Bg6p3cqJ\nc5pPfCwlvYD8Tfoj8k2wOc94mRP2G7r2/tatlChm6crEwGOC7WX3GTr3fzgi\nm5Rs\r\n=kb4K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEnsuMqhhkTvu7R8QwXUcGSwK4LglSPK2OHeA6xrD8vKAiAptXWdk6VaeX9407merGgFL/AZnH21cnPVAO2/iwU/sg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180708230924_1531091555490_0.3789554048288517"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180708233810":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180708233810","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6f5f5270431233947abd3ccfabf039773d449bd1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180708233810","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-luOqSKVinIJgZ1sExAZ2plZCEqwlsTXSO2Z9m/fTQbvSpneuLu8UJ6KE3XUlEwFSytCncWl40GFpg0I5B3CB/Q==","shasum":"54857ad4553f431c97dbca19c11a46a311480b06","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180708233810.tgz","fileCount":743,"unpackedSize":1699273,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQqEkCRA9TVsSAnZWagAAtbgP+QHM7cKpnTwLKY0bhPQB\nCSGxtU/imxppQozLIpo7ozqaZPKz3u6nZNThHBj+t91kMRVIpr55/+SEYBeb\nbL+2tjCtl1nzV/Pw0MJTM4n1+yFjNvKTUQ3HUBVedRjHXa4kGs9paIaH7e81\njWb+FVg8GZcCjw/GQhasOfAlocCm6Ic4t0dNTHfWGOl54/bqvsua3X+4bUDp\npcQVzbjm9NddqlX88ipt0BvPv86Z4WTu7aJoaBD6USUa9tfj6VC07k0GyUzb\nLU1z7CtCTZdlriw1hKMowK24zuQn9Kc6VfRMgY2PBQio93cc3iJCnZOFbHOi\n3GFNnGYOsMwuHIie9uJFxtlVP2Dj938Uwhtr6YakCdKI/PN3UmF/LWjlBbHH\n0c2ShwMHlg6VJLe7IQTzruMguNvo+i94UBdQPn5hD00wthy9T/Q6P/uAA0nL\nvaKlDmj71aOzjvbxiiLTrMuJ9CkyhIDYnZ4yqS6iPa/F2WgPPSiMvkbIR9CE\n1hJilJoYkaG+ybLbI0XD7kWWkTuPJWFf0HetJicwvP7up4Hl8LXFZyJlERND\nuMWcAZHHJfIDwbMAZ5xToBks0DX7UADVOx/1qX3OQWWKbw2ACoXlQ5L8aTXE\nQNMoZFKSX1zglvfaXZidllENk1xX+zoF02rswSqsgwg3omHqMD8stccfhfRl\nDXG0\r\n=VdOg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBO4Vc0z6z54fVSiHoP6Y116TcxZGLmOpX40b3qUnvuuAiAxQZFKwjcHA+j7a9Ql2BkvKkUJaqsKon42mdkkHjcLZA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180708233810_1531093284360_0.8585789549333753"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180708235213":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180708235213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"11450f9d15116f5668c4c92e05fe5746bcc22a7b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180708235213","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3DIVexD620RG9SI05U9XsQgK1XG2ddiD/1pFT1joTcZunHW0FkvuDphM9dVvagYbMzYe9t8uNLSVTRlZE9H6Cg==","shasum":"110a6d4535d0a606abd24d4337584470a9cf33e0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180708235213.tgz","fileCount":743,"unpackedSize":1699574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQqRxCRA9TVsSAnZWagAAf6AP/ieq9ij/0t7yKTJoJ1u6\nbURx1U3lGfp7+uvbgbgkmr4iP4nxZRNNje3p2SN+9qsEHzteYxxukG74O+vI\n+BR/jd5oGq/RXulppnteaLuQ9ReH+3JaKqvDhRnnwB+sSLgRNcwogGKWxxec\nZEThxJLI76pRL3slI0V4n7lGIKc1gR8ryGBHa+mgxOJ4G7Pzv/YKvb9sje0F\nudNsS9avHa2vbofYjhPpdhVvWpUPqoC2hhtI5n2GxnBSPy7b+s8NCIU+792U\nHVJxoRxsG38AU5aG35slmcjylJFcEHWSWlG5CJorwK6C1qm7w+y3ViMhBkyc\nztsX3tv8zmG0gQzxRQtBfbY0XbzbGRdJh+tq9YgRNvt363EJLkwek0n8GQwl\nu+U9EjVL+JQAET8bKGGLUODPRXC9tJiNbqBYTeazlu3g9AIA1/lXjKePHEFm\njlVDQSFdrmDLa92e02QAzQ0knCHodvdXMLNnOPm3JQfI3uA2IUUNtQSfxaPg\nTbiPTuuC0DcUe5/phBZTd9WlHM6DCrfoe/UWE58nWsDNH7A5ukQ/Z18gOkmR\ntJRL0egxDh9lKJU6urQJJdDJ0bSG1DQEwSWk+RXb01ay406bFJT0PhaMU5l6\ngMOHroMZTl++kKxrvdyPTalKAIfLP3YkB+dh3jOmuhYplu6L+4l7fpTHmQMl\naHy+\r\n=O8pW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG/bo3v36LUtzn9oLoSVypSTcE0JifFHFSK/wMnthD4kAiEA+KZ0+b9TaRa6zs83Rsgq7IMjeB+N7SPcp2M6VqZZGVw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180708235213_1531094129389_0.1747519132585298"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180709000355":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180709000355","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"108a2227f6baa3c135c74fbe0ac7c51eeb0aee88","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180709000355","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dgPl4re5jTtmPZFq2ymNQpN8JrTZgCjGOOV7fPPvkNYGktxI0z8RqFEUvD4bLPH9RbksBi58IJgK8/QTWHykMQ==","shasum":"1cc1874a00ee0b88e2673b529a6c6e52b285af42","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180709000355.tgz","fileCount":743,"unpackedSize":1699579,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQqdACRA9TVsSAnZWagAAZAsP/3nypyxsJhMwHrOvSptB\nwLR714dpP4J4dWUoNkOpPjHDRYxbgASLxaENUFbFn8/aOe2pRM1tJmVCkULm\nw8fj+6fcrGS1rP3uFDpIdrLL83SWL1eMCR6ffWHQI/HzpgJl786Jv71Rt5kE\n2gZwaMXJZK+rsiVT7LhKWCagF+dPXzLmZDv3IlzthR4tDQoMFLA26Qdiv1Il\nzX5Spz/ySmVxpO+MVihdOCdLYng4wqT1H27DgiI7lK5qCMu89t7zFpvU4bQ2\nT4EaQMFu6XGCIVjYQCUwdbBXseqc1o1xthhNsd9svMCfmkY4HP89ptRGz35K\nDfLdUqxoabjp9soRg9cOKrwcEy+MHtseVnJYuzbmjxpzNzM8ip3O0eH3XDYA\nvtqwILP9zzfpZ+sX4LRCb78GisQtH8SpM4HlPSQ05y+6mBU809KXCKDs8Dcq\nN56XtKgUq/sz8DiWpaa5Td31fqwWJbSRQ2mpxVxMlBTige9vfQot3tMpzAKO\n2jMMLDp7NHZfuyfDdg4+X2mN+5Hjyx1zwM1MitpvORvAhtQ+H3ijmNjtj5Vq\nAMPJXnW8qvda16KToD7GoKyAecQVwWTnueD9BNjsUMFLGHC80UwP/mnHZT+e\nIWK904IBZKE5nVIIfFXorZPOnWF5iBtIk1rqsw06rvOxlZ+w0q71WyUrLRvy\nhxeB\r\n=+DWk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICgIIDh7ZfhLDw6iVPx1J0dE/soGc7FrmP2eb0wpJtUGAiEAvmOP2oJMrpVAlVBuWbKdj5kTti7+x9wrxCirZrFxAO4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180709000355_1531094848468_0.8109034104189863"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180709004010":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180709004010","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"36cb665d2d7910a82e4a5ada6f91ddc810b9f93b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180709004010","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EkqDfka+MettgRco/SA6zd6wXwtAppCBfpcYd8gIxRaWrPQBFJqfuvn3oneJRwcDU4L7oJxL8WK9xq5ySSEwoA==","shasum":"5043b0b9fc3d6289ce6ebc050eddcc4258f88766","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180709004010.tgz","fileCount":747,"unpackedSize":1702121,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQq+mCRA9TVsSAnZWagAA14QP/0cjW+GIk0D8P64I9M8C\nYGHq0He1VQTarMYiY6JURe2mDpCzAUQvOUzhCnh7/D5+YjusS57yCy39zp06\n/kgDYyhtcTZIOqKEEjUbw3B/ZaKtRQZOccNpFTv9zbrWOO/z7a83ig18B6Ax\nTEc93LIq/jx/CKlkePJaiR58KBPun//4Sh332Dw7ye8dEoL/7lZtDRfbnkp2\nfJ4pKnmsfKn42Ty5WMN1PuHGHYN90frYLh/FokaXJYNDenfdkQl2VgJoSnoH\no+5CMePNnPiQdRtSSvgKcZnPewFMQiN821nvYHfbfW3zdnMWUrv5tdRmh1+k\nAh/eHWp7nurFS0YKnPksMLHltdCGkH3vfUGs3iR64o5rsEi8Ur4aGvb606dG\nv9UjRA7SFnueOZVsuywS/060kBm6HCECOmP9YgyRk75hL1XUeQJ/WcaunCx7\ntJUhh/eyPz5hX28izKVLuy0fGvyfvV/6Z0WQ+8hz5OUhuPcTZ8FoKME9tOJR\nL35YXjvJQkThChY5l5LHXp4PHkd9dUsM+z3U7ipx1vof2lFK92xcztC+C4IH\nlnG6cQmVYXTWE/KofLJ7IyVn+W7heYucRrJncwX9PX7U1JpxgQN6vKt+LwOI\neauMxeQBVip8Acwdcu4sejXZWPFxX30jgCbj0TShUv7oij7NnYH95wq3MhzM\nng39\r\n=SJvp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICUwy/aMWtEeVsGnxe6ToWj/wElPYkiIyWwrgV2wlShuAiBlRW9E4rdu73OxZf/UdwpmLabLEN+0vCTVeKzYoLJbLw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180709004010_1531096997999_0.9098354922760346"},"_hasShrinkwrap":false},"0.3.2-20180709010018":{"name":"@atomist/sdm","version":"0.3.2-20180709010018","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.17.3"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180703131820","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"18b175aa15be730f3ae5d8dfd28bac2bf2ed8f02","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709010018","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Kso/mpbxa6fJd4YTZn1mjCGiISSSkaemNa7hBMh7h+RhH4ZdFVTSYzrgJqBLBwDNFgMVj6QxLQ5FF+tyamYszg==","shasum":"27275e34abfff7137c9bdce28549fd94576e0e69","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709010018.tgz","fileCount":718,"unpackedSize":1674926,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQrR6CRA9TVsSAnZWagAAjxkP/3v23D09uNXIgZPZu9n/\n4nTvEA/qHIz02mnwkJmofvL5DA/wzMRdC038MVi7iKcsZH/PtlLlmQsXq5S/\noBsPK+xBT/3rdp79GSDDvQvz9aUXFfQS010TxClBV4CSXo3G550J0IStrA3x\nEJAT22+rcYUsRxBzjEwRbDhbWqFpL4HRRNvFvtTSKxOY9eV1hCyTj7Aqq4sl\nOmUplROSTCoIb4CdazB55gm5dOznc9g2ISgLIE/GJpqDEm7D07TjEQsuVp4W\nTilZ1LIzb8hAKo8yYsQJ90lCw2QQAd7xtQfN+cqryVw5dCdp4/A9M4Clj+9j\nVGwXqajguTgK+MSzGyiwh+pVk5AtBk9O5uQGUKd22fW3UHN7EQBXl/XZ/CA8\njUA1K1ah8jqyig5Gqa9+U4UhLi/fJIcsWDOrQbyEzSMPc7qpw5i+HNP1jvOs\nXfOL2AADn12Dvu25oT7fcKXxio70XLY9Luk+Ot2oHFyyUDLFBu3QMd9YXxsN\nO3ZtQbk0UykPxLc0oXBx+xaTsulZS/3p864WwH2jSM3IfxFXCz3r54vngslk\nyjqwAr1MCb1pelB5L6q+TnAmAiOgjMxi4W+DrazoemRJrdtulALnRRQahC+E\nmmVkE/+rlS8bciH7c3GjCVF0BCUoTfEbO6MgR/Oe4lChTSpE91k0yvtgRRtC\nnwBc\r\n=uRAV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBGVkgASZ60UiQw4pCbD3aEi3dM4jU+P+u+JH0l+SIINAiEA8ymG07n2ZxNqNAF6oZJhl2XZtCIBIYzaF7KqXR/nY9k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709010018_1531098233940_0.005131380798321761"},"_hasShrinkwrap":false},"0.3.2-dry4-simpler.20180709014010":{"name":"@atomist/sdm","version":"0.3.2-dry4-simpler.20180709014010","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f17784b9440202bba2261f57becc7ffc7c15c406","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-dry4-simpler.20180709014010","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VHhCYBDGzfLlSkxG5HMrC1H6c+K/a0QKB2WQgNBqBVHDpbSBtuRfudB81vfkR8/lIUpROgPKz+ZeHYe3nuhnHQ==","shasum":"15571ba82839b80c39c4e1075ac7398c6537f27a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-dry4-simpler.20180709014010.tgz","fileCount":747,"unpackedSize":1702201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQr2+CRA9TVsSAnZWagAA+FEP/jUQmSWK62NInYPp6eHL\n1MJpL1n6ODaHpLQbAcoy0HvjZHIqdNTDbQPrTry8MG7MC1l3p+UFILMp3U5j\n5zCFxUedf3rPJelIPgU6lluAchnGCaOgQffil6FztbNmNhqI93T4b2kCHInP\n5AJAGFvtWcaf1j6noZZ4yqllWHcF3PZFje6DotbfWrEhd+rq8ga8buhVzjHj\nSksyEUElMA0D8kD1D3jueLz4h63Jz6Cjnm9WQH09UOlRhawcPxtqEl7xrQs3\nqqboyczUC3saQ4wZg8vIww+ffRlnbt3StSauI+biWGXogFjQFId+cx+ynF9v\nvP1gN5TJXcipf9VFisR755ux4DnkQ4eOUkML4JaaPUbnT1SewVOfKmZBV3Lx\nkWgHS80jHhaxKxIMwUozfAGgFWuRo+Bg5KgKUl/qezj1STsxnc9hGoKOx++j\nmv2DRRtf5yjmZhrP3XkEiY6ZdS/RQh39X37CBa0b+UKv6/s0nh0V5H8Kcurr\n58N8eHaebzzWdCGCoLWI0V6h1oiNzdK9hesrTL5awSmloql0W8EQpTNxuSBm\nZN73VyzO9/RQXa4VwQbrwqYtlhturwEbs7M4NOdH2FbMfcYmPG2EO2IFG+gK\n9oewoHfwhKKH1M7Bm98mkA5NNApPO61QkryZjXPpYqmj92NzmY9Vhi/VCDaZ\nYCJS\r\n=XshL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWAQ6eDRIJbEIZMKTU1dhrQhy+onXOvfxOKe8ABXK7rgIhANn/p1tjp5+c41OSXQZM4iTtLUCo0HduOXywOfFhYXog"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-dry4-simpler.20180709014010_1531100606239_0.16294099880840518"},"_hasShrinkwrap":false},"0.3.2-20180709021108":{"name":"@atomist/sdm","version":"0.3.2-20180709021108","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"673be225bc5088fb92c2398ed797a3e8dda3f391","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709021108","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/67Ht1i3JnwVo1mccXMtC/5e/s51t6jc1+YQo3pIF3auByYVTNyxyWAwCVFqmwZuPZiYiZNg3yPcz1W79p11xg==","shasum":"0b41f80e0d3533a1436c540fc7e0abeb34dc2a7a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709021108.tgz","fileCount":746,"unpackedSize":1695312,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQsT2CRA9TVsSAnZWagAArr8P/iQfixn+mWGlye74YqrX\nvYUfhIArxjUqgGhDcBePYswwWubaPijJ1XGI3psrrBYSFmwxyqh21wFiq5BO\nDFw0FrkXbXcoii1NuhOciOfHQg3ByO7rHUqjwEHmPi4oZiBHJe4UStNOSybA\nNMO/BFfJXL7aR7vDtGOTwS4TxDY2NZ+mvOlmbJEGHvnlQEMsbGVqVBsxwmlD\nYnCkD3HEtYZXdPnu2uzi/tBtvVNqCl6B3pYXKLiOU45Y5Kh56vdGImP7mM4N\n7wKFYqTH25C0xE4YhG7xky5Pez1MpQdzy+E74yCqn1H7aS9Ex58U2QX5XpWl\ns9jWvV/a6zs1XtA1gI+omhLjfP6RdIDXPvZG76qaVKZj6ExeWnqDRIjtRSE/\nW7QZWy7gRdg11AZdsbEmbpRi4emalUxDrKebsTe622OxpfCgRjFyv7R+bMBR\nfPN30njfUWi49nu11dWEnNJmKj7YOerC6FpQWLWriykxBWYQwsmvQmdSMQ0n\nEehgdn0rHUQ98RIMdIKiyV9jnRspvct+5HG8PE7Wv68ZOlS3PJMx5Gyf+Vmc\n2TxqOEyW0s4QOAsV36n0uVDph8CCgE3C5hPpFuSOEYw73LJ1Pu/ZpVXbxo9c\n0xd0hNH1tcZWAkP7+Kpolv+dO5GICC6m4FWv4gGg3iBR+euYN22cC+/JuGsi\nvskc\r\n=y2Kt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH8fUa27GNxSFxVDy5nersh8oNkGzEocHofMd8ICrNRUAiEAvoXjFhTzcve/4V0AyN3ML2dfn+HsMRFi3vCg8Dloz+8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709021108_1531102454356_0.5078445020482365"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709024734":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709024734","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"efa30ea7c8e272035a384270ee3adbafa313128d","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709024734","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-THfshgZ9/Ekg9NFmEWD31qWpOlFPJw1+lJTcphO9vd50fCs8AMLVN3Mre0EkGqOhJFp2qDaf4sXqSbdYJycCnw==","shasum":"92f9701090d33d528c4243fa245d90d56f01821c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709024734.tgz","fileCount":750,"unpackedSize":1703575,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQs2PCRA9TVsSAnZWagAA9QYP+gJF1bYK939TZY3tKae1\ncQNZcfQtC8M1KnJkklYchKIcASE5/GLolzlYxWeqxdhfaHVWsI66Oh5l0F3Y\ng1OL2SFSJecSfm4cjVwHZLFgqehMWRXC9+KsnVDtYSLcdcVNkvAFFUuvHaK9\nSgMtN3/YtBdIDCSZrz2t3sHaA8f4ElMM7JpHlJu5YRe+Jfebp4SjWf2zZnv6\nxJ1sIXhoRG0s9ph0pUXAOLj1kwR6nqK+lcSUkf7SVfXFzlTJf5xiJmSKIKh8\n0e/gSmBsWZcNJ9kkWnaVKp1PtTKDwqChN6O8f963C8wBPWEYfn6IIcn5yK/L\nuQVc9IUO7CVXg4F3Xz5Dbp5xnaK4xzu6H/eyYyHP9N6dIz2MRFhfhTkah53I\nnnhsuC3OFc6nc7ZNR+rIgGagoeNQ2s6Mi9K0uMFkuBsrKofrcCj3dWsPcPm1\nE5Pn1QgL6xc+YfH7CklLuYKwG3khUexXK9MAaqEzsdo1SnNxG3Dc0Tu2bYGZ\nNFsDrlr0veWIZP+AR0vlN5O0ExasM88McRBPQhoSfmzFBQyVAmV4mufDsniy\nBnqzdF6zUmHDSLqU2MBsMmTPlgBKrrNRW4GnNlKEytBY97uqz9JEeqk8TCTb\nMbbZbTX2mPvnsluT9YUfeG/2aB5+1CiJZcBc2n+xPmvgmFI/0WtUCTYEnS9f\nx0xk\r\n=M359\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICVy+xmcTe60YIlzrm3AcTr7IE+0oIl5+JSIhIglHz8NAiEAkj6CPtzjEncqIK1bfQCmJiSzWpcSk9H+Kmvibpr7tic="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709024734_1531104655516_0.821000995600619"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709025105":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709025105","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7d05a9e9d102f196e12e3d83ba5af255c6881b72","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709025105","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4Fr53TYOLv3wsfagzeJksDk1ECA7QNep3c+erbQzpA9jBeKjsfA458ufutjn6FEeY2njOUNIxFJgxSMOUDSBrw==","shasum":"6d90e813e45879713ae0fef8bae6ec54be09057f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709025105.tgz","fileCount":750,"unpackedSize":1703691,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQs5VCRA9TVsSAnZWagAAKtYQAKQM3KqM51B2upVk9WX/\nYZZQzzKCzKEJaB6IJPfdIwzf1+03OtWXQ/Y1odUIdWcydRb3UOUKdn/Z5PAj\nLC3Wb3yoMOSyHSWEeI4yZMYaaq0abSGzemWcpb6rLeorqnIobGDxYg3qNBIV\n2f3pZcArgWCA9qil3BYzeqG5FqnCAokU67l7L8Z4YKWSdRB/bT3HM2JVychg\nVzJ+ttEqWdjmNxn6mDj30VGZiRbAsD4F8EurVcC/TGcMyEvZLBxEE1gne8n1\nHLF/JrOskvIUGNYY6OLcQSVXgIGLLvyMyfvNZqgZBG36XEsgvjxw1DpUUtS7\nHko0+E2RlE0ofuJEeI3D2PdLCkYbn6iwxToNUpzd+UpeiYP5buEEM4TwNpEt\n0Y/DOcj/jlvsRh4xWHdSA78TKgN1EQh3ISVfBmJgAQGT+kcyHpmlaX6ccDnV\nJFFcpWPbzDsnGfGnEcNFEo7zhK0JgCGYnGy+0ACISOFvZMuk9uQjAhkklvJf\nQHkyXpqVGjovLJWKSfao0xiHNjf/9IN9CNH7SokuEnZBLawDaRDqbACuNnlu\npGu1t93nsitvHOdLFK0Qwzm2RRRLOZs9gCVyL+GnFOgpCPEGEiZT+P/P7Jbc\nKNZOGDUPP29iHjs3gGX7QMEQkxLXxLyLn41JrFn3eZQjtI1HaFAsva7kFJWU\nK36m\r\n=5/cq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDF+VwmxRqL2GME5AdSxjBha3+E27/dQoeYKpVwFKFcfQIhAP/uQELL5u+KBx47WmlDP8OlOvgqFmLnF288UhuxpMF/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709025105_1531104853630_0.2885516212250263"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709044431":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709044431","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5e2fc75a20aa99cf985280ca9e1b592a87ccbd85","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709044431","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UMyPdN+6ruft6elHcjawNv+50kjFx0fKzJj5QI8tD6w+a02PU95LBw/ac/mtt4oQn+ovo7v7Ey/dSm2NEFkwCw==","shasum":"82a76be8ad80bb2e151f9349eb1dade27e164fcd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709044431.tgz","fileCount":750,"unpackedSize":1704196,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQujuCRA9TVsSAnZWagAAeekP/37fC7186C0TRNgpYxsa\nYMeSSxza04+GDWKzqlsnyujDp7VARvQQ4JItWaqf5tjAYWevRX47TlnrJEdI\n+MQ216+PZIkG9SLczOxt9vywLhOM/VGCofDnUqjuAQhb6OdRgaN99ykdwswT\nf9Co46VOY7UT8FmeK78nAOm8l0UrG1Yrn3UsoU0lR3eSH/JA+e7B5LqII+3c\nrLR4qhpGpWB5K8gWq+9Ykq+ajUpApVHB1n5v5ih61zktf+gYKsBNucwplmLb\nqsN0PlWM+x0BKmDwswNTSoQ78VFTnS7poQEGG/N4VSVEMSzZzFPUNIgIxMrM\n47y0yFqtkmNyOuG0uNWK+iaSxEcxSpJ4BeSvLP98WLHoKjdEDr5P1254WrJm\nK59Kc614ao+zBzkjk3U3jBvclAGW0nXw/wtfUF71paelJZZ5QLNz06ua+h5a\nqift0RBNCXG4jaeIXJ7Vfjr1wIsmC+IN7lEUMyeOsWB8ybO1RXT/NEI4qkhM\nJT2iDKjCGGanBvW8Ex8s+WGfpw8NP9cx3NWq0ZlNUzSTAHy5LzSsFXFWd3Kn\nLfpE/HU4XDiuA5a7hiKTCe0nEbYT2b4rxsnW8AZK/fWKpeYVRkEDnvnGkkZO\ndFN+Uq+Gx/bW20CNVpj1djEC015FHMTiLbX8s0s68dnIhY0i52mzfs9xxs0r\nWRl3\r\n=Uq2L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIApcnHIHLagRtcZjBrK5gbgqL+7xpzJroZBXQRutDJT/AiEA6EtmnxYaU2bXvi3JRx4AONBSOaPGnygGX6h7Oi/olxA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709044431_1531111662505_0.5912785070613524"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709050213":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709050213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ac71c3b9a750eb239b3e79c49b3b298d63b3389e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709050213","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-q7RAn9lNwskGEuPQsWdvDh95UTU1864DHY4G/An+DTIB31f+DZY571Ez0OA2Kt4lKhQ7C0j/3LVbrs33xvXkaA==","shasum":"3a9679d7bc49a905963b07147216a17c5ede325e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709050213.tgz","fileCount":750,"unpackedSize":1704188,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQu0KCRA9TVsSAnZWagAAov0P/i79/+kZXfz7pVd2fX2a\nM2/WYLSuHrBwspGFMpkAlPWYhRcxblPlAsCgAHrbf9fGwBy8koOlJjtrxoI5\nuDpWhdEy7FJuhJ+nsQA43NOXiDIcP49dtuB30ps1+hrBbg9D/sjBwkxGyTzt\nAEhnzixWPgPvGfaNW2dNhYq8xuzJC7DLPZcit3VUrlxLDNAF9Zq9c3BgZ23K\nPBjiPoyWhgci1NHMCb8bGuzfG5uGmtD2ryw19sNyP3h5Z84VONaZw7fR6A/c\nMXdMORcRXKKaIqTlIX10Bj2Gw+tYgTzb4cIYjQbk6kxXOapiUGEbF63gban/\naP+XEcBbZ2b/tjrgFr5FF5f81aB2ftiiBR81ZHg/Z8+mzNp6T3e7rYJSYKHB\nqVfa1MKZ7rHjz2ITFfx5n6q8ri2JW99kCUcrzFGfuEbdRnGm5GdZm6XbmfND\nra0ZYdju4Ih27K2XnFszeUtLvQIVM8vmJ0Dn55YU4kL1I+vA92GdccUvEQIx\nIzv4NZmOzIlsrYYRfhNCoYW7BnCNfC1yyMkyqskNRA2d7ohZYR3WKe6qQo1b\nISE4EjjLNfIkmwJ0OYEB7jYlVFGnWPZRwgc3lL/RTeOr4xdyj5DXNze/iPPG\newqDpu0mldrw0780h1bbS+XpaY14C7PKOjNBEfRTjHDaZsoBM1ggpG3V00yL\nyPGb\r\n=ecTn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPOCnJr/XO/LM8CySHp1e0VkVCt9VNKC1vJvGbMNVHmwIgPquQu7tSeIU+b1pA0qK8AN0psu1GQWalm+fxj/2aqpY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709050213_1531112713886_0.05783192493364764"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709052220":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709052220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"52c3b44c89886ddb545364595ca9f701d7e30b56","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709052220","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WCXoAWaRXTWXagX7B8GbYoPhNOniELfg9qhkJMAJcOtMz6RjaXWoYRFMz2pxWUBMSVMvAJ73pTKSXBH3O3aPEw==","shasum":"83e874088e58700d7cbe96db6f6fdb96ab5cb39b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709052220.tgz","fileCount":750,"unpackedSize":1704547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQvHFCRA9TVsSAnZWagAAo6cP/2chEM18MtN7qDO67EhF\nALnlQDXP63NNonsiAbJFR9wd03GlbSh+8ym38HKC7DXnK5mNlcEKPSWBbLHo\nil9VZd6ktRiJ6k8wemkuiM3rnJwCtm32xLRWS3kRJvb7LY5/sNCD3h4gclx8\nJHvs+W4OTgD5jxCYR9Z/7QxBS8RH5MAzryxX00YwGxHUzf2kOhJruBqFIkh8\nRTMndb9IvMSVmby7XyoCEyCCcD4ovMXlfnivpzCCUFvIa1kOYzwOuSh0s6SO\n5//Tf0+940OdhgtqPeX6X41ZuzjCm+jpgJOoB4S9HmI5wC+QVEp0eYOLTmZT\ns8GlREHjFbHxkttC2GxY7kVKRx0q7AkPEcZ+k4Ju9c4XshVLnpl/CuP2VD5f\nF6TReSQH152h97slP0nZcBBJO2SZR7ARH2C2vhiImJLtxi5k3k2xjl3ZMu/E\n0RPPeM6eCQ+fsR2S7vcdMiJyvHuZ9sFX6PL9oPDFoIkjjffuvuq7m0M14KU/\niH2Y8pC0iUFx98YwCWgcN//jt3R8ogZyWKuc4EgcwTMkjHLpIjoWDxy7OWoh\nU4/T9GOdcyaJgX4ig01fc4Nz2KEOb65Z8msQhCDAie8nsfe5MSMO9n2teAez\nzZh31sC4UrqVKd0+xhwgCQ3Iva8rCpqYOzXNPenjS+HW+sBTdD8B72k2k1A1\noX3b\r\n=txNb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSr3zU2BpuA5Gtte9fC6eQqUZj1oI0qHF37PA7hMgeyAIgaLsm4+PAm+NfVFEFGCKCh+RL9qEQGsSJAqh7+p8s7k4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709052220_1531113925709_0.48115936747795285"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709053644":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709053644","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c7b715d4aadb9df2d53aca8cd26098a4bd8aca83","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709053644","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YfLqn3cybsMg4DiGWM1n7bWU4N5zL48NwDNngqGKJwPvWDQCgK+Ky4+o+Cnk/vHe0gtJZTJke7c0rYy7TBXihA==","shasum":"423478bd680380d3568b23d8f9d85b57c8d4e8fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709053644.tgz","fileCount":750,"unpackedSize":1704571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQvUrCRA9TVsSAnZWagAAKnUP/R+kMZQAR1Tx6Tzn1+zZ\n0IgHJ9BxhzOBsXHi4nqGPhBUxpbmgPkYg+WKTY4uUdo5j4jyhpKCnnTuJZ9l\n/m8TFnLebvncZ0uj2OKCFNnzuj0GQf2CLc7NaGwv0EcOlBeaQVcuF/AHRTO2\n9i8zM1vGfwEMaFjlEflLuJXhOrnz1i5RLpcr3POCfsfM+m2BQLbdBXFJlaHW\nOqsgipwAwVuPMC6FnR1nSRtN5zExLImMtIsAfo4DWDmo2GQYyobS3Yx1Q4ob\nm2JEV8sPlN6KHYUT/M3kJE5iGz7xN/ahHf8fDWmpqa+ZkzOlAN/bHxmWlVtv\nosASVTd81PDCGYQIlqdUsSTcPu4SUMo8ejqKnWGLEv0Tj+UQzVRwjytSwmp6\nfciX6hdLVQSx+gCIfkq3YLM8bVA9/cSbO54/GY/v3GLFbeTOHOqkxmNJL3Qv\nPVZtBTEXq5VYvxxbEH+we64LpBVSlpopU6PAXmXP1/C02PTgNs3B4zo0KLm5\nnPMZ/tvXmUfQAa2hsok81c7tplYBaXanSjn7haZekHmLqibZF5yxZ79+OeG9\n1sl6JAyqTvK76Og7nfLI8UkyyGfS7OsY6DExj75EvRG53hVaf0frLWjUWyUJ\ns/LkEH///MoYeBh/xvxESRhtmx4VSDBQx2L+Pwra417yXv7VtjgXBbFz+Ejq\nIS9G\r\n=XQ7a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGe74I0p3QIj16++utdxZGVX9g3MDnOJZON0yGapvVvjAiEApn1/Lx8JlYN4qu6zrr+AFc+Uxe2qiOPXtRdnC1OtoYE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709053644_1531114795516_0.5890571850507402"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709060712":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709060712","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dbe543ee482322992f140379b71f9644863ab281","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709060712","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-f7EWvSHJd2HfxLCV/2xIy0n4y65Lj2dfD6+BYvT7I5y7urZayw77OnwXgeKH9bZ1Gf/uXDMeF6CZ6uGgxfIp/Q==","shasum":"504f3995ab5b3042743ea20373558f37526f963d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709060712.tgz","fileCount":750,"unpackedSize":1705227,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQvxKCRA9TVsSAnZWagAAVCYP/RBz8dU9oG+CE5CCjiB/\naaXsSR+FGWcSnEpwJjWGKfefLkR+6TY1MHlNpt/BkLLiAEYxHr3OwD6ner6V\nW/6uX4kwUGcZrPis8sm6mXVhTlLYI8YQQmcrIvr7Jx9KvYDtv0LMjXjTWKrH\nWI5eHbHfUdz5GKaSOk9EzMzBJDBudfJAP7JpEnn7XARGeWFpHUtET6tdeiET\nucl8uZD4Fc0gKIO1pJG67Gc6Pw+16mulhtgjf/5NAqg+DyhEv81nJot6dDUK\nFjtnbM5+SLnLYS0dkKPacITE+nAEwdvYZOuJX5FEzDOFcaepdCwI+tfi5Z3J\ntccRn1hltML4a7OfGFgmt8YxSZRJVvVL0dSe5mppZx01BfRJ2GP4e8oSwhK2\nTtaHNFu2uKkLZEjzwXzrNqnRNhSrq15p4NiXrhGYMWemkXzw5lfrjdTnkuB4\nsF/LmV24RNNG5JV0o6NaG326RRtP3HkMuwn35s9WduwUbIsjcPrjEMIpcFsR\nkMG01yP5If99pHXKLb75TUBq0SLIHnj3zcbvxLxiywc25c+27b3P7cO6v7UH\nBMkOAJ596/gVDghB9FFZB0jrW8KoI39b7PEorW25r1Sr/CoArllACcGMXExc\n1WgWwKIQqapr/vKv/4GXG+vl5njuBpK4EqpQBy06UeUpZNxhya8166nJouzF\npLCq\r\n=+DPM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDg2wxlOxXJLUgz15KNxLl9zdWMLoeSnERsHROguST/MwIhALSfNBQHY1OCh7ah32nNdFod6V48/9phfBZeiE/fq+V9"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709060712_1531116618567_0.16981310706494934"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709064926":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709064926","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"77def693bef6b4c33c28fbabc2451b1eea8ada5e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709064926","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fey1fP392mrQY5PwmyIscPrks/bLxjbqE/O1SbRoQ1BjxBtPmdW6HbMLxi5Jv1i4JBOjROLEMGn+cfG5guyR1w==","shasum":"e7d1a13f80ce467199e6450dd9a0156d05d04f3a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709064926.tgz","fileCount":750,"unpackedSize":1705487,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQwY0CRA9TVsSAnZWagAAA8gQAJVUS5cyVHkCc0AuNo16\n7fAUBT5tuoRKIbXzcVWlzhQ020lwPu+1OypQJUQxyK7WnXvFIXDUcmm+MRRC\nQNhpijlD5mJfqrWnjX/HZ0lBlrnWvDIaET7AjWvOKE53tzOzEkm+erwYDKBs\neoPdWrTzZ4dLNIEJ4L+Vj1FeRNN7dN41+2uaA6jTdH3won7oCaZDMPNPiwP8\ndDIzhL70HxlRydYzZzeUAiehet5Pu1DvyNC/evDkQMmVI+Hy6W9JHUJTrYR3\nFMNwFjuWs6WmdCAy/hujeJ6Y8LKG24yQ0F9eAvVLKwL/IKJfPFvDJX44iPxh\n2I+pUVbhV+HyjYd3k8Co7fgCL3U+ya7bclAK4cz4SNeS/5IHL/4bTTECExhX\nwU+1fXLIN0Z7POtdvBqLqYXpOEzjY4Y8OrtAoe60YU2PnOA5l/Wm8qPw2Bqd\n8K9Whnbc0Y+BhkF9i5nnht6RheFFQrt1sHIrAMlx2idrYlfg+bxsvExqLyni\n8sEN/7QVdt5f/tdosAdDvSKwC5XIJCf0+/HzOhK3/MQ/7q8kuoxBILZLwExP\nVMNZng2E2Y2AGaUguLIjcKeZmgk0vl9al8qQ8+ZirS8gz1MF1bj1pqKnkKG7\nle2ishyMPpIKTdUvoDKPok5XinHE7pEfJDZbkv9ad0qhV3pmP5DCQCRW2Sse\nfeRL\r\n=ktBB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYL1hEnRwo6TdXOW9VKI8wD62gkTvQrgIpjty/hb/mxgIgAxcXtSaEr9lrShno4a8OocuQ1dRiaLYmrFlVrtX4O2Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709064926_1531119156716_0.8064557799383376"},"_hasShrinkwrap":false},"0.3.2-20180709081501":{"name":"@atomist/sdm","version":"0.3.2-20180709081501","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bcfe277a950b38f515a39b8f49b1513fdb9a3b14","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709081501","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HeI3snvi0+yoX+XOxEgQU40MxfaS5Vy/w+EoktAPYG2W6ypSom13ZeOwW0lb/Mju8G7547/ccODy696SzA68eg==","shasum":"0d06e7311a51e9bf1a7d8b76e973cf489297f98f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709081501.tgz","fileCount":746,"unpackedSize":1696141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQxpFCRA9TVsSAnZWagAAVS0P/3RpQS6kjDx3CTqhZo8s\nFAXVv2M+MojrvPDUDQNkAJ2rtkQycV8M6ULKgkIXWqOgpPLtP+8vrMBfFMvI\nNSrDVlM70+s/QH2liLEwx15qdVUc8ZqLmFt3mwJWaPqsyi5QrVHeNgilni6U\nmymugWmq1mZnXp9aQu4VFWZvh/+bObuEMUPZyY/dqmeQzAco2iAVXhvqDxOe\nXP0xI+i38edV72addemm8XpNhMVFCpojMxok+hi3g0zhnpSpqqxGRc9o18Uf\n+sfL5t2Bx+zlTksRSV3uNdYlAMJ310a7zLYxmBLMiZndis4QiUeBlRKAWgKt\n4eUcHUQ9NnRon97KAbrMkCQjsVCtwxN2bYAdLB/3CMA9ZwVooECtq+wvXMW0\nscbsXO1LeUIst3tKbUwLwmiaqLVdx4t5/yB9CQkHN0lEULK07TuSac2SJvjW\n3T18t6NsYgbAthfif/RYCTWKNOovZO0EUUfu28/EZ+QgwycaSvPIbtONSrhF\n7ZGZ6HcIQvpfoxk45syqnxBCNLoQ59EW/dNsVB4iQr4dCUq6a6JzQCHfAlvB\nD9q36dT6282N5U1ofK+XhaTsSxxoDUHPzAenMnfd3WlVnA8BrryakuFrorjn\nK3Fjh0aol1ZblfV+uCb6FCSMBXtG0HsirEaNMtCC+ToOrTai+1HWNLs0yUtr\nd9rW\r\n=7HGN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBzvwWEqbC/khAq3KchT9N1vvN8BtV8w41qsZMrJcf+LAiBuEn82GoIVE4veMj0lMuLGlgwJqkOkOut5rJyGexV5kw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709081501_1531124293621_0.16437590356788734"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709085810":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709085810","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c25c2f6d8059b8711e391f437ad2dce1a2aac9e7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709085810","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aYFMw/B2MrtLlEV/srJRy6iWsknzqS8a/OgR089c6Lwd2PZHyihE/MCl/qDtwRu9JOsPnKR5E7xHo6HEQYb9Kw==","shasum":"d75e14d48ca7979c492c8483804466e1f0072515","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709085810.tgz","fileCount":754,"unpackedSize":1708779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQyRyCRA9TVsSAnZWagAAujMP/0ngNbUB0GSlXGPRA+vp\nzEVQak6dJcGIi/05QjmcF2A1Xz3Il4/8Ee0zULBHmQKf79K2WPv9BQXXybRB\nwfwmX89a8UsPRAyRkLdOCiZJeL4n/CXs1GBl/McYom58X9HqIvkFO1d7EnBX\nY7lvfplSd1FCA++NYaGtru+NM2TW2AuShsG35N0OFB24b1tKfWVnttkotqPW\ne2RpuCYkYbpToNIoWSWapth3EycJ7nTj5ZJw7i2SjJVNkPLm3n7Ve6HsxZ1I\nTwfeXr8B9vey3iU2fJ/ljM8MzWT8JI/3VppXGRBT7l6xR7rJTUnA0NsbfNSD\nBQWmmKRqmJAbFLx2cbDJrWz7aFVsiZ6n00iB8DPUCIeFOOpQoZSSIHZRcq3v\nIpcP4h7eUWYzx/Hz8MPPw4bmM4oO1YxRrFbhUJV4GxCBBrfDTq63c8gJ1MgX\nVbPtkwSw0TZjoVyOsL420dAHcThQ9VULTwelsR8Xj/b+uAnk6fERorPy0kIA\nIhNfmlouiXMESekkuJ6B22pz5msyPQ3ketHcP+NSMtIl6f5/h5eHMdOBKrGa\nzQEpt0+YbG/kQx3od6daJS0GJNCUbU9T5CpeGhJdWOsFFb892UKZKfPWL6/+\nTzk6dFjHsWVMoO5ybsZLjNgTxxEn4ZqCLOS+GxrP4HZ/PH9Lu+AyU8qbJMT1\nxAew\r\n=zzR/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGrn3id782UJZeu3U2ZPe1zjncf8gvJqSy+FUZlyKRQ6AiA9VzJITBcN7zEs9NPn2ef/MrpR5g8sy1v1urdZfGtayw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709085810_1531126898249_0.9961334291013828"},"_hasShrinkwrap":false},"0.3.2-20180709120101":{"name":"@atomist/sdm","version":"0.3.2-20180709120101","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fa0e9e9abfd9658c5fd3940a86c3586c8521933c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709120101","_npmVersion":"6.1.0","_nodeVersion":"10.4.1","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aWEpl96AWIES1sn/gnVaw/kxhrKiiVEe/s14WkGa4/77+cyeSujG3Mz5D7gOO4O9Wk5TV9azYUqmImpNdqV4Qw==","shasum":"42663598a97b3da86556cdebd8fed359aee1e939","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709120101.tgz","fileCount":746,"unpackedSize":1696149,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQzLaCRA9TVsSAnZWagAA6XAP/iqSybhjgKPTxBjIxxql\nnfefnk1gfLz66CpFeDocd1EwjR+UUj6pCzC7+4R/RNSNbG808tlraBnHR4Cx\nEJLUoByNne06NIdfIx+4YgBye7PXWmagQclV3UhS7BtUolS6MA/NpJTUu9Qq\nTu/bvGjaStmskKH2vG4ciWtMxFEMF5oeuuu4RYjg8WM4MaPbF6wVbBmgADUQ\nCUpiEZld0eBj7pLw+Y2UfpJnXLSEHw+NHwGFBxKGVdlpKVcThWbB0daDt+FQ\nVJq7XN1khHjnSCtcmfEPaT7uvoS9rvUJLqCUj9oGN50V5ig0nurFUy2CZSbr\noKotbv+A8ozlcXszUFGU8mOVuj/kPP6DFoV5VAdWZJJs2X6+b3s8GP7oJOaZ\nLD2RP4ED5AUUmDYHKmbCA5+0NqmadtPtA5pR3OOKT7BduefpltUnLtNhNFoJ\njjW+zTzFgFjmY+1F9APf7sdR5nsFY2YculSwd7/2hLi0WV1d+XftQMFUMS++\nv8HjVmAA9hhK+liPnfYZOZSv3WkHe3BqGAKdMCNHDt71X/FE0mTbJ/zFWIP4\nohd1sq3gyIg2oiRYbUb1EekP7sEIPjbbcMTHDArrI7XGzHcouSylaGtuaw2j\nitWvgy05RtyQ7Qllxo2Htg/e5dIchdTavWDd8SCUixL8IQX0A6kymH4m39IS\nlU5k\r\n=lPEF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICHnqrTivis1mMMYpW56g+HrTrovZZWQIbnZmFMCc/50AiEA/3BTYsffG/LeZJIhztE7AAinIt8hV8Zl7LpVDmehapo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709120101_1531130586038_0.428768292308809"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709103118":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709103118","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2da86797eb527a669a17157df14ff88216de33b7","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709103118","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1l0QD3Osuy2i2T1HJYQlgcaG6bUOVxe0PBFAHNRyda9kRzKaNgaOwIsxXcbG40fqc84qLr5bpJnZHVYRuzwfzQ==","shasum":"3f8326254482f56882f94e54bad873936a2b076b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709103118.tgz","fileCount":754,"unpackedSize":1708779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQzoSCRA9TVsSAnZWagAA+40QAJ28MOZ8LkdjU5mvHjE1\nM5G1PnowaluTDVSzBqrytpeq1eaAVObJyvaysEXYdzW3s87M7CveS32JGpbE\nRiQ79110bFCN6qJ/4ll4VMdInbRCxvlV0MKbvNASncflbizJ6crNkxa598uu\nS9vqFFrVHGHa6TTrboVDSIotJcc17IsRGjK6bI4Hq0s3vlQgA7zlE2RU9/KV\nIlnv5DlAnKXAxLk/KHU69c4yALOZeiDN2WRPTrjAmbExJmpELAJtYvCGl5qa\n0CY/y3QoWg/l6I704fqbSZf0+bBHd5OcZBuV7IlmEBt/iT80SQjVwFhOfSqT\nBElvVZukW0bBiMi0g2NkjHRG35YJQkICi3CuPbFKylBf5pel5lTU/agdEKHa\nLmQxVBp84BI7eSWZpcr/iILZWNNW49KBUC+9+dU/2KWi5hS4N6siW/4I9CKJ\n6TjITlm6BQiEMxVXRvVLMW49JwP/h3RBlDPTy1HXDgR/fZH9llEkVln+wfEu\n9G0qFutitcYH0pulglmVTpbkLX38YAPilIEtBAhsrN0pxx/gFQCn0wbjmI15\nulbZ6r0LNEG54GSD2rm+Jk7gdBO04YLIkaBuN9E190XL6mFHNF+9Xs9UgfKK\nIAA7ynW2xhYaUnlKGeE1tjY88hxTKEnD0pNVmxQQDm69ROBRuJxRAzNBQLka\nR95a\r\n=3HXb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCxNiz/1f8gL/0A8xxbGg0J4QXUsKqlS7IbVOujvDndGwIhALc28eFCHekHNyN55aO+ignm467PBX62Dn7SFMuDbJvf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709103118_1531132434628_0.44265019568415664"},"_hasShrinkwrap":false},"0.3.2-20180709125009":{"name":"@atomist/sdm","version":"0.3.2-20180709125009","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"edb53b9c91d761b08aebbdd3ee1261c502b080e0","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709125009","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZSMhibNzXInCyhG8dAGOh9TyCfMgAQbTt6FHrYXYVp757X4pVCt6RYQiQn/N5KHi0IE4kCbpep1dQDxFs8ruMg==","shasum":"f9e42dd1182858fd27b0e42de1e5f10c90579f18","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709125009.tgz","fileCount":746,"unpackedSize":1696093,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ1rHCRA9TVsSAnZWagAACgkQAJfroGeYs3yaNROzmP2b\nFpIWE80nIRuqTLIWDhDFWdFXX6UZANuOEVMp06RbBVXORu0SdkhG3HY0nVyJ\n5SyRqAPMZzaMgxoOJRrnv3O/CflQ3OCqyuX7TRRDjkXOTjxud0Ga78tL462G\nCECqpgvc2T7sTGTOqaHVRVDCdIBl/ryT0ce1VrJmMuCXjtOtFY+G2huHrYUP\nvPHaq/pJAbP/j0L1MlI6vIWEYSxHl8DuQ+rRh4N/3Uamht4ahA4f3V1wgrSX\ncTTUw6Tg9Oo359tTmYBv8DbNA6TP8ExJTOWgmaLVDWnltNZKIprTr0eXKfC2\nwgCjyOddXn5OzASPuT7jbcOjOxKqQFiWEvjoFCTavc9biMNkWXqxuTmOfRAD\nhPcHnN/JSa/3fBOIJ0hJhbxH8dCO8eYpumqs7QbtzgWHHhAvT8HUrXZaw1mw\nV+wCeYmsXDRwcaz0KqMz+tPcX+HU2LH+/Fy6kICBl6QIuVijvjPyqtH4hSph\nyn/p7RUzuzahhWKgoINnRtVP3TXngQeeuaf+pTYvy1Y+hjKrwpr3qGMQ4HvY\nFAjN1wDk3ANT7ppkAUYqZpRVIczTSGUC+KImqEFrNC8QtBZptzZmtGrUPiz3\nuWPgsvsZhG6fjTqKCvt7jVaNsot3jF2Q5rVen/mtvswECcuhgsoK/jprQYre\nCBOt\r\n=nWii\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHlLf3FlDXF4k9kPTpEfnpBn8h5/hrp8xhawdSn+ChvAAiEAk6lRC1S83k5nogr5awobG10elinNtCcgp2VitunteQQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709125009_1531140807786_0.3840391055742045"},"_hasShrinkwrap":false},"0.3.2-inspect.20180709150530":{"name":"@atomist/sdm","version":"0.3.2-inspect.20180709150530","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c41dd17636a6ee4317b297e2bb2ae1b6599029e5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-inspect.20180709150530","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pY77719gorNZVND6Bul8sbujEE9ynp8tLZ+Gl7A3Dnu7NKY2VB1pjOT5qOgRq1WjgnghYKK3gE94MuDdrAeNXw==","shasum":"3fec0413f642eebdfdca4882ac6a14702acc7814","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-inspect.20180709150530.tgz","fileCount":754,"unpackedSize":1708866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ3qCCRA9TVsSAnZWagAAmZwP/1YQNtY+7zKGxK9ekeWA\nCE3iOJSSRseMFXQZZczl4j86U5ScLteE6o37TfmJMq8C2C3YFy/D+yPLX/rO\n2cquFM4klvixDMrlD3s/SCpm1u463gBP6upPbF7ktOBzBDg9rRHHvdxO092c\na8zLZ1t4b7QP5vM8sG+n8eQkhSQlL1iYJrmsw7c2OkmBbbMRdoA7uHEWKmUG\n0sxLpykyjnuJYMCkrwLZUOQ+yKC4HWLwi64lmQSMYr2YElLXPsG6dwu9BFCy\nKwyWLgvkQCMN96GS1cek8HZG/gOAWIklr80+0E02Fo8cQeB/HrpgY8xwBdvN\nzOTcNncS8LywVwapY1GHtno3ORGLwCfMhhO28t8iR8RwiLpL7Dg9Dn88ABfK\ne++Nk8wpXnMTJhaLNz0Tp2Q4Ealffof2hnCWiK5lKBuU2mjb60ZV8Rd9Z3t9\nYU6ae66Xli9yQamfx7BLnP5/suYnO1nGIDmvzSPj6HcbsN9TUYUM3znDiTCC\nCyPofFmhal7vhWof2zogYO8/Y0y7rra1++26+DuEiGYDH+cDA4A6IZgTiH/t\nbZGmQWBGkNN+pmM6kKD69LLTpXdv0OyHrNPS7m57C+TMKRYdkidOiTzbTkze\nYzACUT9AUUoUxiRDKxuN584b7W7NRi/bis7HZrTBi36Zh5mKD+VqBH35VovO\nBWzy\r\n=dxKv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGuqDzCqP4saTI8Qe7wqgE+g7cZYfr6FBKRbT2gT+skOAiBqHG51qSZ/Au/mhjOT6Xm/+EaDVyVVCWZxDLhIdS0g4w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-inspect.20180709150530_1531148930351_0.9553395753769096"},"_hasShrinkwrap":false},"0.3.2-20180709215344":{"name":"@atomist/sdm","version":"0.3.2-20180709215344","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a02cfbc2ffcd20a8ab9542cb0bda8a0c45449a45","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709215344","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oFg4vy5zbejFRVa7dh+Ui2i2w+XZ5AKv7yyEfe6fVAGfS7O55RlLYmcjmG1dY/4Ixd1+9j9Rgrs6x5zUVqvGCw==","shasum":"dba57972b3706bdd4e136c03d1791e26e29dd4a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709215344.tgz","fileCount":754,"unpackedSize":1709639,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ9o9CRA9TVsSAnZWagAAN60QAI4kuXp3pZ570qAZDStP\nKRlMVkkdaPEPr2ZN0l2Mx70nyypfTCuF7Lu4U5QnTd7eiG1hkQ6gm4L5EAWZ\n3x1xmAxjapQZo4sjKHrgnMqXRUC2OdTi+Se1GGhwEvygs/bC18277YeDiyWW\neFxM7lnTy3BdEuaWNU485ax9BJApYRSVoXxYZwYako6lNlWywI9m4LQCSfs4\nqWMZV7uDBD1tFQPNLua4z7BT3deKw/n3Qn6hOfupSZUkrqASBdeiFQpBQw9e\nT3EbNrOkSuKo+pQPvddZMR7EJgArL2D0JxGEyybVRC5gmbcj4lN9lO4m3VSx\nLi2ZwLDMzz1uyTnOQIKJkj8ipP4KTTyMRoSRU1z+sd01nH1Rz1zSb/DAMYkF\ndLRDoHcfcROOsCeKklmi9kv6SA61F7Cu1NbgNkw4Eaqom0qqR3JowNV1oHtk\nOn0JxnXCLfqReAFyJ0soyjy90q3b0X4e8qws/KsNPNV9i8WjG/mx9AjhpwZe\nx1ZdHJC7NJ+5y0s0TSVzouys2NzbRXEDAL3Gzj3Jh7C2Ged5fPQxjTfXSQ9x\n6f2pGp+EONvMRBWq4DjeRqIpMWUyCx+kob+zwXl1ddajXSvcJy4SbkkBdveI\npaycELFXiXYiSRsIyV1wy2eVmrb3h7EcWngsek359aMYr0XL+f7biSf5sEra\n4IxX\r\n=JNOc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICFX/sV+FddTmNMmRb8le5BU73hjqkQKDaoVVhUzFY9BAiEAgkngEMWW0IEZv/oceXgvxSEixw/7nbvCDwGpE3zCuWU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709215344_1531173437447_0.6193145536953275"},"_hasShrinkwrap":false},"0.3.2-stronger-typed.20180709223359":{"name":"@atomist/sdm","version":"0.3.2-stronger-typed.20180709223359","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"466883885a3535d4b093a72c15f281ffec09927e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-stronger-typed.20180709223359","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-n+n6xFBxjJr6EZUTL01oqkVd7S7sJoIk6QZpBuCu94HX9HSIIHCyyQkFwwlHYDozUpkqTeu//1lf9TEI0byahw==","shasum":"936a2fdb16a3267d11d6ece7a067aad6a5877cb1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-stronger-typed.20180709223359.tgz","fileCount":750,"unpackedSize":1705962,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ+OmCRA9TVsSAnZWagAAhM4P+wUZSqWVjQI88Ig+Kyv/\npYf/vBFUJsqPauHlg/wkREOOYAT0UuMkjJtJRoZI4bEHL4RBSr3e+PhxlsR2\n1N42LGBQls5+7LhPjfYC10Z+QoGlrQup0qZBBLRqKaBYq1qiZzfdtAINKcB/\nEchFKa0znRv/8HWtwV7xU6EngwfvorpaJPtU4/5525xtlP+uWokk5KvaFFE2\nZxHg/SxvK7NVoOlP1+DV5aZ9Qik7sU+J2G5zRlSqS6pmXdwkBI3qS0N4VECz\nhMcav6r3v3K6B3XxTmm63krNwGHYZWhDR78XLan4QOkQ075+KMbKkESPq030\nBfNRWBmH99kkVdQ9V0uLlCjIPBT4O20ojs4yrudptHVrl44Ci5V+97+I0yUl\n579syWoHXGW9H52GVf8P8DSH4zYRgeV6G8+1DXiD1gMlb56WTf+tispYn68u\nMEJeWFxg3hVxlDF6zwiAbZ9fKRuEEh9x3K5k+OR3RgOwlFmiGJzLMWsx+BG3\ndVp5rShxqaG8zIszZZGpAlU0kNtDZXGTfMvM1EMAPt7ogrPq1O6sty7+twZs\nGYCbEA3YQEpJrxQ9rLLYVJaUC8OB0fsdxuYOyXVAMz//lyh5/BRREgolbkT1\n/4bKh8utoZrwurY6uWHaAmc8xk2Wk7jpN+cKeGlz1DltuNBeu/WkOISGjC50\n9cVT\r\n=qD44\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDa7gvH2D010GQG09sZImfP4KzyAzmViT1+pfH7uWpfiwIhAOpvusNROQEj9cWkAYw8GdcisK8Xy1CIK8RK9LbIqoVd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-stronger-typed.20180709223359_1531175846550_0.5145376210645234"},"_hasShrinkwrap":false},"0.3.2-stronger-typed.20180709224410":{"name":"@atomist/sdm","version":"0.3.2-stronger-typed.20180709224410","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"616bfa4dd5e1b36cccfd8149852a8356246f7d00","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-stronger-typed.20180709224410","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ku0hTcQjHhc53zmuv8MNuBL9d8cRmm2oUHk/pNC8NBJ7N8exH9QpaBn9+XlNPvTDCdwDeK5eGn7WDQUOM6LCcw==","shasum":"c6adf45d6cad5ff20fc7517eb12def39763167d3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-stronger-typed.20180709224410.tgz","fileCount":754,"unpackedSize":1709203,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ+YFCRA9TVsSAnZWagAAgCwQAJExL+SoXUd37Ua+wkMY\njdO90xoC7T8bfFRpU76WRT55oY7O+ZVd4Gbl/C0LS9x7Gu3vxB4N4R5n6JYL\nM+2ZEhRQ+VxZGHiaDrpILqTMAiqBnoZ+McCXbXmXoz8vH9S5Ty3fKtx6Mrdz\nl7EGtaRieTm6RS31wggkpJTxAG0zFJ3jqUz+L47YgReE3r8RTQQwUFwtrjGT\nNqSPvDtXdiRkHQXScr40sVHblUsDTM6KtKRhwv88YsObO0mMcHPSePQZJ5h8\nSRt3AtjaFnEA60WpJubuFRt7RK983CDbHNVcuGYBf54KztV2wC0Aq/csDToT\ngusxxTqVbs/5bnR7kflIyOSetdxJCTeUVcJ4ZWaA6amMfviInexgOOXbbSRa\nvuJV5180v6agOtXS0VJ5Hq3+3i6MUs+/JMFLx7w71s7lSwR6jC3TwFHoejCv\n5S+HjYEYsvHDhGuhmO19rC4XBQtaMVnU39mTUwBzJhT+7A1ZHCaD+FfVaLkD\nL0ZaNAQUKxKOk8kxq6qZpxXh84V5S7Lt3MHdnLK7kPUGYOPJefMcR7nl1KH4\nE7j2gUNLG0vmaQWCqdDopcrDRfnes+dAFpWNpzxy/VCJFL4AFejGoCNd07cl\nl8RN17ZLfT7GxQjw6VnEyW1kUvkIumfs1uQxtyq0ChqPVqG3PRPUreqoRtZD\nuObe\r\n=yoCi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDdmuHMhzcN4xgRoabmjSxF0d0l80wvbuQfvcFJMd+cmAiEA/f6tuBLT4fUEx74+hWC3dKw7/JGcJnCSu4PJ4D1IqEQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-stronger-typed.20180709224410_1531176453688_0.46941724937415485"},"_hasShrinkwrap":false},"0.3.2-nortissej.barrels-behave.20180709225535":{"name":"@atomist/sdm","version":"0.3.2-nortissej.barrels-behave.20180709225535","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8ee963ba1c904eaedb936073ef9ae9e99b8755e4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-nortissej.barrels-behave.20180709225535","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eA+K/qnO90uCfDY8Z9G7H5tYV9ftuf9HhdU/dcv+jEus67GLHUvwPEUHegjaGpn21n23GBVvcgqsZzlpfAb8Tw==","shasum":"2715bd7b4698d84156beeeae68427e62f9fd7059","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-nortissej.barrels-behave.20180709225535.tgz","fileCount":754,"unpackedSize":1709871,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ+jFCRA9TVsSAnZWagAAqVkP/3Q6J/D8ARr9hjNTWoQr\nZo2kA+RWKOW5nf6N31vStklG8EV1qZOYg/gKkAg24SJKmGBzi3gT2ZPu57p9\nIgXeoYlQZJHYMSY6+DcVmdwLvnsP4Xxo42NyZr6ba0x7vg2IM/kcC+7Gs2Tb\nf/8ynb4Dc9gp2UkOrLb6hi3SzGDTD0Jbr37ZYWHICiuz5KLd7kWFmicudgsi\nfh2uFz09eAre5otZBkMvDaxKIpJmkZdDebSTqRARal0Kbb59QCgNEImbOmdK\nS0FmUKRi33OnfBLgRe5BKX8VYQjHOEv3VW7EV/UKwdo3xebKor7ABzxC27hu\nV1uV4VahHcrv9XGei9QzIqiGPHW5ma8cxzX+2zhRF1za9Z7EA4DFZ6notFC6\nw1CP+ZCCoHJ5aUh7e0oLXX9Y8bj+dDzZyemi+yn1tkATpIedaaWnE8qhpQJu\nN9Y0wQOZ4Gk7+ZVRIbnjDwS12FKesJzyK+IsLM9uhDGvkH9pIxy02KqZ47ww\nPn79pKZOFKOiZw8yxBvtb8Hi+ChEA3ZPIlNti3YydYYnAVup1Hl7A102b7GZ\nHGWK69U9w62GeqekmwA0s433+fGtz0hgzUCH3iiiJMRmaGg0BdekH0qHC6IT\nLzzeTFaEji7l8U0jDZW8y4cDRK9oqhqyjnbBi1751xClpyPMOZa/fU4ii07U\n1Sg5\r\n=rGIZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC6fWWUY0qAGJ1fsapUXhfX7lpV5N6ZlHj35d5ZCzda1AiAdwNVHhIAmdpBNRkYSwenZlvmOuFBx2LQWsB3IXcQENA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-nortissej.barrels-behave.20180709225535_1531177156900_0.9051215775543178"},"_hasShrinkwrap":false},"0.3.2-20180709225831":{"name":"@atomist/sdm","version":"0.3.2-20180709225831","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"99e365a26d9a801ec8c698bee28eb699bbb8006b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709225831","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8BIaGMD2qPX1/EfIScEJD4WiiqkYkQMGBcBMg0u6PaRtxXh9wNdXZP+miitXMboqiQtWspwE0GBvbLlpjoa+3Q==","shasum":"b17bd52c436c3d70635f4df65c7664e7adb0c320","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709225831.tgz","fileCount":754,"unpackedSize":1709846,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ+ltCRA9TVsSAnZWagAAvq4P/0FE/qTImjfGr/LJ0HwD\nPaE2525HYtCYETYQRQVy1WuhCOx9uleGfKuCZofLRQDKi9o6NMZngSoL9pmp\ntlw1P+sJatqzPOJndLgdHKc85a6niBXXWrsYfEZIG7vnYQrT1PGN9rkU1tEI\nqq31c2zI+gMK+aEfsS7fsSSq9CL23gTaK+EYX5lHbum8YPjvrPq9K4cVBQq1\nvpGfHHreBZvrl/xdUnVSPb+wMZ+9HlpAaFpZeyE5LKAU1woOst/EKIGCWHCA\ntT5RjThuPEyTa7jR9zPUggOLSv1RHBnKTI2SMIkNofVDFsq3ZVg9H+EXyuPk\nuMQBAN6q/Cm1Dkxt5YKvbRDYnrlI67kUoE/idset+leis8eqjaenNTtzXdhC\nkzP+c71bO+oJyej8gp2/5c8N053WD7U/McY6PZI5xY5oGPAVOLhgkP/8DUbx\ngCeDc+UwBLMgpxaMFP8KEl8O1Wfrx892GFc5KPp0EJWlHm+RFAeF/O2jKK+P\nDAzO/EmmhqNZ1Eu01XGOTCLE6McyW3F7NYYHofcIHSkME57RSqljL8ZM3dl8\nbTDGsyBnPrpfo0KLiE4yMd7dbSqjvDVymUHGdvZ1aWzWkE5HiAbrc2uMtYjP\n9ePQq2ttw9q7NotTn2QspDf1SLNXVuvW0UNhKxZJSr5a5t59MnDIn/N04x8C\npXJC\r\n=E9tP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE+nqrVP6KlUd9QV2ERkQUD6HkVQXMSvQQe7EeBK/l89AiEAoXhSRXf90R8FfW2q/wIxbeqy7ai/VNOtfU9o/zCq6hM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709225831_1531177324928_0.3974092613195166"},"_hasShrinkwrap":false},"0.3.2-20180709232854":{"name":"@atomist/sdm","version":"0.3.2-20180709232854","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4b21ff7f41ad3ad6d2d35ed0d9bf20f3d830fd59","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180709232854","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QGXygKH6r9HF6JHF7o82TO++xUhapwobadayexBNCpb3yVZJLtOoYCIGW+mwrdygTT+3lb6H11qEt4qHyzV3Lw==","shasum":"7290352956ad45746d48f968ee67eb00195de24b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180709232854.tgz","fileCount":754,"unpackedSize":1710014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ/CQCRA9TVsSAnZWagAAR7UP/1YzIdBNeNjOk6cxtAZM\nInqH8iigv9wLFs73d3O8tMsIzgJoEyhgrDaaXVdbZV6on4+e68K8cdb5AxWV\nIeqWXMBAHJOKUYSDPMa/5DUezrr/lPJ1UxpxYf0gUcKj4BDthQZqDOtkqtfw\nMElvni40h9tvhHK19Plp5rMDWwUSRuJ4k0ejiqklKusehMNOMVu2uVWkQxHW\n9cl0XGV1g+yTCN2eigT4zLtHOf0MNpWnksUt2dFFb56ZKc62oZrldursEhmo\nFD3zCav5tm/8hRIK6T1Q++MfknqKa1wae47Bjfy78ZE1MoII29tpbv8RFbuB\nrThUWnh4UO1AiiYSTM1GG+N9YG5bPR3LAfdVn0UJ9YnOSDL41EwjQXY0ixB6\nmt/4eYUc9cxt67y2pP3cn2g5AvYybIblDNkiGdybNDKLuyns0ltPqDuv/TRQ\nNNgrN03MDpCcj3BjjTI6SaS7Br8rCrgkwQ0gFFBWNkFwa7eru6jIowKyUTa8\ntyFJWb60lBp9Emcz4eghI9YH1ugLhRNeoHnHVVJ+7yNx37FHgLt6meMwQ5Go\nCms15KnXfXMuUpWwxiyr5DzzB61ENL4y4cOUzCLRNDrdYsSPpGZZ383xtay3\n8Rjl0CVZVHAMpzUUuKSpDBqA7tzx/LcIBIXFXiKI56k9iF/3+kEQ2/R1vjY7\ngE5T\r\n=kQD7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZnU2dOOnnopoy5RvHoExVmOL+NbCEVEc4JaFnqIHfpQIhAJ68R1fcJnbbwhj9D7AA932dmqZxrEgOOyGriDHsmyAv"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180709232854_1531179151839_0.42536902739546933"},"_hasShrinkwrap":false},"0.3.2-stronger-typed.20180710001447":{"name":"@atomist/sdm","version":"0.3.2-stronger-typed.20180710001447","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"34f6d12a9642f176e203666bd0ae33872046654e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-stronger-typed.20180710001447","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GsS8FS0IQlCMdfSXQQzd2Mh2rVf3lBgiUX99ZhZGyJHprudlV9OBS7mzP874e7kG0SWFPtRa8IaOwxYRFmdmVA==","shasum":"7a512377a09c7c095a3b4355014f389c9a947181","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-stronger-typed.20180710001447.tgz","fileCount":754,"unpackedSize":1709221,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ/tDCRA9TVsSAnZWagAAxKQQAKTBbYYmwdGzRcIOC3YM\nemq0txagWdS9X1cGSWbPGKINHczgKKseKorzBj4MxbpxYHfVHrU/GhcTbuJy\nC1f2XuPhozzQgFpU3IKh+b/M1XmcxDMhzxa4FbUj4h0s2ZJWP0R9dKM7K7AZ\nMoX38gd7i5xj6aibWqsME/DY5SZvPC5WoFGyMOb8tX9DPAjHg4ms8fkEUgqq\nD7ZyeIwxmTa5/QPCp0CFsNqaXkBBXOwiaGoXwJpqe///yyU7AYySVUfPVVnq\n3EtC4srJecjSt94u4qL3EFrt2FUZRYGBp/2Ey57AX4TpEkFpTxOK6L+tfRMl\ngBU7IHELbBh2gT96e4qRSnk4sGkzd0xN+HDKbOiAmJaQp44a7EBpmb8qOZ/w\nvwsoespGcEytiWk75mmtqLTLQhMBANSLxbdy4mWGDr5JXQwVsUNgeM4AExnH\nOCt8PL8fEuPWpi+VSvaX33Hu+Dk1taPZIUgY6mWpHFjr4OkKoVkswvglR+hn\nBerdlkZ17ki1N13y5lgXmBLa+1Acu0Cq+NFlaiVyy7yc9DENZ0eHSq9RWNWN\nS3V4pXf7YtCOKvQK4r6BCbYYR7utqx+Ru74QiCM6RbXpE9maHqig0NcqU6Fo\nmOko2jdDm3UFalV5MOzsDac2XZQT20LFrUi5cgergWfR0w6d1jHTx9XX+SDd\nx316\r\n=iMs5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPhW/UvU1bXmSLIHq88XcX5en52kEKMR8vNknGJSI9rgIhAIl3jAP68Hc+qoBktRhzac8kMhO+bhnlJj4VznGLmPg0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-stronger-typed.20180710001447_1531181891370_0.499762906871003"},"_hasShrinkwrap":false},"0.3.2-stronger-typed.20180710002349":{"name":"@atomist/sdm","version":"0.3.2-stronger-typed.20180710002349","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","generate:barrels":"barrelsby --delete -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ad0d0bfe7265b34f01bb67dde040096317a1ee9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-stronger-typed.20180710002349","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-P6hsn+YVHG6N+TkJqFHKM3O1k//Q+MOO3NjN7f7T6WTry5rPDIw4dNxpKCE8KzZ8CZs/Fptx4jzgxIJs1h09Yw==","shasum":"cb4b0369067a97afb5c3d4320c63cf1e7a6bee26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-stronger-typed.20180710002349.tgz","fileCount":754,"unpackedSize":1709427,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbQ/1oCRA9TVsSAnZWagAAP+YP/1vErNoejovNV925Exfr\nDnSV4QuUwmVckHgYkS41sTyaNyYTUiZ+DyfNc0j+VUF4x6XFrsAxNTdtjhE3\nU6we8KMQ+AQXHrxURM4S0uKfdojB6RtYuObs61xG6UCzlMlB4sut5ta9Vrsa\n2y7fL8/fePBeEJMxRk5AKo6XvHnqL9skTklMJUwAxcUlh+5iAKbeZChQj1Lg\nM1zerLwDoDL+Hd+i4yeQ0F4OrAn1cSnJwDtP6ZMPz5o33hgBk0xdHeh2q8s2\nypX1L2wdsuC4RfI+xqFpWpAo+Vz2281ZFjVsricbpNvgWnVgHBsYfBXJ7dKJ\n/mgmOKVD2gfp4O4+inNAVgsI7qO4qMoQ+HicFFuWPs+JvV7xbFHyFfLypG9E\n1V1SR9MJBhUq8Mp4N24l80CSP6/R6gtnjNYJvIJfT7IDsHut5IryI3ZZhx9U\nfCrIyrDO7YBdvK/bkFZzrfuLzegJIjEqOmADBi0hycVfhDb4xiI+mc78bJA5\nJBlw4wP5SNQiJrhQoJOeIbBgne1syN/GwCc3d3eWTMxxZ996k5ADykSWkld5\nFxrQe3F8QiDHHWFKt95pRBXReDMGUImyZcOWvJczboHD06qEX6cYoilfO6hM\nrTSqWXwFWIsKwSdAqkKGK1iesNY2xz1blEfbR7294MOO+pvD7ibqe3l3sdUY\nKu/X\r\n=pnEZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDpomE8XA33Di2OBlfToAbP4wtQ681tUPwfjl1LdsHN+QIhANolOK7sOKpOQ/ZPAvyzaNdExJnf3nShx6tTmRlM7xfL"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-stronger-typed.20180710002349_1531182440375_0.8693203227745558"},"_hasShrinkwrap":false},"0.3.2-20180710010159":{"name":"@atomist/sdm","version":"0.3.2-20180710010159","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3ac98e8bc5ee18a936da453cea5e3650c15a23c9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180710010159","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dtVncMckKWB3d/QTrYs+nyBsix8i8oRwzNOftvpqd6oJWeXg3atCG5G7gqL652iMt/tF0CGRxZT2QL91Ld0kgQ==","shasum":"2e20a1643f9cc07eb34af9e77edf7681a4bc10ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180710010159.tgz","fileCount":754,"unpackedSize":1709701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRAZpCRA9TVsSAnZWagAAtJ0QAJGYGcPWORADameqr30j\nQdD33wClb4kiuRSpuftMb237Sgrogyv+H75lO1KlNLjc9k2seyMFdov1ACRv\nbGTZ+6mSvm7HcngpFEEcG5X80aFvnb/+DB3KJIjox/rNMXJDuvT2/ZMrO3LT\nnUoVvPEDNQTbG5Vi/9k+2LTFkU6ApKJFbvjP1cATKHWWay9dWjedZSZ7mtjd\nTfOwLDolzli+zWINau0MAyIylI1PuuajXMbp76Evov/HTm+AP6vkIfiE8fqe\nriWBhofuharMZ2XYu3QgsKwnB/2byxDNFhVORKOLlEJMJvMV0sCETUhGGILO\nApX2L4JIFXNxuOytzFzjT1kfk0Zu9KdYk+LRP2iSb3gH3hUL0AWuBElcj5k3\nej+ggY3KiWY6DRK3FoAKHml58+K2V4aEJb1fICF53Lz5LMbu/rxyDgYK3TFc\nVA/zWOR+hbAGLe3hqT6MshKSr441I+S+Hri4tl1DGKOr5roHj7te9V+wpOPm\nvhF8pqlvn2k819majZoCStFws+l1++e02QjKnNX9XfvGqo0tKYX0DR8xZUyw\ngpnLSiWRjNv4pL1W+/o9Ac/hRQd9+0YNUhzMEhCO0YgR+AvKHAdSRpDhRxJU\nT+J97KMTl0vz0SKlsJaSxr9oNs3SREsLqLcRGidEtxiyCrV/72yCIxhErUG3\nx19C\r\n=9lmR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxLIR/soki1/N9v6xZkWSPmdm+Z3njPUvCckefeqGGNAIgX2wAPlhtSAs8HeAxokpMCW5p3/Bpm1t0OWnxWT2TlSU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180710010159_1531184745032_0.9510410092490973"},"_hasShrinkwrap":false},"0.3.2-remove-deprecated.20180710013529":{"name":"@atomist/sdm","version":"0.3.2-remove-deprecated.20180710013529","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cb69797bca95b644952d637e6fe7fd583434ab61","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-remove-deprecated.20180710013529","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-t418gF+aqGNYp1zBNf9q2gR4MedVlv80ePG7Rog3uDbCKQFkVqlVTV5B3dioG3trLzD00xmKuAuVgTZp5UY7Sw==","shasum":"5f6d53da3a88c00a9f1aea53078beca281e8ff2e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-remove-deprecated.20180710013529.tgz","fileCount":754,"unpackedSize":1707943,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRA5aCRA9TVsSAnZWagAAfRUP/3P13c/SYNrFkMxENYzs\nFcYqYg40MliHUe+H270SDuC3zZUTlqMYMkpq1LUQWXcpOa83MRtoaAT53tgg\nBPAP7E4HjlZdoOoS6/N/AESvcnRTivu3LWhZ/9Dk8j865QRAuwMwleJK06Bt\nbKZ5KKptW1PB8v4+J6K7rD5bATpWbQgKYuFnvJiQLhRclE6gwaByzLYk/zUN\nXd/tPupTtg0MbVz1/dVTE7UOooTGoXi3gQuF1dh9hZzbK4iWlGkyIesGkUHk\nSY//ZApqriaFTqqqtGPX+iZumBhPEGdqq49I6zsRL2JEj1BVsxGXsfgFM1Wj\ngwWdUjccu+/Ju019a4d4zVF/UFuQJ7G3AwLMgupJf5m+Tx891bS21xQMhVXD\nY+gZXfzsKhkpFZMFTVHqmG5c/7YmQ9KHakxDCOqx23MQCJQqqYLbkcTXD6NZ\nr7A+KiISr2yEgeWsLi+ll2DgXV42ILuvdM/9Gxxq/+dY3PoT3oOHvyHyqDZW\nkCBcvKx6IX2hlUPiw09sXMjLDjgK1A+qh9rGstQ5YBASlnURgadWDALf5q6z\nT2UKwz4dLedxox0/VRh0xm0l1/GMsCCx9FiRSxkPV9Rlx92c/pTtjG9WJDOD\nNFHAp7hh7+rfrCaqQ2vCh3cOzWQKGw0HtYFOnselESgbyLnFFbH/Rs0wS1fp\nrxQt\r\n=zArh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIExeXbNsvrxz9iBNcNXLZuXYG2Hlo7HxL9kVMC/2i72XAiBDGIsByRYFYWYFSk0iv8uKo8xed7+myRTFJ6uk3hM+mw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-remove-deprecated.20180710013529_1531186777949_0.3520770184895179"},"_hasShrinkwrap":false},"0.3.2-20180710042338":{"name":"@atomist/sdm","version":"0.3.2-20180710042338","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"93740cec855cfead6b101b14afeb9881057b2cda","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180710042338","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NsjyZlBCNADkpQa8etM0uH/CHWZjY2WO8JTR+RGw8PF3VLsCzIfnAXGm4N5WCTUgra1Wr+7iXhILTquKaP1UyQ==","shasum":"506478d04517f54343d013e27766c19828a22a90","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180710042338.tgz","fileCount":754,"unpackedSize":1708973,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRDV4CRA9TVsSAnZWagAAzPIQAJaDrRUlvnmwKG8hXa0v\nkU9onW4mYmHRI7W8gXcBBRZKDUA5niPieuNxkBdm80RuFNQita87g1HVM2Bt\nogCqHgcKDyclwzTB4nQuPEyzcsjan1p9qBFPB6wHjASDa9ywaHygzg8JJoMp\nWyTuzlcov194uOVTml6a2J3pKdgo9cGXO3R3ieJgeTxcMTroOqw1BIYHTJtm\nHdntfQatxW4VybChb5TkmpQsxEgpRBqGD/avQEWMEMeJ7I+W3gMNWxKqRWd5\nEbKadD7E64S7SdtBmHGrr6brDXTtM2KHZJG+bTkTtVT77qR80ePHBfJ2333Z\nTYuSSXZKgo910TdtT4Jff3tNgDiucGOGw9/CeRB6gFR2OxjxWVWzatrxe/Db\nexmlox5Q8pPqIAIQH7M7zCG5iAkTTNqEg/wllqtNcmeRbSNBNb68U4cTgT5F\nSPkGidz4u6d6Ma68OckAlRP/xmlhdpRzwyZjhmgt9TiDdgfp9NsriV5fWo+m\n4Oko8DzO1UcPOGM2MBAxnfQYQXfkt3zQ64Dd3U8vRQhRFG9hEeA+D9t5XwYg\n8g4dsJ9dViF/iEw2N69TuTl51jMyi+RNb4FYmFYCFR9kheZPK6a+oL5eAbTh\nd6snqkhkGV2rPHLhPxJ46WLlNX8EEUxhbsWiutY/csuiXUXn2NfBmjgPCNPJ\nxsCQ\r\n=tRXD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFkmWfoKRgOtEhFAy1DC36WQdoaRAwWILxt+yHEkhCb6AiAQFCqk1eHNIX3HZOKeTpagOQAk/aqXJEzSB2L9g41hlA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180710042338_1531196792432_0.6331142011921218"},"_hasShrinkwrap":false},"0.3.2-20180710050414":{"name":"@atomist/sdm","version":"0.3.2-20180710050414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"855d99fbaddbe3ab859b22175cbcc101c88e4ca5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180710050414","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HiCxLbsS8S2LNjgQzn4ocNvJqXA2uNggjhPjuyKlnAK1i7XN+EKMqFhio/dULeQIdS3q4gEIhCoDtYeTkEqR1Q==","shasum":"fa5380eeecc17256afc18701158cc757f71ee49e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180710050414.tgz","fileCount":754,"unpackedSize":1708145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRD77CRA9TVsSAnZWagAAQdoP/2OA8gKfRrV11btefb6d\n2lTDsYUhKV49WyocYpD20S73itKz9yxfh/oqnzlLEAKapCZGXP3HO6VdW8TD\n5kpmmCE7OZPKHFsUBNt0rhiMvsPYHos2gteKgt7mp35TKOuj+OiBR5TVn6Ne\n4fwpUeH6MzHE61TA9KL3gC2Rfh53u240Mww0/tGWKVUqvFVk4uTSumjlnKM5\nNQiZKML1IISes0Xdgueux3tpngKmGTReC35PNPUKzqTHumsUc8W1Io0OR2Kh\nlYbjHj8Z9R7oiGxZPsD8S6+PTcSSC1zTzWjK18g+RFbtKtcCXJf7/PWdplBl\njiOLO5vccQHZ7/bMhfzHDHTQxgaM/NdxASDuJjMVRLEsYW9cr1MXpxxP7SJ9\n0Brge1IzgSr69FCz0qjTB8n1lVNOE+CJo1yuNdnehgmUoYuM47SMvcoV/I0Z\nGJt4m47zE9kMw+bIuZo5ldjrl8j3Y2iu6+EKcjpeYRSI4up1z3PvuAsiRtCy\njeGEZJNJhZIhSM+dIXqJER2ZtY1r0meH6MjFlPYgbUQElnk6Io1k7s+e3v/p\nAjjMW/i7UzK0vCLsNRA0rGlIkgBmJG3x0zH2sAITOQuIZySMkcPJ0bDqExIY\nPaY/SRe3YVO7E9g1tgPPM+mFpVP937OcriVMlmU2TZjoWwSmJ7mqOUBYqb56\nM3wQ\r\n=Hhue\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEZTe6aGS3iYDsQM7ROM0df+0L78/DtJfG2HxLiVUq6IAiAUDlyh3d2KjkkNR2R8J+A6mFKFVfp9riRjlfgmeRjfoQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180710050414_1531199227523_0.06735556555018807"},"_hasShrinkwrap":false},"0.3.2-lazy.20180711052503":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180711052503","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3349cb95dfcbca13f75acdb6617b20d906fa8191","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180711052503","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZV4ocYSAa7wARCuBzK2FnxpHS8rZx0ps2K5/jvfPkrCFGq9sdlWw8xddMahc0suHTUtQjmmn7Cg9Lfy9QKa8WA==","shasum":"ac10890b2a82431a98fcfeebc76023b151a0d4cb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180711052503.tgz","fileCount":758,"unpackedSize":1723366,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRZVwCRA9TVsSAnZWagAA5AcP/2QBz76EnqkcINZ08Jym\ngQb0iTnym9jBL6SpYKbikQz3PV4U3D5fDlhvaGSeYKgTGCVeqM/QEixacC7/\nUTbVyTHGI08SzugSsfjRZWtvN83IS8DCt0UVnaqYfSB14M22SJaaqRYlQiA/\nrsEEbQTuE+/EVr+jRGZIB/dM4hnFF8X3ZvzaN+S0ulyNn7MZdv4SUXBQfWjn\nFvqxx0K46zBvagWOegu8qcuC6+SzKLmUPraMutfv5GWJdbB+/5OP48WujV1j\n5b09fxvWZ1GxCxN5zv9GeV6/I4ZzudxahKBL1D1hcxo5DU/NTKBcdXR5Ear5\nRyrYwsi/fbPxl6q3pVKpprtScPebh2B8vqvkFuu1JCZzT0a+sSJ2w+SvdBoI\n8RIDdYTkzvO87rohPwMPT24iGLabwTfjr/0625x/U96tAaw21zoY5dhVW6WP\ncG1azK3+FXUZhTXnYiMdKdYWBhYlFm8N1ET+yJEHumeSZ9KqljGoYTGzxBTm\nMJXhc8A9XGH67bGQUkMLW92hB52wxuPSSq+xoi9brUVaSRomxbE1KEogt+O0\nOe/u+4aiEP7rEtXdVLsu0mE5LOOjaAU557yqj/d+rRU1QPMM/SOXRffwGX1N\nSaSIRnYDJuSb5YI0EusESj6XJDfGcnOzqCnABUv4TcNIsTZPdLSQFRcbYJyL\nJJd9\r\n=p4Fk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDK4gJiUeJimxQEAnm3tw/yVNa2CdoEdQUKOzlUXxyWQwIgHmiLyckPnsQeTDx98xlopqtOeu7f3/3fGgZAdKKoSrc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180711052503_1531286895868_0.5227274707458263"},"_hasShrinkwrap":false},"0.3.2-lazy.20180711053019":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180711053019","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"af4d21070870c4524c90f6d48cbfcca7fe50e011","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180711053019","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-D2AODWPE6EoIfHu8VlhYGMwTrLTJLe3qqe/dbPy8tyXbVYrZK6UhwRuUDvbmFDTVp/Xr6bQWtpOdemMWxEQ9Cw==","shasum":"bcf6f8d3815d84b2b299b7b94798e10ab830d26a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180711053019.tgz","fileCount":758,"unpackedSize":1723418,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRZapCRA9TVsSAnZWagAAYnMQAJZNxBZaFYGQFojRiGed\ntSccgpRYCXwq87HEY/KlSXuFa27jAWlj3VMC9ndeTK1Bw2gArzowALSMRUi7\n4vH3GReEP6sVi+RUymthvWdO+jmnhPE1biJluIErmBgnoRNBssIPVIO2/0gc\nOqgJLQSpRY9GETsj/4ZKf5lVSC5ICu6nYMrKIfUGwho1tblxNCECpOTsV6SH\nJBnE+2TOpMbbr4VT/PhGRQjuhnEDE0z4uIKTnULtzLlSTfEGW5YLjx73hByO\nO+gQF4SFC4DyfdphVekWjfWZxfRq33Qf9E0fl/MVRq0dUIN+jTfUZk6nGNMB\nxWtMUMtreuoFuoCxXNHvZXPar+EsZPatF3gJIaLh/CZqAG1fGI6mlyzIhAQP\nKN5VqfsI6qy6t1fSCexxEDj1yncnXz1TxgPNGI8joZqn8aIQYZERaFOF5MGv\nnACqj2d9Tp7SQz3Wvu3Wfn7Vi7i0CC39k94bucYnpjxiJ8W8oVeRy9VfKnaD\nAgOvHNNAU4KzTJDmD756oSsCepCuuLasgmDVFk3a0NsMDF2EH0dX449E0TdT\nbxMWl9nZ/HL4v+pGUXm9wwlqApBgZP4pNjOc9uTaq/uyZ3tM5fBtx9mo+tYD\n5Q2q4Iio/aEzclf1TOujb5MWp0xmFymT2xGCthlbamkqCmCPnjoq1RO+Laj8\n6h4q\r\n=Dm8P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH+Yx13xokZPuSuEhmWgUnl+txzHfFM4wzvZX2BZVIARAiBH7/FrEuHHQraz60A/zLcRN8aFfK3eK55WvBxaciy9VA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180711053019_1531287209066_0.9259498609906942"},"_hasShrinkwrap":false},"0.3.2-lazy.20180711065346":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180711065346","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ac31db861d1abac375e42aee2a498a5c57ccc1ea","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180711065346","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0Zji5ztEc6ViEioQvQykeguX1KmQyI6u/swkI3zisqH8e1A+LtUUg/wE8AyVjahLW+utEJ040VdPAixu1qHhxA==","shasum":"b02b9c57ab3406019cadd3dea4355a5ada0444fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180711065346.tgz","fileCount":758,"unpackedSize":1723616,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRao9CRA9TVsSAnZWagAAIU8P/2WLYGPCXEV/t/rbk4wb\nWjjql76YEk9ATIcR85jx9JZxoJMiSFIpt3GHmSFK9ANMjGX70eAqH3iMGnJd\neX0Lnng6E2KtWDmGjz6nlNrzK3opOGxl4lwN/ZA+LITu+IJZFRyG0FktSwEs\nh1FG09d7yT8Tmy2zvszCnTbdTNDqxh/fv+hCO/F+AGH2ABDuFnwb4U3Tefdn\nYOH88llpJQQTNi0Cqc2RDvWPNcSFKw3ASlKCUWvAKMSCz8rr3sG34ROjh05U\nlab08WqbcUUIvC6yq/tevLPt+BxNA+beMhwMOfVooQ3GkFbD73dmPB2FArLT\nEasBhONwgZr/TxeLen48XEuK39DPQctEGrV7GTCcrGUxelLCmF87tJeMfEmE\nT1rMYE6lhtWiS+VxUwcqH7S6HrO0un2M74A1Iz0baScmoC2r0isLZOR3sVGD\nzd9kxeerdk1D1ESG0/TcnPN9RgfjaN3M/nqX3zRGc3xIvxg6jZyqZLEO1AzL\nBbOMwOSHoeqdyV69Cn/3wXTm9Zn89Rk83TSrxEbivlzrOiMbXPzvyKLcW6JD\nvSCzhA7UhCQoD/OBOLVZyf6fXSRZ307RiQ7L5CDoKPDoVkN73bqWJW54zBlW\nKRsco7sHuQkQsDeLEZC1zUAYXoPX2v7U6/Vjqfqqtl0lf3Xs+VeTgFEsvmFS\nzJMk\r\n=HIsG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDSpYMfTsFmecg6An2oIgtvfhFzGj/rl48Uviz7eeMwIAIhAKeZvQoQSQKKOxs4Fgdlq+3UAv1kkXU3IjT8i582Ht9v"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180711065346_1531292221406_0.9096875408180347"},"_hasShrinkwrap":false},"0.3.2-lazy.20180711071049":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180711071049","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"215bf424783c4638c907bb727f87a8d86fb244d6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180711071049","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a8md7O6CYIpTyObK/q0tdvBOsOLvEDS8XDm28dycneXwy/Isa4bOVRCKWuawFJJtwKItxutjXbvrohRuPDZiYQ==","shasum":"fa5f65801dfac41d96c0a7098b4d67cd3b8bc69a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180711071049.tgz","fileCount":758,"unpackedSize":1724321,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRa46CRA9TVsSAnZWagAAMr8P/ilII5SQVtvyK1HlbO0Z\nrOHqLiyf5SG7GBRBHRVQWi5ZS4lVetmPJIGyBnOKXy/KwbulrzLuXjgoxk3F\n8wp7Zzd3cj29Jz/fz1N43jHkTBBZ4LR4vqiyf0KfwCE18r9MHLBIsNcThVhi\nsNsmO4jlTdrBluUm/c1c/w+yI7OQus1hShjRN/bru9texe0pK6TmhiriJaak\nFlI+Xp0mp7efCL2qRqaglLMCwCRKeCtON6uPDxphfOKGJU+oznk63PfMR6B8\nBKutm32ST124k8HwFybLttFVTBYaTcLV05ONKhB7LdpXLX+WhF1lw2J3Rap3\n8tPz+mqqiYWezudmIX4hIWHMUuoMjTJEYaTgZvQV17fVue1WPeZ4xmd1GNOc\nZJ50IXYxUTHRDIkF2nowJeypLJN9QQRBoB4mabRpkGYD8mYVAsMTrYncqG56\nZ98mQtdRkoAiCd8PpRrlcuXLEkVTPHaXhnisbt8JmQKjSz58xM1vNZD4MfGH\nibmzZePYbhNUsx+GxaU+kpCU6EPMiDuuIf3GeZJDeFegaZMqLl6D5ssijz13\n51TCDbAUP296vJmHgdjcGRrJYlzaI7IfeLZv8UZahkuS73nHkbSa+rpvSx8r\nfv/781Q2sPOTV08KMobhgDdNJGqM6MgV9kqkVI+eEJcZm6t16TovYfQi5hey\nl58i\r\n=NfPj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD48EKGv4py1w/XTLmDS7vbCreEYUSOPyfQrQ0isR59OAIgJwGxhKIcTdKzz5GqhbwahgWYLqoCm5fiKM/FnxPSiNk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180711071049_1531293242358_0.47308385111386153"},"_hasShrinkwrap":false},"0.3.2-lazy.20180712000747":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180712000747","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ea70d3b6e7712e9097b84c6154329fcbff8b8d6c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180712000747","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BWMjicz4NCZ/D4HEv3QD6GLmHdIZ21+3jjuSa90QqiwuAaWcHbI9C0xuPW4yRWGBcyIHytTtlrq5lBEdUmrZkQ==","shasum":"607e5a57620878c53b33b1eaeb332c92cf11be19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180712000747.tgz","fileCount":758,"unpackedSize":1728033,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRpybCRA9TVsSAnZWagAA4TsP/2PJfPJteAGaTzR2BGs1\n2DtElpSGopn2qU8C9XtEZy3j7bv+IoirrCwX/8ScVnv59v235yK3aUdvSS0N\nId6PYRIqW/9Tor5ToNxVfoHfXC+rrm/KnlAGUA2wjWMfAlX67CSzePYMPRrI\n2Ug4vAv7ILlxvKR85EC4FcF7erGdOZjOVjOWse/dTF80g7/Zs/ppkf4BlHbi\n8sAAqRhITeG60pCw+Mo9yK8JujLQCfiljYAImpMq5QkzHk7oCCyAEHSdKAvv\nPgBeg6WI/1zoEsaBuQG1yeJNnzJEJ21mWFoDWV9aNC4yL8uKF51+t9FlKczh\nBhsTOrxIM5lNkyDGQXue7bUtirLo9ujKfKhEUmpmLPgCvjQS0lZcPsKfNgZ/\nbQqMoAyCZpfnHCS3gcSkXK6LP1rewbfr9wz+taKua2YK3sjO3T206CHWY0+W\nAiVmYyt0a+bW5LnDgAWMpIIPSMGFmKu6Nk3I9uZ3kTr2bVHonwc6RXBJjsev\n8ruWL0luUNlt3hFHJ37hJElJOWaaZUm7Vgmo6/rwoqmEuA88byXV6Wk/ijuA\nxyjL59hDdSY2dKMEtDhiQ8Vcf+D5+CIEIAjyTTa3umgVRmpLWhsn5vFSTUuQ\nk8RnZhizfHTSk8l4qhw+mrL+CtOgIartiNkGDyuwpCo6FuNj1nCYBQxqNpHT\nFjm9\r\n=9n54\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCKf6YaMtpHCLsjdR5oVSCC+wVmkcsFPwrpYKaeTj9PgQIhAI6pSDVOngJT5sJpoPLjESkLNTPY/j2JQf9caIKKUQnQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180712000747_1531354267247_0.9607537627908918"},"_hasShrinkwrap":false},"0.3.2-lazy.20180712013217":{"name":"@atomist/sdm","version":"0.3.2-lazy.20180712013217","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"599bc5ac5ccbb47239a6d1823771d51ee9823e91","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lazy.20180712013217","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Etp7THCEjw0i+Sa/B2XwXEDZivmkGJ2eA05XReJCZveApk6/binrZCupUBWQmioelfDgBzDUHJKiX9isiK2hYg==","shasum":"bd4bb9c4e7da9e7988d08519f0da8319a20701f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lazy.20180712013217.tgz","fileCount":758,"unpackedSize":1727558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRrBkCRA9TVsSAnZWagAASUQQAJxpqCpACnOB9szvdzIa\nbumr2EcvMDdAzsYX8O+Fcd1mEhcIaQvMctLrMdkCLqlb1vSRQGL8pyoAt5AE\nOAf4cs2FyBilXnhy2d/90TOpB9tTCh8bEWN2itmjn2Sdsoui8JVjn9yX9WRd\nZJ47AJ4ddrrr3+4jqQ3DwLzQ6gKQbGUFtkeRwBZs8Mse7quBrSlg5M+EJvYj\nrp3JcUZNFINOix/wV/XGo67DNEjQ2bW2woFz3+4OeB5h4qFiNj0+n1WAHcXa\n6fsTa4xqlMDrF1036giyhgslvC7SBrPpOxaB5y2cpR0PoDJ+cbT1nXZXiUPN\n5FuHu8u7nu291OsnPyT9/TculIRRQKsbFxcLBmJt6nxWtKwBWE5KKdAqq8MX\nWtADRTY7Iei3zOyBL8nAunBteSDu8DZiG4QcaTz7NahaFtzUpxhleCjTtsKU\nn3S/pHlB/H/q76fqZfIUBviCraOlrVsP5lz+syzbTrbC0btFy0KatBQzCA7N\nqyAVwinuApjgfm3TCVFYjyzP1mKyLxr8JYrGyTv9zW4Ka9Z9BJ4TcESUivhN\nj/bFbUrkbYBDoz5eWkS7pPzejRACJjl8vUSfXY3x22wNRYK3c5lfj5nmQCJ/\ntXpdLJ7a0/6aXeFATaF+6b7A3kv0motvPmqmJsXgEzYmV1mOw7B0YQxBc/T7\nEx12\r\n=JgIs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICjA7onAF8MQ0wzxd3R9BB1odDLqnqyNBKgiT8udczWAAiEAwIvXTyu40vkOkRXyyJ1TdaMGtLYeqRl0ptBvWXvf2xw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lazy.20180712013217_1531359332877_0.3483476897863331"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712024716":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712024716","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"07a3c2d8d4a7ab6481ac0261dfed7520a42c4b3a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712024716","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kEf2+7Tekq/xbkJ8yxZnB+8hWg5RQ1fwXwZep2WR/vpZGJ1opfa955daQo3FwBG8wDAeQnHPPb/bSkNcTeXJcg==","shasum":"952ff08563c8f762dd04dfe3774ed680f2c9ebc9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712024716.tgz","fileCount":766,"unpackedSize":1716465,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRsH+CRA9TVsSAnZWagAA79wQAKKLR6nR1RwujBsneLYL\n7Yaz7EdtkHh880bd2fwmkhP2ymrV7WurOkAs4JgoykdD9yAY4/1rT1tRUnRz\nse14bFHthndrHVCN5yt2uPPNFRZPT2guCJuXUx+sDDXCTMpGeQr9+TFMS/Gn\nERbW9VKtzf1Ep6ghciZEhVddFmYMvzoPzTd2apLCqQ7EBlb+wZY4cOeLb8e+\nr5EZ/ZzIsRYbTmbBn1gRRQixqOtDK71zB1YwHJCqL+wRWE4tMO4LJuOaU+Jo\nSUq7IVO5/plzfNPeIKJuLUlirg4wHMYBKMtqJpkuMAnqyfMGxgG72kJkeDZm\na5yL6JKIiUkmhPDsR6Nhpj52fsb80gqMXehDwx+NagJ4/3ojJgHBsrRRGPHC\neqODMjVivSzAzYi/XOp8LbChIi8wD10phNDW5sOGAK0NNECwBKbHJwRIWCQ0\n8Klfp/HCQNoV1YopQAY6TGgspWKaQEJdGGcLZ3VQDY0grXkm/CExVfXCHCn+\nKZzKKhfPSsveC2+YWEa96GapfGwoJ6m6GumwCqNwcpePmatJFXNShn/dH/5L\n0GfLu7n9OSq1vQW/cegcKz4NZ5uVispb/BFwzJhL7rbRGcqARpKm58LHXSqj\n4aeIhWBwByf2MYV5NDk9sHqZXxdbHUqdMormsg6yzoRgdXmoLBt58vw+2BgZ\n8V1/\r\n=V9E6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB1o05JHXOToIPU2ETFaDUNu+4XqblV5AJn5NYyN5NbvAiEAlqfazA1aVs4TUoCmjlF8YT3OXlwWN/Xt5tQET9XSsmA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712024716_1531363838589_0.7788800023446187"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712025100":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712025100","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ecd806ded09fde9c3de58f6ce56389ca60a9a5b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712025100","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-L5MZULZgPBXM4ARDlZw0ZmuGGl2bhtmYxstjgK9aweMxaq1i1vlNZI+9IH8vQwNqr4SM4/nvef7UxgqjldYruA==","shasum":"c3bd74da7db7cad0005eb45f56995d710a7b50a8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712025100.tgz","fileCount":766,"unpackedSize":1716709,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRsLhCRA9TVsSAnZWagAAKFAP/3wii5dib59uGR3foKEW\nb8maogZ7TULAHBBn18m3LAwSlLIa0xHAsGgcB627Kt2K2m5bqVHRR3rQ8jdz\nVzHL6v8pmeabR4dfrpgMNGyfXt/bBMNTK/yFdB4abPAhR/zwTrAsAYFjaUth\nZROeI0LoxMAO3rfRc9vTIGgGBouwpjAGMELhZeNUxNsNTyZ66Oi/VceTw58p\nKq6wiQSP5ucLixopmrVCwCWolO/Wuy9bUtGX813Nvt53pKX9i8Y4vMIz26+i\nKpNTJBSbnCbsGzrhAed9GHTPsLwPWHs6epvNDpdMDBu1LLEFnsuLk+MYpfzC\nZRMUvym7cROj2RuqSrGt/V20ByZBljUeuOprQuRnL4Ipo85V31i9RZWhWF1i\n2nP4xOQ118iAybamLuZHweXu7iIvkiaI7anosFAK4/N/7Ond3wIUCjF8YIdp\nepaDtDeBFTCeeJlHi8Qs49KTozyulonBER79nSikCtHvbP9/i7NJJulChYQY\n4n7GbVluq2Pzkl0BIvCkevKoKsu1Yv0P3l09CHCQwMgWzs2x1WCBxU+SKVib\nYmibLx4GMK7wPr3M2zLhV7TXJzSKG1ZjZoCCH2VwHby/DQzt+HetCm5xoqlf\nv5F1yLuy3RVDnR14JSM4F/+edHVG3W9htJLxnWE6JrQ1/CAHusSBrbpP2fTZ\n+il2\r\n=HpU5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBsJaHamci52Rd70tCIv6RtyRPb7z2MiApGFtvAtIv3XAiEA8dwQEnUr+9RTeD2PkmLT8eXLtqpvSZBrEuipumSG7vM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712025100_1531364065186_0.9217558821868106"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712034742":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712034742","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e52da0949415cd324cfb8077afc589661a5c39f8","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712034742","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TEbm4qI1ek2lno9Tqjf+e/yhJww0Z3pcwzuZbH7sSy2D3uv8fTU4tZvhenhq8rq3B6vpc6fuo2Q4qQ33GHzLsw==","shasum":"e611bc8b0a7e481b11a37e3587827e7014537def","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712034742.tgz","fileCount":766,"unpackedSize":1717941,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRtAfCRA9TVsSAnZWagAAclIP/32Vm1JksF9L/PoWCcrd\nTPH63DQlaLS+edx+zQcsld/wuC7C3UMczH/jB72voI1oUKkYwPR6BPIKBAYb\nMR9sOcHJuceqg9p3tKDZGfaQx7GVOfz77fU5G9rpUFSvbvn4jzVPruzCrkWN\nPRJgc7PuHem+Jk9Ofair83dfsIx6oDVImEhtlTEtp5KToQmPWLP4LNHKSVwo\nP3xyIS4/vDMWv++Kv0n9GVdlR4FWYmeyA6WULkWu9ce5FwCzeMEk/e06Yzwk\nM1kgKK8ouIThf0SUBTnS5MdFekk65l7DwCOipYKryAE8GAVsED6vuegclWby\nYHXa6bP0ogxW6d6sii7tnwcZ6vqhziZ6tu7AM/Eg34DrKQ0s6mDb19zdclyD\ny4VhP2fULBXU/x0UA4fj1VKxW2Ysq99Oa1JaJoxAz0x4h6wslsy9QTlZGOyA\nmJ9bd0h8tUcK6IrgOt3FV0liNNGSlqGjkWqrjSS85G0IYYAjvk2BRrSqUduv\nl2qSim605WQ0UcpR+Bn/W8U52F3CoJKe38vtVnLafktWKGmxwpvFRb01uop/\nmXDoQDtgMDLFCH2bGeAN7/6S0sRpYyGfkvTmTgcxE/RhUPfPyTF1itdWlK5+\nDS/6oYmkkPZKfNtdaRn7JVST9INnePk4GOWxcBS9DDKA46S0fbiYq8n1yrvg\nDKjb\r\n=hqLj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBT9YiUXcvVG3TSAGnV5QAigdkzIM8MAfjobQjCkmk0rAiAghY3fATKSUHSTAUsyiFNgTslIGcsepHKoaK7c/PaVuw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712034742_1531367454894_0.44680157113722285"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712041512":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712041512","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"218a79e827ca4da64600110a79ca5e57bb4fc991","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712041512","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MYul/JGVgY13jiJ1X0uy84Wbane3I830WSMwx66M/+NvCQd3EqkA27tooiur783WNAuIQ1i6B4AsFTmS9n7i/A==","shasum":"13bd01eb27afeba3410a1991d714c4030e0b5d09","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712041512.tgz","fileCount":766,"unpackedSize":1717972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRtaVCRA9TVsSAnZWagAAwKgQAKF+LhKf7OQSnzrs6jP5\ncViiePStQitDYM0zjVSSOiCQbgTam/guTzi/GMPKZoijNX/0+I9TVjVQstQ+\n5dPR5rUrBOTogpRBi0/ci3DFKFO0MgeOkaagnHINwxnz0MDnRFJSEM/t4dsn\nWUuCUyTd/XH/NMhTuMGom/ANUKsQQh1TM5tDbD9Y+AVnwuowIxdGccwDmMY1\ngWa/EnycVXX116LfXyW2r0VJT0XNdaNf0+4zkBOBxKSVLHcgW4c1l+H2ebVU\nPDQh8G8MDKuyTr9W6h2gf31vbh8qmsf89MKX+qqSvbkYMNr3+bra7eJ4TnDV\nRk7fVsD3vr1wc3SuprDQ16RAlMUnZLyEHINltq84LhMUeMglxTuU9OaXhE1a\nyeZUuJK21i5MDbIb0mArmBx/MKqn4HPQ7P52V51H4YoSutex9F1LWLurpZXP\nU7AupbbC/eofVz/hdAfEDdXEEKJd+BRuhZX0Rb1aCMuhuZ1PpGtckklA7bzS\nLZF5PxF2pGRuZqNp8yNEu3hQSwjlxmvVWzAWFZkUf4et/JG/yerVbrKqR1Da\ntCPkn/TXp2bYWzInYxHVt54WpbKI38S1aPQhTVrlQtd9I1pN5pjlV/x98Lcm\nDkwyj5AmARW1whVFXRwNhTgjBAY5/HBvUlsnsKaDZsobBewcBsCurFxOYg5k\noTYk\r\n=7X0s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFYTp976c+YMV0ZHK6oMhJjO7GQwmCVCdTNxpVJG0hPwAiEAmS1llGSagit76MGD0X/u7a/c8wG4kJO6uWl4Nobk70E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712041512_1531369109188_0.8817188924990587"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712043342":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712043342","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"155ee025bee7b44a92a0b43971e2052deae0b137","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712043342","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-K33kxWtC5KGwykA4n9n/JW94Yob6U888aL7iY5SWfdZ9BZuXsQSMqGgN9Tq8nFGLk5EtSzX7j1HQpIo/tc4JQg==","shasum":"c84cb8510be5327291c9ef76e27b2d10e51a8ffe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712043342.tgz","fileCount":766,"unpackedSize":1718550,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRtrtCRA9TVsSAnZWagAAk0EP/AqTz/Osg1AU27x3gCKZ\nveAkirvriBXwmeqykVjUfX6sFOjEqWwe7DxKZFZGXyCqDFTAGaoztnDegcfG\nGoJrAWjcQAraOAtY3wJtUL1qL+AhdCVwuidnYGmugd520VdGIMbfB9IbVly8\n7bqkl10pfX1ad8m9T6mlNhh4l7VmkeQEEsRyzbBEt0RPyHwk9BO7C2QzJMMs\nZgKJUY/Q5+w9eABgNc+q41xO5D9ilZNUSgt9ZIwP7XXSyG2fY2gGZaFAZTRE\nFpyRilIzWhTPhmGUX31S69oMNWcYRapPFpcDKQZdlA5ZTsHiA4VSK2e5vJSR\nw3aY7sVNJAOxUvHWSgivECCjMQtXp4KysxXywoM+pxN1UNLkHmraWKtCky3Q\nfF8zjDdQ9S+VYDAENCumgrHvpOdpYGkSijeBoveOM7k3NR2lbOg1hJ76QWnp\n6QHJBYuqUPeJ7Xt2xyK8bcBp2LNpahrmXx7O6aHfev+J1tLZszzwsnsmPCI+\nrXcvSWIMMDoWkGdPQD//HwP3rZ5lr42Dphcnh7eJYSAt3acbcu0ZrqYq72Yx\n5xeqh7b7rL2L1iqdq5MqNz1WYM0HbaZYNLx/IhfXgNZkbpO5zJ2Xg7cCxwCm\nev+26iv/MpFrx/O7zJZtIAaXevQ735QBz5d0VTcoJ2M8kljnJmNFeqi9NGK6\nPSaF\r\n=9hp+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtT4WgBNGmjLkAVr0OJWrA+CU9ud0CsWsMXtfDkZym2gIgGaomrMVYkJ5wqh4T2I++KgV3aIun7EJnhgbxLzHjJQo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712043342_1531370221347_0.4813646762380883"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712050924":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712050924","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0a5612e1c21f593d1acf8751d77d351ac9a0a442","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712050924","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UUEPM2RAogiDih7LpPO9kvAG5vc+CF0qyYqkqXzVsnbjwLSWLeCaI9gOm5yO+PuLUFR4PE9RifccPnjWRIrINQ==","shasum":"07450354baeef991d7e14627837e96f3dd03725e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712050924.tgz","fileCount":762,"unpackedSize":1715430,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRuNDCRA9TVsSAnZWagAAxkIP/2Werpzu6Paa+R/mHgw/\n7uSh4HTnkdPRn0nbgMsCXisW8OCmxo1aMSZajTEvnvOIdZ07tDcIn3xqzBFO\n/DnungPeoUfza8oQWOT5XEFdctawTuilSN5+6uea1WHPZOfQfwOedwLdamQW\nWw24S3e4DnbkGmfz83YT35vVCSBL0Q62jiMMw2zpB2ptLbSOF98MkJcOoLXu\nkIVddER3/zIQAyLqikXMOscVtLrMl+MoHP+yrqWYAUVDsc8Z5y3qHWVou/mz\n75Uh+a1onQeQptfHVYtLm68rLarkRmUJeq+LH1lIWu264L1Cp6SxS62HQ2c6\ndqQK0IDw1vH3xlIIveuTA6BrgHBYxgH/i28dxd4Pz9zuz0Sz0oNE3RDHJ5no\nAh6VKvFuUg3MqeicCf7VA1mwJjT/ZLpXgezM2BXSuWgCP5fQbImW1gVlliIv\n4KtIek5/jXKsBcC3GnzY6PTmPl5Rpw6Y++ZxH2wEnTa+arcKUrWv+ez7+1r9\nDJd+4P8vT3yUwQVkRph89Bmxk+9TlMHgp2EmqnKCENGVcz7NIqVXTe7yxLU1\nISDCQhkijesYImxJD0j+kTf2nuKcGy6HfD8fuerN4CMzmNO9ZAI2hzMSkwpU\n+5VKqMNZDJi0WCi29o7txBggF3XI7EIN5s/3AJl5nkA8l92dA/858/DXIEq3\nKEHZ\r\n=F3w3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkl6pj7kYIYxXAvKBdU9XAo/HfZoVZAnVhB80HzBs5oQIhAIY6HFErmneP6RRN6T9q8uO632xlnLcnvPd03UlWrEmj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712050924_1531372354239_0.7721452041162455"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712054358":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712054358","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f3a97eda21b5b5dcbe2ee6e286efea83c2b45c56","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712054358","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aqd0R9rhkkPx1Fx6XjTy6wb2ES/Q1gSj28jg8RFYrQezIc2ml12uI34v062loQcOWsnLE9RHg99wf4RE25/c/w==","shasum":"fab61bcbbc860567dc25133339046f53eb513bbe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712054358.tgz","fileCount":762,"unpackedSize":1716052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRutmCRA9TVsSAnZWagAACmUP/2qCBp4BHy/wqcQNG7fc\nrgITLeMoVAMr1c3h5mVx/2HI7gmBlo5zxk5a1tZ/NiDN+v19GUabk5ax/Eti\nn1U0qqOFIiFXAaPm15NW/JHND+FlUYAGy+UUVkTBdHOUlbdh0tkyDl7v4g6g\nhdJZHSOSGyqvYa4jHrNcuKye+gacIoz1JCa2KHhv4+OjXssi5/i3yYBOxQ+b\nnoLKusnTEu3NbWZLjkOJrq8sZRyAIjhMK3JRaD6HmFE7NJtzMjD5hXXQEMRS\nGUnkT3BGw11t973IN2hR0tGmo7X9tWQGg37nyF0nOV46i4S0J6mz7BpGGPV6\n3aZXRv4uuLcnCJ4ADGRPAD4KpG28Om8HiIlMIaR/EU6em4G1psA5rh2zlE/S\nJ6xBXxU8M3TPlSrMDShVzSa+K4jATRX3GdUEgDguq4gR2jI2uaVzG881KA7B\nLDv8HN9os/UXp/dGQWE3djapSnadlCu/qNHTzX1PZVCoBdmPfhBLl5mR26WI\nW1TXzEI5q2hjVyqj3JMyA187SxwdcNXYKJTZmIaFKmemuzkWBMwGDsNbaHG0\nkTmniMWpkMtrhUfCZgUy9twUvsa36A7+2CHKjLcWYTZr7GqIa/vi0QvdqmkZ\nrnNdC1BkpAy8McYknDK+8Rd02eZJP9X4vs9zgF+UyzskE+c8Owdd5BP9KPQP\nLNf0\r\n=i7ix\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFdDrYbzuC5jZ/765hoWYRUjd+8NhJnEzIazHVWBOQdtAiBAoIjzh7s6ohrTbWP7XOCmvJEdBzKWdvjHAFTvyhSqwQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712054358_1531374438801_0.31227372467501846"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712055106":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712055106","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"49656ee285c991d1e69075e1eea6e5c4db380be6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712055106","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1Ytr+fc/f632aIMghPwNw0ST7R6StAqwV3DL1fBocn4e4NCUDAvb/6g8T3GbSWyCLkVgTY4tDGrBFad4YV46Mw==","shasum":"98ed4d05c123c2aa9506b53203975e32275434d7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712055106.tgz","fileCount":762,"unpackedSize":1716066,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRu0MCRA9TVsSAnZWagAA0XEQAJEli24BgDDw4DmUwzWd\n3iZk4MzGptakbyPaDUHJa8+ys3bn0cMeysZzA7v9nwLmthIcpCYVJVuoFqmP\n+SQItF04tFoesz4bosV8kO3Fv4jmf1+cyutIWLY4ras21XHn8AJocB8/fRg/\nal5nFwOCFsoAZRcarQjsW/2Vqf67f4QPUhYwBAVAeC7/cWvQjzKLEuDCS+LI\nascYXCSyid0Y3e2qAmiEd0yZ1a7ktA2lfkQ9MH8boVoJaltoYcE8XkjeFrjc\nUrPjQoq249jKy9O/HCtAqt9aR7jLHxnRnsR4auNfwAKiOPZOHoEtiWhlX1GL\noKCEhEDQWwpvf67Rr4ciF249bE4FkjbYEI7YDchZZNN4cFV+8zdFApTWNU/a\nHdBF73T5/Jj1iudmv0UnRO+018LXp8K31eDimWn0VNa+vShe8E/Iz5rAEEqh\nOfLnWq1GXk/0Rfc+xGUK28A8hsfj5yloA8JnpwS0fy6/h9zItMfqpioi3BIX\nYWo30OYEs61zdqd4Q+D7Iea2d15Y/DRQnzDkoZAJAC6WsO/+Rx0G5MMt2S44\nuW5gMO63ilWUGN+Thyg6W8en9whyMisg+eg0ZdgVif8jY6j1crbRneNVxhpV\nJfoav8INspAboJQauBSFDh3gnTx62ZlYzHJfDad15/YVKtmYZc+K+sF1pGcR\nJFtV\r\n=J1OR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE4mANOR1aPxvyMqknX1jRm1N+znOoa6a/q+rMHJHgdoAiEAnBqv0bfEOyLY278hcgRbQOMYOOC11a36XK9TRmVl42E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712055106_1531374860336_0.8323165711541523"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712071458":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712071458","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ccc787b8191605675bf21fb030130aec1cb17408","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712071458","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RaQuxR5/NUfiQ7mJxz6v9T0jyycfR2W0xyt4eS3MzxWtWEhgWe4zpo9HmRDmoqoOq937DkS9KkUmsnIMD2iu+Q==","shasum":"f16958980c794e93db16dbc6cbb3e76b27b5db3b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712071458.tgz","fileCount":762,"unpackedSize":1716130,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRwC7CRA9TVsSAnZWagAA3TsP/2MSUOTvkuXO+p+N8hOz\nUXpXajzD1v/4qno9FpmX3J2Anz2HPuj/TRz6v18Zke6lvfESXQfjpWZwRgel\njjHGJ9z5/3h1mS8YjwoJi1NVMo+YlUVqkKHtUman1VnEHyRSwqT/i5eDBsyp\ncjxwQQX7dp0Nw01f1BPGOMZoakqXrr7MFdzYLWDYmnIZvqmSdslIkbZZk98E\nMVysmN5xxy9cpe2LXv8I4cEC0ds6NuC/Ep5nzVqX6SPXm0SmHKCByCIaf8tt\nPsryxZT+QtcxFh9TjFsN0iHkwvrdf2NaZmvejYWVMMm5TkK/21T1bNF9QCNQ\nDFHEBAdSS5uvIiKBN6pROXG+VxLZltuH/V2kkdPi/1rDd7DRikNU/bBb0Z9I\nmIR3RDRe4sx6sSV/yitVx3pdA5L+0SZ07pXbVqPoKTYFiFH2M6FUws5VOi6h\nOUWz9cvjeEJ1xYU39UiYlaH7V7aXciEXaPoEsAtSZFGcx676ectmFVmJt+fn\nNxU6a2RTlFKtBv4dv5XgLU7wsy1R6N6To7S/0Xos0zD22KI45diCUHmD8O3M\n6YOelHOGWgevZzsIgjqaAQmY8/TyIlyjl/q+rM+tucGnrFl1Zv7rHEDQJ4yS\nhdeojWHCE3OTP5QGDBvCGgvNDJSJcCC82j8+1jM7RDfELlU9K0n4DWOFZBjr\nLZLS\r\n=2lNU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQcT1KmloMB2m5srkfrtsPhSgc9lwGQdW5lT/6TFfBsgIgeFW8Itaf5McZ35qB+yoN/K+8Dw/sVrGF+vwHenG7ubg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712071458_1531379898866_0.8242619451330917"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712071725":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712071725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8b8c83dacab91c1c00b758457430ea6bb8f4db47","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712071725","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-U+vvYIss09WeZ+SxcCpoTWaDGsFJfB/DIIehsaBJhN1gmEsv+Yn8sVEPN/ETfQFd4qW956EY6d74bhKGZyayTQ==","shasum":"b3389cd36c5dbfc3f55b26f8f61f5e9ad5d1f5b0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712071725.tgz","fileCount":762,"unpackedSize":1716174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRwFUCRA9TVsSAnZWagAAL+YQAIv7pLQYsvtqZ47YqzOA\nuvPs7rSz/crEmzTAKFkm3s3Hlrq2kVqVj5PoX5Cf+fH0Y/Yf+70EZIKCTrsc\nrWepRPltzcv1K7nKd2C5+593FivKNaalsjifDTvy/sbtQ46YyJ/CBZBxfE7T\n4VNRgvezAjiv80EfokYwgVz7nQAbJyzWWc1LQRkLWsP3NNYdK/+jqp0zzVse\nJUYzUrN7K4KuMfUYg8CWIVNeh0p3PqjqXf7lCK+mMc7Ys5ZzGRyMwq6HErgY\n05GAL3Vvh9NSFKYBVlxkCmWZcMhHRA/6JuPuQPgTgGQZbbpOXvFUGFRbOmm0\nOsj4xHo6eqGxceD2HfG9GoOhbSrBkHvMAE882YVmMq5hVmEOY03/CP0CNMNO\nvq7qqgBLEa0etAlPspXEBH1axxZjALqxiu/WzpqgnsLtKh4PIb6B/exMGJYL\n3I2u01w+czcfNM6NaPdQBWugFgjhuqxV4QfhxtsZJHKp4PWUvjUmpIFBLca/\ns0+APGbLKiHbA2Nhj7H4AJ60a2B8bpCQv07iAwmIBmLGhprjhg5OAT5jpZ4P\nE4qMdy1dK8uZcYinXJJ4CKuXeZEDX8H6aXYmImsspFASFnJxO9ZC2Oz6MVXh\nl5mHtiXpYK74UtUlRVhAFKHVgHt9VxzhHse7z5ydwGw/Zcj6g1gyK8MCdDnz\n4U71\r\n=oZGL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA3pk1Hykx+d+1hFSc1LO9+Kv9Xbz0zO13/1Z7r74MG4AiBZqQbJDjkgh6uLigCzOMXmye53SP5QWONLsbLWhCxDXA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712071725_1531380052053_0.7328470049536169"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712073656":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712073656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9bc2d28d4e34017a34b65dd51d40a0f44f2fd28e","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712073656","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-inhFZbUEzcC+XMnul8gR9skY8PQN8tOEsE7gZKGZDUPTIENi1qCgqyMVM5Q0vVAZHQMjMqj19SGJUnjjKMeArQ==","shasum":"408d3a5d775dd445ff74203a93a17c19a750b0eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712073656.tgz","fileCount":762,"unpackedSize":1716312,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRwXwCRA9TVsSAnZWagAA8c0P/ivNiIbDx/Fbz6CTNWQ4\noN3S7d8EViLlSqstKxLqhy7nEheJKyExRGOJ6ViM26wn2upeCPt6sAJYDGF7\nz8KxrvaLe3xkKkfmBvAHPcvM1XG8ROsjr1sQwoafj7vnOlx8IebYkOiJCD7S\nV8oQh9rJaEtIJ8+nn9lalky/QQ/AmzTvF+EKroBqoHJEREJma3+o1chGkbe4\nRPVLvBsOoyEikkmh1VHEgcOnQzHNHJsGfFwniebPGGYfysFfqf8BlAR4/9aw\nHFeon4BiuoyCyGH4cug4ncvUYHUQamRjOK/4Q0DUmCkerwj20vWkJYiQqC+B\nvY6VluHKYQhNhJXGz4HOoGsNN2+ElCFBzxiy265bEXo3VStrrdZpqVkgnCOV\nY9wCPa+4vy5G7heEkGU87NeY/MpV8H7Oq0i7mSsFGsm82kRKLo1oxrvGsvRf\nGLkGOW9gLqXc1kVDvAMxhzqIgu1ZzLulX9To/p3A8D1lyqG9PNiYVEsSCQpV\nztGgcbntYD3XgMPtNc01r17PgbqW4cXqTSCmg85Wle3ZL7aUcxFOPKrJDLx+\nJEn7XC5842Jxfqloe5B2KwEWNM2/d/odKfmETCcdJEKftiML1Ui4i5VeCT7z\nK1O+Jzb1CdlfMqfXBWlFs/BCfug10n+AJdeFE1IASDUgp7OAgFattcdMZDFP\nolq9\r\n=3vt1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJP8QtoYjsdJ35CCJgIUwjnXxNBUL4mdbKj4uX4DDwZwIhALLJKnZVi0kYUv81b59jdNHarUS/31F2WdwL46gUdt0l"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712073656_1531381232635_0.2500335576757837"},"_hasShrinkwrap":false},"0.3.2-lieven.npm-audit-fix.20180712082812":{"name":"@atomist/sdm","version":"0.3.2-lieven.npm-audit-fix.20180712082812","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^6.1.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a692034d34da0b8b3adaa5c28bd0238180cb3786","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lieven.npm-audit-fix.20180712082812","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rlf5XbTAcoZICp0pZIqYvgb3HJys1hfLgGkLFJ+fIxOmUYgG/iVo4W1Lprv6ipthiV1GnWrlrmUuMcF5EhW11A==","shasum":"80211feea9ba1edea2ab16bb79d924219d047272","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lieven.npm-audit-fix.20180712082812.tgz","fileCount":754,"unpackedSize":1709761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRxHdCRA9TVsSAnZWagAA2ZcP/AlPmRqRka+qv1dBsQEw\nvOJDw2nFfPQr4r4ruPmhf7BwwFL6wHFHPxBCsEswkfkpJi92cCw6y1rMP/jQ\nEXh2KMBpu8ljXFVfG7rzpy+krIx9ocFLArXwCrQ+Id1YVrJ+f3eW3vIgIhBz\nB3RQvq6jtBP03e9d6JbTaX81NcyJFO3C2aVrhOFYOckp1EA6kXo2YxxJym+Q\nqk62AzW9tvtoIzU4nYCdfzVmKc3seFNSEmA5xJn8aaZBwpfJYbU76KT/m4iP\nvq/AB6tnWKYDl+tJO8qUj6tPV8F7fAXieOkl2PClm1ZkSX1J/AEH4vvBJXKO\nqA00diwzhitiViotncPbmow3dbAAUDwsMu+PBPp+0/1GnvgHmrPaCG+MoJbE\nDbaNzdra4F3UorXp3C+DdcLSkNIxMG4fBMcGzAPAglSId8dSwBo4/OMtT2Xq\n4P4wpGDmEhNqugQmYQ8uReiBT0uwTMbhiBuPqbDXyES+8VPLaLuvlueDTCDK\nW+iIJmKexnufso1I8+l7+yBxGrcGKR4kCS41ED3Rw0mW1IacLVz+YbUuixkI\nWP0GPjAYXsdStGaKEerNEUYv2Fn721rsvAigWh+WB6acaF/JBaRYaHO2FVj/\nZd9RbBZMm7sLlciAVDanTI+nXCHr+Bp4R6eK3MkGExZdoUcyK8ahNi3/MuPg\nP2v/\r\n=TkDy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDdtpVkEjU1alEHyq4EosxKVeD/yeFWdC3p+81V6EoLwAIgP0QJED93FmZhdZgm2Lw1Znv0ojEOnlhoVlURbnfUdIs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lieven.npm-audit-fix.20180712082812_1531384285407_0.754028608711864"},"_hasShrinkwrap":false},"0.3.2-lieven.npm-audit-fix.20180712085609":{"name":"@atomist/sdm","version":"0.3.2-lieven.npm-audit-fix.20180712085609","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^6.1.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5f10bded1229f10ad08eac25c41fd2893d7819ba","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lieven.npm-audit-fix.20180712085609","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ytdORVMOOHAiu00U6r3dHEyRoj6DyO7BJvmcWO6ADnZhdbg1T/9ObWEUciamD0tck2B+u9UB9okDIpLdJK7zUA==","shasum":"ad19f956d0acbae00e675cc817225a278d17b535","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lieven.npm-audit-fix.20180712085609.tgz","fileCount":754,"unpackedSize":1703014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRxhnCRA9TVsSAnZWagAA500P/RhaNofSp51FDnmwF00R\n7lFF1VVAA89kiGUdtDZJy9pw+0OqaHELW4wlyEApUNaijLHbQZJqc9mH9GNn\nhJL0I1d0f2zPdWJ70SWhtXV0Rf1acYReJQEXvba3I1xpVmwIMtZWwMC4jRbl\nMU3rqLsu+GQnbUtCbflL9uiln4HaPdH4AwJjm/mscoLpVsADJLFZe1IyHkSZ\nSYajP4UJGd6XIotU5ELC+lnWAkTAI77iXucklPEO/fA7xdPiLAQHBwqdQ1mQ\nuFWL6qZSaaFQf9Tdx2guMPQcGzc1xWTpR/aXdKa99q5pA5PbXbX844+y2D+o\n+LplCPnsvh7LoL2oWKGPhzuRurJJO3wj/AtAI8q+mzNZ0KWRLkQ9nOleT+4L\nAurW7M9a0i5uHAAzpR/0wBzkb9AR+UNdnPvt8lDVqK4bAegjKXbZQdZdHOiC\npvB1evc/ijHDScDvX/pO2uhwfyvde30AB1f0U0DGXq3cC0ljYkLIhSx8ClFJ\nISDVWYyTiEIdHLHeLc4OcDIBRsAKfNJCTyJ//eJsp/3wRJvm4qbe1fQFwj9m\n1NxKPI1akz5I4RweMSuw/+bQr2EG6qhvIvEA0X9U39EMYmT1LDX4dRnrfvPl\nC5Hc8y62jJtALa01j/2MNIw7ABjBkXIBWWOLVXvFCkPkBKfpKH0A0pN+aczS\na3NR\r\n=0SAI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsxYbUBURckieVh6k6RnVuURQ0nkvfLMhYFNDYDe3QVQIgIMay9AfwzWJZD+sH7b4smoK7IFXTpeRurCzs0FyNJu8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lieven.npm-audit-fix.20180712085609_1531385959688_0.5482361115551451"},"_hasShrinkwrap":false},"0.3.2-targeting.20180712091712":{"name":"@atomist/sdm","version":"0.3.2-targeting.20180712091712","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bedf7319f4c415a53aec7d0137c001e5a2d97cde","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-targeting.20180712091712","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/7B1sSgJBahIKwCoplIkJjYP1XeOguu3kc6q9LG9rLtHfmFNyIO8zOi6LnILTQyzPyqEOCLNrnreclHF4JMhsw==","shasum":"6fd000b87e73fc7a2f54c35338da6e6fd9fd4990","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-targeting.20180712091712.tgz","fileCount":762,"unpackedSize":1716748,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRx2WCRA9TVsSAnZWagAAdB0QAIHnFaaRlPp2ag7qY6RZ\nZGmb1UoGTuS2jwv/m76HKMkEP++33bj7y+EKXVnC2hXYZII6uSqPObuAooee\nhDVAykd6nGR0f/0c1k4XHtC8inuoVnoMy/elFCl+3xP54HeEnHB/Nm+NpUJo\nF8pT/hxNVQQHLdYByAiEVzr8FAEGaXEMfSZsDirW93Kl/ERKcPXkYnaWOA9J\nqNwbNXWa629xiuvwP4PAn0vjUcxeVhY7Um9/l8yrBTWXF0/dwqqeORHJD73s\nyEtNcXgKUWz3sGr7bNJtgFUpPV/zR/pF5Q9uGwrP/Jr3gUc6O7nPr4zQqql8\nI2nUzYiGO2NYQbzBvoIWf96eQWHhVwv4MFLrg8himdbSTu5HWKgthR339kcg\nxK9pw2J10QIsSOkVcxXlvtXc8epal9AuiXieDMmGtcMceVPruccScnb5yxA4\nt7+7WcWdXOoYKA1LYmmjeO29swdNwR4VybCjwY6Wpuk91weNtrQ0EOv/XoT2\nOMoDMIUnDK1YGTEPJurOWIJZdqX9oHn7O3YlG2oPFJ4jKa8eq8ZUoLPQb3Xd\nFjFG+XVTzFOklNQLp75jkoY0bPU3rVK70lrA+fPluRlvZ68bBHCROfTPN9mz\nQTusZDV+lybbhhXFkOt+prdiLdCNa2nTH6HAh834q9HWzNUnNu95UtMB+w94\n/XUi\r\n=NXko\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDb+M2eU9Ekln4ROi7jv0ls5y4Z8wCY7f99M3L9VpT4gwIhAPhZqkLqoWiEO0pskvrCyRP5iki8aWj8wMDdQXT1136Y"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-targeting.20180712091712_1531387285869_0.994836608696636"},"_hasShrinkwrap":false},"0.3.2-20180712092230":{"name":"@atomist/sdm","version":"0.3.2-20180712092230","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ddbc03dd5d074227a133cc07ea65f90f126ee436","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180712092230","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xN1x/ojGW29ck6MYFaCZM7eFG1wBK1pDY/t0WLmt1qb9CKd/RkB85PFvtsuEvYFHeW5ZsF+D5HN+4LemWFTrCQ==","shasum":"e1494f65d7f9d8386e911b5e24c5e7a57575ec2a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180712092230.tgz","fileCount":762,"unpackedSize":1716738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbRx60CRA9TVsSAnZWagAAbIgP/jjVE8QWCu4i3Bpnvhr2\nLLR5i29QCR1cmE/vgOK5aS15zepApr92ty4wkHfdmRy/scPy23SF3xppcVhI\nAKvLhpQDjQ2RFgyfvbok7khAhGIaLcs9LxK1e4UUhU8jfROb+jEZBSR9JimS\neofwyYiL4Mw21gc+wZjTH+30l7fOkDmRMNAdurvjlL+BXWwIrnLBCF7gm2dL\nEfCXjoIX7g7B85lLJmQSfcjw19Kitmuvm521tSvJQYCl7SB/xddSmZxxj5uu\nam0xjLhxfDGBvobkt9bDMRZnsu/Cd5FpYM8q0WGfaxTPq4PJQLx4eKKsPbiz\nckJCBt52R5cfog0bRJJWxEIzUrf1aUoooBuBARxKSlABNYg+VmkTc/XjhhGn\nYCeSi05jiwGH/DUs54rImacW9SutBwmlylIO7DBYpiWyQPnzIFiQbESR8xud\ncUe8aVxaDU/kW4msadTiSnf3XwB/x/I7icxhNmUXfU5Qj1PgmYmg8DYi+V00\ndV9upA0sNRcWnaQd3RTAt/u4g9biS6NJv+BbMmyick5XTxBLgTAW4xskJ54Y\na2imhurKg3X7Lc6oqiDFpo0KOMG5MUW5nCRXAZNXKxJ2BYgvuzfu5HZCPqxI\n0M8sE/DZ0gcaWHeCHn5JfdGtbfcE/BaRVvEL2PEBzVhd9n+xUOYPY4WCXTJ+\nHbTb\r\n=RVkL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8eZHpHLFVz4MOSiaYO7xnLOygp1olL7nsPAy9t6QGRAIhAIYIB/uWOENGvOlyfLVFdn/WbroKUws1A44K8IoyHkUM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180712092230_1531387572861_0.2162268795951796"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712161734":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712161734","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fc7f997e472f85ac5e31cc63f6a826bad17bc318","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712161734","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YdctO1L2UFFPuzid/UIY+f/J9JWSEjGTJsxkg/ta9zgf3ks/8DDGTSp5lC87BbChrfrodoj1npD0OmTEc8Whig==","shasum":"12d808f4a3890140d407d5f77166a6298ca35b54","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712161734.tgz","fileCount":766,"unpackedSize":1718429,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR3/+CRA9TVsSAnZWagAA4zEP/0u33REvCgtb6Nd75Frx\ntSdLjbAsIz8wSCHJoNvghrxkPE7elsXje9OuukAd9xpW7ZzDfQXl9gyzHcer\nEn5nRzINrI9EV3uqLg0wUiGMeT3KvqStE3PEDXXbmpvBtqHJ9o6Tis0yH3hq\nsdKWBiJ+Hm2ZudcxywWC3PD0+3r3vurMHvYqUpP0sekh71y2X5ArjRQwucg4\nUN2/vW+L1jvNTvplvLxkIS6vebgwWH7tnVriY8JphGkSG+Pww309Ih6JvT5H\nO8mAVP8q18m7TCPnG3PaslgD/vKDY3e8TC8zcNMWCvKHgmy9GKpbwyEHMbwW\nHGFoP8rJ6Mwl9iZHssRBzeFPxoI4bZjOtn5sFLXV/XUzsWbYUpGnpGFf88HV\nvpVtOToqR0A59G00mvtXS+9iTv0fLu+CivVtFbB66K44w9PYeIkwD1x7p+JU\nJj91DfxB781wYAhZqTd/u1VGFUscm3Ttk8lKTwp6NFR5cNrPJ/taoh65dfWx\nlZS/5Lh7Dngv1sVXQYMSnd7iiBFOfc0MhqNboFSTOsRuelBRYfXR2pWyzNgu\nPrO2NvbiITfsgISK/gPorg/62wD/kj7J4jbL/d9bs6n4uCxtNVrq9ieZ5FFM\nw8afsguRkhzF1kbUYYbuwZTijhZq9pgnTW0eGikhhcYOaYx2c2LQXv/SEo68\ni8DW\r\n=SMYM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGxkPshOFTa68l8NtyiiM1SG2s8vzjQpWitmy4m70UY0AiAoDpQpERCOJa7BRLQtIEyTKrAyYoJtBcNRZgvqW+m8lQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712161734_1531412478213_0.5701952446580021"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712164148":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712164148","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8d5b7c0f2cceefbde61ac8c73349bd95219d7f06","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712164148","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yN3PH3oiR2FAyh/Fn3xzurqw9mYsbmZvfu6iqXbp/zBFnW3yaTYs2FWYG/J0L4pXJsuUUPBXnntG/ZuqMrueow==","shasum":"06e2d7a79e80e36de692ca21a13a85552ef31f67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712164148.tgz","fileCount":766,"unpackedSize":1718428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR4WjCRA9TVsSAnZWagAAafIP/3K6E8qBk8pKd9qz6mfV\n+M3KbBfPwLukkBXJNzwoF6acgeTeT7EtZOkYmIIxlN5zAmjfElHJhj3e8Gfc\nutEuRLFpC+vA/Tqx8yIjTMMVQzcMt1O5mlel79p3ExdTUWrQnBgE90R9jS3t\n1DsPFsuSeaL0RJteUC7LopuV4Tr/lcvPFfazaD+aN9MME0pZWneEtiHhmtx8\naRprHMKiA7+XeFk3VyJUXzq4l4kLd0kRh51HZt9Rr8PopK16CDn7h3m29oGh\nRlpgm9hSB2+iMx/KHD9+Drsy2/GwruCrjyrbkX+QDZTNmlWkoLJ4eCom+woc\nfQe9lwJ6+jjwU0ahZOdFpqIu2v49lWpG2+NHIQTshbP2cdX/v0zRdczMrywi\njDfJftacY8j+TlgnXSXlZBrUuo6QeF/Br2VxoQ6f8CjMUE+rqYK5i4a3IWi0\nqM6/tu5K9xAZfV02ckP1zDFxpYNr4YKPvM5VOJ2QcmPUQ7S0bkU5kicr05zJ\nyMirW9SVM5XpM2D/Manc9mrZNH28uYZleigPH01/f3Jz3p1zAjSebkeqYhHN\nzfumzZi8GjQ1zrNk3cqgTI0mlO73nszwM1FI+zqU47QQ/cJP0OrWRzBdbPz9\ne8wTYY/YRWsb5NFJYg0Sw6AkUL1SxW7aCRyhGcGOu4mMyjFDiPueasSt4X73\n1Zyh\r\n=FbL1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCKlnBs+rfa9NtVzsYJ7ePSz7RPMjuBrSKRgfc2QehX2AIhANi7HSGegCxCQpTun8wiQ5oAxy5j5fZ9urqBXQa/O8wP"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712164148_1531413923244_0.601797766028074"},"_hasShrinkwrap":false},"0.3.2-20180712205800":{"name":"@atomist/sdm","version":"0.3.2-20180712205800","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ddbc03dd5d074227a133cc07ea65f90f126ee436","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180712205800","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WmPe33NT1WFOm2+U1tdXrQX4ZwMbXS4GqttGteF9gu/PGN4lY7Q154epgliKe9l7THc2g7l0pvTSB8SiY4f1xg==","shasum":"274466abbb2628ca9310851c3a052469173c69ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180712205800.tgz","fileCount":762,"unpackedSize":1716738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR8GeCRA9TVsSAnZWagAAoYsP/j4ADkKF88dQYdt44a4O\nOs46zS4/fj+gXY9zHAxyqegMHrOYm1nJtEDBQ2RboBeZJmSI5wmzFEprxJoZ\nirPglBpSuJhwHYid9fEuISHq7dqJr6P/9T69t02j5ck9h21k4A1CZIVMp9nv\n/Ra3vsmbqtcYyIbJ95YvWD5RTYBWhIGSlplwqCcjd9lOt1AHFwuN5nVzhl1U\nbEpf1Y3v6qRRutn21z58Ljz6XDMO3P72HUUfkSv6cOGwtV3q7y3LFsj5odFG\nCYxtWFWuwuxKjmikjSzX9QjNoGq5lx6hpLE3CEaQs8VIvqhzWL93fvSEmFxk\nNRXLed4h4CO6pkwNC7jDpQiNZ2mvQM1BZjuk8jZVqqQtM6mBtM+962gUfUA6\nYlFMUDzrUL5FBlx5Mr0XcUhBrPH9GtEokdeysZqcgKXf3itKx59EbJiefPCw\nMki/HuF0eO2BSivZ4yJgJDZdD4wGdX+sYEmUPnUKoe4gJinvUPiAEADK/K87\nx0IJEmuObesKYhMESIa0A2GGucfdV5c3j8R+U2Qoo+WN5hgv2m0n5jpHFfky\nSrvaQ60f2gAax/DvrC6Cl+uBs/N2c64+7lp5A4kSn1AI14zYvcN4B+y/vi05\nTzHnLyFwXAPDFQtCAfE3NXZ4Qn6l1LBLb4Y86nvTM8OmwDR5KeTq5Ni3Jd65\nXFPf\r\n=1iPV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmklUjTVeuvQPTkrwvcX9dbvYAZyRAGvWuh/IezfnAOwIgPlWaVYsnNT5xWb65sB0Ybjo21HmFNK81ODE1wg1jYpg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180712205800_1531429278679_0.4439157965962075"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712205814":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712205814","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b751f21d78623abfceaa5440b638a28ad3375f06","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712205814","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R0NPov5TUvynVAHxD5/0JWUsdzIwqGQCBtwCorQQ2YFs95UeHgeuvzWXwRFKf2JdzzXHF08Jf6s7svDpvYYnHw==","shasum":"1692b7547f3d7bd0f4a1b43374c5b88c2880e91e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712205814.tgz","fileCount":766,"unpackedSize":1718607,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR8G0CRA9TVsSAnZWagAAGtwP+QFCroSnCLmJYk6wuJ6I\nZTNqL568u4AATNGzwamOfbcg8SiG9d5xAhyXrEz1EdEJB3S1T69tJfwaB61C\npyN/cYf6gyD22kgVdcoXs6z0bZ/w+9wNk3WKZol+eL2Iovnw7KeoXC3xU24e\nJC/8F7htaHg0g/ZtADtjPoW5GCAZnVqzegH0cvZHHZQRs7WlIbYrhZ15lSHU\nCPB74UxOkTCJuvjyiO0EChLF+6uoLgBOrMLclDcMPyBigawpHkhdy5NikWxP\na6mPPpSjhWTnYEKKZJLw0iO7zj+VZsCw2S25X0AjZgQmBCBmBJYuUAbLvaSV\n46WfErbn1dgmvC8yQwwBd7ycLwQbfrmnMY5kSPqP8K3zt/1P6ZyFUzYKlzkj\nmErhmVZ8Ucr3I0y+J4BkkeQiDGEhgaseXZ3Yf9nZbxKeNeTekTB+FmzRwRuR\nG3Hb4EyIUNlXvvNDGQ8PsgaZ/gMBHYSCIxIx4SV+9TmYRB8HZv/J7ZKAUVW7\nkgyfsEIYwpHaSVYVlPsMuasxsV10xDIGQgIIXzPe0WkxUmd15jykgBh6ROnR\nk8hEkMH9g7psQQQTGidPaJrwQO34RP70u0LycGOPx34Wd86T8Z6K25cdAACw\nSWQ8XqGh1XPjNf7PTZj3lj5lBKGBQTQBBeLQV37SrQhfWgydmcTw/n/4fXCf\nSRQo\r\n=gs/U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgIRyVrI8RJ8CIaO4BDO3Y7pGYpRTr6ELEGDIsgvIpTAIhALtQwXGfz52RNebrGLBsvEh0Oc6GvWylczmveQeyJ092"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712205814_1531429299846_0.18613007996528474"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712212457":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712212457","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"96e91785537d6877dd4934fbe1d96079a4a7b614","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712212457","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-J9IF5vW7bEjChO8IazHKXtj0txtLKabUUGDZiZjwAnJ04SSnUgz5nawLzpMH4vsUToDB3G5/tpsFkgggMnIKtA==","shasum":"ff3d0f91b6b68d3c00f2d066c0c79a2a3070d4ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712212457.tgz","fileCount":766,"unpackedSize":1718584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR8f6CRA9TVsSAnZWagAAMyoQAJ9RVPcEAEGfNfdr+ETr\nfSqt/xXUC8SBbVS/2aOdHqBLuZFJ7CfgaCKzLb3alot4V5y7iK1Y4a/QiWZu\n5RKxfzDSgCQ/kJbDzZrEt4sQLsQT9ro4CRCfM3kwtJMJRHb2VViW87O5M3DN\nTB16vr2RBZhkzdhNh3A3iS2LNiVcup3q66TDCPrLfJUoPdPoA7Mvtv1jZBOk\niMRJLsWVdrr8Ubm/V/ksroRJy/fEXaGNO88SWXNhFQukn3nQA389cEp27/nt\nOg+xeEaxwsxTn8XblUKX7VCbDJFH7ziNQxl4QFDHe1QehnXCLLpPrwzoNkEq\n8FH+fwVUOd744wRPaExf3kEFm0qmn5lN/TgpHg6wP0uUUGFN2JprxYhunhFz\nmNJ4FbHZhzVoeQFewHhSMsTgc77E51Cg+JE34z36dQfmGUFa+td0mknQwejZ\nMujcRcGJTyrwWGih6Bpw+AxxGhmnafP9EVnTjJrUkpvmy3RvUy6GWKxBcRgQ\n1F3arxZtM9/SW/j2vD73Oh7pBr0Kd0RRLmfLW8tVOajUa0gwi7d2UjVdzzmR\nkARtwo1HPmVvN9fZ60tCKYtKXxFVolrDgx8wARxDNBLvFx9RciMM7vKH9rJZ\nnqDGNrJz4i7G/46IYqo4AuIZ8yT8RG7fJizbD54frxeWKX2CJp+jkHCEh0MZ\n/B8R\r\n=vviX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHk2KC40keLV+JfKuJeiRGpgatHOZS/i9wL/cNMxAFkOAiAWfkMRjZtymnaSfiTZYksuItp+cqzfVeIAQ/kfCM8YwQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712212457_1531430906778_0.6883852483420896"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712213419":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712213419","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"79bc108040efec3b4d9a229a71118f7a54915445","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712213419","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9AQXIFTYAm8ERml0sK/PniGbjlqlgFVr3HfuCsKe3wQUKx+LVjDPBFEa1MIIdNQK1i4/tnKBS4lX3XGHr3rXkg==","shasum":"b56d731631b40b8b9155436a11e44fbd60537e6c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712213419.tgz","fileCount":766,"unpackedSize":1718560,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR8ogCRA9TVsSAnZWagAAjeoQAJBWjd8uBrwUbkRwsU08\n/yJU/ejeMac1j7DFWozAC3uH5qoPqe+r/9MAGY4TBHl1wUcKaeB0WjH+G8XT\ntMW+kyC5K+RMeCRP3hoyx63iKtd7bPIVtb4N5lvvdwI/2Y6KkUKX+2PMkWpg\nuJyA62sLBEtnqXFC7q5YQaMTxmCorvO4Froi0XC1tceuEVdGOZ00fnqZbF0O\nrEzxe0YyVS5h4024UxOYKiz9GdeOgCVhRmAXOO4dwsZB8xWIzzVyRTfp/KkX\n6iqHOmVhRyQPbjmIBp4++S6JK2ssXIhcVrMLUgSN4MxX8Wb9rnM+HAP5SHYs\n6G+r2hsgGwJLgrZGN733vBPAQM0lGMyPIKb826pzdEDPd3jeSFnJPXBSLgg1\nHeV7T142S02PGgq3K8IkbcDWRpVBHtu1K4Lk8tBCNfZMkL489Yx21fwBeIQm\nYWSsH78CX6mOFV5dF/ANvXUl5f4hTVCY5AZ/ZF2Cn1O0gJPskfukHfmMjrjQ\nsF3569pv3krBtTg6MDhERl0OhRjqNFJzQmKhEi1ycEgP3kYzgvJvXLjbpbh2\ne5avdEtssbNBvszlICYT6K5omqz2UBvxWHKjLSH0ybzItyRgzQHVqKkzJKQj\nqnaLLpBGQgtk56yWB599IrLortFqk4J19O1MHOV5FGZcqMKYtFFLUAySOTMH\nn9HJ\r\n=uPQW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEtkglb1kaGb3TaH5j4OKANA9/x78fL8xJOH0rpSVGiaAiEAgd/YoYz5mXxdwPGa1uA3DtFbsO/TotB7+VXNgspueLo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712213419_1531431456476_0.5234629105478823"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712215649":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712215649","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"767e0af7a94c07951f41ce4fbd5bfa74dfd8c5b5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712215649","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KQoM3x2wZjRZ06NdjQp+X1/bhgRVYL58gdZIb508OJfTdnnktAfDLnJKvu8FOI1OeCptIbYAI6Yv120XrcI8rw==","shasum":"8bfc5aa6c746df809c615a80e80fd6e2f740601a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712215649.tgz","fileCount":766,"unpackedSize":1718840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR89oCRA9TVsSAnZWagAA3HEP+gL52cf/jkxgoTdh/DRh\nSZvEP/1LpfWlAuNpfdPXLspj5pJGwPQhtPwViNUlEC8ZuSfybQGM0MReJTfD\nDkADTUcLN8MXLujUzxOZ3b7x5vlpRfVd0DxgpWIYdGJTJ1gJOB/ih72JyCwj\nY56k5rPL/Lxd7EoBs1ljERU0D/TSx9u7Z5GmsPRiVbIEHys30yb3KaK3DBv9\n+KMvOyNn4bcigL4j49nVQ0Eexv+heS0qwZp4hXGrxepgnwc0HURKvlb2XVrv\na3zlet4eOCPrBhVNZRPsjL2NpbFcE95ClQdIHaNJsPrD/G2PbFr1QOyFpGi8\nKkzHvtUON3D1LeQ5HnwJz3kysamWLuj6ZngUI0VB1p22Ai7QQZYuTUP4lnwq\n+x7n128iO1gaEcTFVEsRBheSOa2kQxeEpXSk/BCLcn3W67pBN49zu6BJmvOR\nitWr52V9c+AFkrbpmWJt0nkErPIU6uy7Qqom6l3548GaUeU9gqFLHE34FPyJ\nu+lWih4SfvAptasXk/L6NAtRE7gsyWw47v4m6WEmCovH+efxQ649N5jvqLz/\nzc0hNzhoBZwk/L5ZJCYeaB9Vgj0l7M2QTP2KNzuZBLI7RTHb6SQaJdsAYEg9\njz0KVnFGjqRtL4CARWg8JDAP7iPHiDNlrXZ649HmPK8X2Asyda7V9iwpxLmc\nn6Mo\r\n=pLP2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC2pTN58+UQozb2aoUqm6cufX3cz1p0yxAdpbI7BP7z5AiBWAbOq4qqAV30dptK/88fdyxry5ETLt6ByvcNOIjdVog=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712215649_1531432808820_0.5644311444495429"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712224306":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712224306","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1e259302fbe5d4b5947f058cf65d96786ae60fb6","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712224306","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m/yYk0laoumvAXdJfwBGBpD5cQLM3Doa28GETSzzsF23SkW9r1tMydXkMsiP0gjsqde62xLubnlhD4rQUbnkiA==","shasum":"93899129770e748d08e09e3d2f2128e106d3f900","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712224306.tgz","fileCount":766,"unpackedSize":1722114,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR9pQCRA9TVsSAnZWagAA5+gP/jbxpvNk2N3OIbSFQtlz\n32Ev9fkK0U19ULusMPPQ6XZivYeszU6dIsSSNTG/GGCGTwrAM+beRRdYonpR\n9VFzRY4IllqXkIJ3DFzcNrPAQsqZ8PoocKzcJWoYvTvxWc5pb9+cpfYwyppP\nPL/JO25UL5x7ruxZH3ter/BFQFWSpkDn3PvxyeWtRiUv+KlhRwBpQAxADUdc\n3nsconP9PbGoOjk/dJe9gDqKt2R3sZsd9/azwU86IoA3K/dHv+Cwe1DIYTlB\n4jGhAOstEaj0yeeIEx5sw6X8th3pWDxxyIvK6JRdNurgPPYdyPs7HNxZH/Rl\nK4/HGFuVmT4t/3uql089SXXx2WlKN0UVVNWKgD27XWnpBLX6C5XvIlBpPZ7i\nCgHPamzae+KGqOtoMZFqntHNX0+ILLDMfqF2jaj/tjF40aUz8UkZu3UDPxsp\n2dIM9YbiXHh5zYuOFwS6sDjkNW/v5c5pFi9ZAgHlpAFlqdyexBNFBuBD1hc0\naESMkKWO6B6CLA662wLOKt8JUdW055pgnxz9Rt0dg7OAxwlAPAK/J1di0k/p\nWm2oEd5YeirDlOd5k9Xnqh6HMC2jAHGDvM0P6FS97/g5QnNUoEGKBin4Lf8w\nEmwHQunNcOzCJHxrO9ykSO1P0pe/pPhXyErQK7aJxZWnThImbRPEc4gXCw7P\nseQt\r\n=4XVJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAUr/5TUqT0wYzrKguRTqdEeH4VBAIXk82TwIveVqMI2AiBtndpm1+MF4rxqj7z+t+OcmXRSiHba95O+6Aqe7tdcOA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712224306_1531435600274_0.7789529162882156"},"_hasShrinkwrap":false},"0.3.2-20180712231942":{"name":"@atomist/sdm","version":"0.3.2-20180712231942","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ddbc03dd5d074227a133cc07ea65f90f126ee436","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180712231942","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-X5v75qs/no4evh0G80Zx2FewsgFlWX0joyK2HG1jvpYGLmL7aaYcopVfg2qF3WTjCd7bGWUnWAEPKWkoQGYwtQ==","shasum":"e8fa63134ea1183da21410cfaba65a96b106ac3f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180712231942.tgz","fileCount":762,"unpackedSize":1716738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR+LtCRA9TVsSAnZWagAAnMcP/3XbijD/6XANXUIAebB9\nV6ZfNBUWm3tNQKY+Rt2SMQTIheAcoZHPqH2rRu3gc4mQVv9CsTzKl9WHDroJ\ngxTuHhZ7yWCIqnka1JB1hUPTQb4kb3hON+Do0UoQ1zY/uBtiBcFBWJ75N4R4\ns00DyyDC4MLlv7dtGSd1IGU/TiXwBnwdN4MfgmVybx4T58a396fnZ5soNwIu\nAih1jQdEr4H4qbPxOGK+aBxkzyirGvEzhQv9FC4/q6HoEmRVo72PuYG6xd7Y\nbW2ieNXvVfTXdzUDMgafbTxgU562QE0tBRZY4yjVdkiP4+31c2kzLR0lRJrY\npdzS9y1rlx5AzNoOpVG69j4NoeA+/sdLZNzyhHqNL8ZRFWZAtAirUb6lz5PU\n8MiQRIxVH1kV0GznDPqI+iiKvzQ9B8NjAJDLFfPfGMahOvG02Hwj12ZKW6VJ\n5GZ4XMjHFNIt3kUaeMS+i9mpRiimlAjwFg1YRib+lnr2+KktFLKC320/dR2L\n6r/rhnibZ0ohQvEbEX077gpg2gpRIGP88mQDRwmRu7TbUo0cRk/zHQFHb5K4\nJ4iiIRhWRkrMnLYeKOpqRf++v0o3b6/zCaghhzxDHgXbDB2GRR/4LBU5Mh1V\nWszDXTHZbC/YtUERou8lpaX+pUwtnFalbOel41rN7HkSJfiSsGw95c+hNGOn\nY6eM\r\n=6z3g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGXZyG80AslN7ulwdWrpPjRIcsjgI+PfAHB2sK5EbgiKAiA2pqkU90artGI8SaP22Cj7W63Yui9WFkDtDaktCYXEAA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180712231942_1531437805203_0.07786090865064543"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712232213":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712232213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eb4475d7eac68a280fad3584b10205b6adc0be2f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712232213","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4/D6KbeF5duecqQsD1eOMz+zfkFQqM+UAIo5Eo6lsBawKl59Z6sVbq6byLXDLma9X39LbQrYUhtuiVZjec29gA==","shasum":"1eb55f10c8d33ac368b958284481ca030b20259e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712232213.tgz","fileCount":770,"unpackedSize":1724232,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR+NzCRA9TVsSAnZWagAACNEQAJOD/Py4kAQcwtkFb8aR\nuErqpHmniCGeFCFiWbglsoT1SopxdlutLocs9zBVnW4X5qJYwniUuyE1pZbp\n2WyljN+YIrnYovztKa0cX/keDks2l9yscYp1XopR5dMlDcsahPtX+2n7i+h8\n8nO+Sh7uyc01ZyorrnHgArzcJNKESiTVwvfgo6VZkTxl5xXgRBaj60xykGoI\neOVIbty0WHIuzmncI8CgSta5YYHhFHHOscKGVFBZhKtZUXEyvuFKha65YQlR\n33/JUjyB04FdhzGzvuGKdh5u1Bm9GSb2ms07rKPTddMjvNEyF8nurVr6fqmq\nJGCHTCvNUTjREwfcBhBOqCwyo0Km2S2Nnn85v1uAiwRm1zN5Cs/m2WDuOYOc\nyyTln0t9nJFE0R4Y5j3CEQuJCw6oOWXnM7fe9qvp/ZBtMV8qZI0RClBLx3RJ\nJDghVhY0m+ysk+nD1tzYssBMACiupC/0qrgfjmEBIQ8cYM0Km/bG8U1qyr2M\nLG3Kd9giwjsy/fveYksBY+6WmG9ET00cnLYsNl3S+94h4YKX+x+HgJb+2Bvf\n91B7RFZXKcYXXjdNXfpHvYj4zD/sBTNVMsRvGF19ezeDjBJj0O+Gflf+DXlu\nWxStmap5JB+cD9XiP2xcjbrSwAm2gfve7LZ7XApjRgd8hX4jQWlX8Lgjkl5G\nkI+U\r\n=+2gM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYc4WSnUNQ9LehuCaVv7ffTfAOC5DM8BTF8Ufvo/PSMAIgOCdFWCXVe6DfbjbGuwGxaETcR34hvSRC6O9hu+RVJS0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712232213_1531437938997_0.5348967813576004"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180712232736":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180712232736","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"46f1590d393709b1fe45dc7b2039f71c774d7998","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180712232736","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7mt1ASqg5kDLUVWV7xs4gpiPMUu6DKoslAvJFWTsCGOlwWsSNqEQfYNvV2bV7dE2wr9VQ2TNLuYAq6k3evfiGQ==","shasum":"e5b5a62df1d4fe64a6c46df624b8c4b856b8d8d8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180712232736.tgz","fileCount":770,"unpackedSize":1724233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbR+S7CRA9TVsSAnZWagAA+/gP/2QEGcgc7DqA8ZhGPgA9\n/zaNXVsBSHB5yf9F3rvlv6+aJaH//Uo1z/y39SIY3zLYEV1YglS5nnQta/gd\ngY3TXjR07ljqKopWAr6wrIFFa9zeYLN1kk+/UjSU5HIGpj3wnQVsiNYXWZ5T\nN8OrHw3nqUchQFCQpFIusLBxWwtshuJi02P8btnCi6Q6X8Ad3jL3w+/3OHB3\nczctVU3iSP43zt2x+cVUklflYAW5IitOBXSCNJ6z7uV5k+cMAkSVPflykWFW\n9fUrMRd00N2BL57TJv7S8umI5pW99wcEk1Awl3hTOWlw2Cnx5e5/ZhYo8Ble\nFDNT5PZfjLvfjB3vA+xel9B30HF68CIjuKa7W7OEbsRQSG6G/Uw2ON30IJPi\nVlyBMv8WrdG5NoKff0hOeVmcrhbDDNZGzsUC1OzPlLgxzW9IWvQAEywvNUHX\nFXQN4nraSi23Qh1PhZem5nkgAascm0TZjUB4ref5EcF2AKrI2bEl6rW9POJP\nIA893PVcIA3RIc+V/r+qeLDr2JEuELori6jQuqrv0kt1VrGFoawHaZlkNkFH\nmJEzcqF7M2U65GBSL90KdUSL3U2GksJFdw8Gyni01n7Y8sysAHGaeWxO5p5T\nxweK5Wl2s5T2xxaf5SAK4u98tcdfrH+M9lSYh16TyGWg7bED8TiTSMh9Ovys\n+Nhx\r\n=l+C9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH++T/yFt0FzzdmA2OAdKVZQIYUYcF9pJ6spjekkP1P6AiEA+pES4uPNfDvJrXUCFKSZZMaKvW+GKDlQM5d0DsGDzF0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180712232736_1531438267842_0.060315679586762094"},"_hasShrinkwrap":false},"0.3.2-rename-listener.20180713021926":{"name":"@atomist/sdm","version":"0.3.2-rename-listener.20180713021926","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"969885289d8f1dff406201f6dd3184aa7a57b1f9","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-rename-listener.20180713021926","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-t/G/vW0diXidBrwdcfVsorxdiBT+Q7k76r3z3B3MkiNeraUaTAWvWQYtMruYUzsCk/9dHY5Vcqw9t/6YZ+Sy6Q==","shasum":"b8ae8f2042f82ac28029c8445412080786a09418","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-rename-listener.20180713021926.tgz","fileCount":762,"unpackedSize":1717811,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSAzyCRA9TVsSAnZWagAAd9MQAI572AFRil19ZAp4vuFL\nCjU9VE0zZRTkkW8BTWKDTHe/+lvM/GDMR6S3vNWFqlYAv1qYbluOYEkK7VXH\nc3qBj0saEzHFvAw+IcNCNW4ZQtPbP1ahTbE+o8ta2wos81OSYU9qMpw+LdY/\nFeVlVVgHwNpgNQ1Bpa3oniqOpyaw+xOHtmwEL5LNQAkos6VwmyHagcC26/+6\nshYLFZguLC08fMBIGqYc9lcurUHtHBKXYza4ZBoVT16kJG2dUAk9oZJdzYuA\nqpA/koZJiDCGkhx/MnxhwKI3Fw34Z8J8GtGZN+uAt06Iqa9oZE4sq6RD7JNl\n3FmfbKZ9OHJv+Bidjo5j83EMfH31c+yJD+N0Rce+mSroj+awviKKYnJtIRPg\n2ULyn4Y2x1fkG/AmTUEwtmzWZVnez1VVLGk1Bj1R2+IASpPN8DteHQGlu0HO\nmEKj8ZdSoQ95FP9uGpnsW9UHYKTOMj2n1Nk7ZDHNxnsBhW66nQcd9NJx0z0Y\ncX2qGya77Jh+gyRRap37aQ/jmiTIkI+IQSBJopBPV/mFnQjLKr7xJpGl1L5t\nofw9LWviiw9DQFyhrqhRvbM+mdcQ8TGhclxY4Pw5szhIePg8NstF5T2N7Zqp\ngsCGYbr5OmTdbIszvwdKCGX48764kBOn30W4g65HbW1xL96vDvZ9IY54E2ys\n/NRK\r\n=qs6t\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBiJfuAdgtAELoAor9DO5R0dCQjPkx++XqJ+VJ4Eik/qAiBKDo/38QUvDDnduPSeAnK7KLwGh6QNrsSFgHZRNGYjCA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-rename-listener.20180713021926_1531448562050_0.06711200770991299"},"_hasShrinkwrap":false},"0.3.2-20180713054022":{"name":"@atomist/sdm","version":"0.3.2-20180713054022","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4ec7d8152d7ae59d19b2ef867c1a3caf1c6e03c1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180713054022","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7usco9P1DAu7InkGy13lrgmS/9UQ2DJN9kLGzgrWuKLvb8fh8J2Pgiok7P2CMcNSiFW/pXNk7LFhiuiwvMggpg==","shasum":"da47d4b2ee78cb0ca86cb111ba2351a93377630c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180713054022.tgz","fileCount":762,"unpackedSize":1717795,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSDwVCRA9TVsSAnZWagAARVAP/3d65yY3F3u9I64yfBCk\nvgUG9sl+n7HbDm+bmuOyIYnPT1Y0a9uOp/u5Ed0uYA1DT7ZrFdO/grQGe4Io\nnLGzX0y7sqaFOwH/xnMt5fGOkv2blSzXj+inKnQzGrCUNL0BOwdci4UxghUy\nsyXr3WMM46vcEhj/rIcmKRXtkJ8Cqani+P3vPS6OdrpfcRVuMQNU08b9HpIf\nPK9vgOavS4N0RE9f3pHhmaxK0BNjVCLCiTQ70bAzjWc9yxdx/gl3lwD8ij2u\nU4jfe4SKmtsuyjfivE7NJOu9hWoCO5ISxastVtBVra7TGfUuEojCpD1xc8sm\niZ734C4fWemEeNd1Wem/Qe0nJMutMggvbbR/JidvfMScjQXmxtlWkthr15O3\n/Jr3QAkc198ui2KYvs4gMOw4MdFTIyvnG/sKe2yFW6RPZbEpJyFyWTILfGet\nSJtdB0Zr7UWfZDJjWE5kaCSwPqoGpk+apCB5kKHacsVTT+jL3DYsI8GsTVJv\n0/TM9aKWEHG60jE8fFoqo0tna2agzaq+ZeBnFctn9/gkL6xTPfPCVL+K95fd\nCWN459M/SQ3eycaoBxrsnZDWJBP1b/wBZ6KsBdFlVFEmSvfREyhby5BXolHX\n0O0KLdoF37fejtTDqZH/Hu4EvIlEBWLo3ciBoaZC4vCNqC5xQGbuMIIqWe88\nqaKc\r\n=Q7fy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBv8JdMerRiwnsTrrn6EZAG3+DLf57E/POv9SKjywCRcAiATWsrEDZr8j/KAv6FjH6oZtFsl6WEfjDKxcx9gLW4rBA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180713054022_1531460628962_0.030175071710434098"},"_hasShrinkwrap":false},"0.3.2-command2.20180713065152":{"name":"@atomist/sdm","version":"0.3.2-command2.20180713065152","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"10e9aae82b01d662109a31db4d8695452dcfd022","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-command2.20180713065152","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EMA4WARgkiMC6e5q1g2zOyfwGykwbQbXJ4kZENyvsRrBAPJiNLVWy07cNMIgEhmj5Vt+SVwYHwwrDQNBs4TFnw==","shasum":"62ea6d100314cf2e6a7c9449316349a932c60d81","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-command2.20180713065152.tgz","fileCount":762,"unpackedSize":1717860,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSEzLCRA9TVsSAnZWagAAzP8P/Az335VJeGviMqdj9+/k\n9365Jq69YSKUiWUXlHtd1wigY+Sey6CVKqH/ivRQhvQ6V/A1wfEXw4kqwaqa\nEPl0lQYT0K37B0KhveCmQiaLl10s91kqmx2ua2dE+qV4cW+Ny4nb5HftIDjm\nB47N5k59iLKswJDscbVWQt/SmJJWjRvTSEXlhEOFg0ny/cppLgwVonXNKVdi\nJpm9rltbvn5jX/DFnNs3NZOvaKdIoGbRGiQOGtIV8yTEi0enz5F+i1xYINbN\nwoKYtJ4hkdwjczA6NElP9Wfk1MAC2xPzcBSazX2UWHhW7uGuxjZrUogkl9lq\n0p+2mQxw9dl70lJS/mWA4pGvR+yfKuTQtJ1aKUY8zbRYDx2fmYEAKl5yjH+t\nZWbhlFLB/mHKSGA/5GNfmwoTdgF8pYKszdz8mV+kvA/vrKMy66IcsuW3vxlr\nuU1d38e/M+C/54xlS2tRtOqk5VZM1F/gw9O581pMAKSK58QmI0kh6Ndr+los\ncn1wfB8SGbVnHEGpCNqhhJtCLhb5iqkAjSZb8sNAtha5bDeMqXkAMaFrglkA\nTdhAa+hTxnwvU+LJ7bLYME5KA2YY0FUUXb1JivgKJs3eo4dDM7ygPRMD4B3t\nGbdh4Ffu+XTU1tQYTPfOPyYtNFITqAj0rZRO9YCBE7lzpzmBATzDxYULXmSR\n/336\r\n=nD5W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC7NyM2Hh21IgiUJZzdAn1kcUt92DKVyGpqjGD+/KN5pAIgRsFjncD3nJAr0hp2xqPmgFDIUn8DzVQ5DHOugR6tWz4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-command2.20180713065152_1531464907621_0.9852827043927843"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180713074226":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180713074226","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6eddcc46fe7ab077b3dd5df0afb8ba87dfa91089","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180713074226","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rnTy0QtyMFybAJwiAcS94dl+B7kt59CSsLxFEiRTo+w2Je3btTfmEp8Ykb0XFJ2AYVt4bVja48+L4d30S3Wwcg==","shasum":"ec0f0d9e3fad783f70f86f03e1aac33544a37981","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180713074226.tgz","fileCount":770,"unpackedSize":1724950,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSFi2CRA9TVsSAnZWagAAKnMP/RwMYirf53+hv5R1aaHR\nkie/MJU0eSiM44hYm+HoZVoxpLOOaeChAK0By3Bk8Nm6Xmj+EeHJKR12TZ3o\ntaocHUj/2EJy5gGBj2HdUA8WZKHLvLyPi1gX3Tk0sPjSKwQEyp3FftFhsJwY\nbz1OTVgYgmA0CnGtf06Tb3LxksiqokurRreE6LLt+LN4x7XCnuuEUvwAFMIV\nyIFSAJIwlJ8BLfeTzuPHSdnFkXNVOIHrPdb2sYxBOX8bx8LFoM1IYUkrgYG7\nTFmX52r56DeEAselaTuKDxMcmZJJ2oRhOhtMSp9NHA0Wy2kT8gtVJzD09uDb\nBMa33pxFzrbdnYXQQUM6DHTB3WBzxqFNaBO4cq6wb+Yy4vB+Th7yVg9RlyFr\n1S3L8aTb2MIFy0EjRG6uAwYgtc8Lw34m2+WLL/kEA7Xezaen3lXs3v2jgMOe\nSQkYOp78fM9CN93vo4Ww2W9hExYj232lQl0vv1xFxNF/L65oPex/bnFt6h9d\n0J/pVi7suDD0JhzKoaQTGOcmG6RHt/ukxeg6Iuot/0qy9qSndLVzhDa/Fu00\nM9AScOxLqlnAHeNkjfgvnUq+BeAg2TZ08o2iyrkEKZsglUpxU4duvyP6kVpX\n1fvaqTSyjs+Dvzn66BU/scnHdkDdwQteJajuZIZPVllwXH7q801FPSdc7Ep7\nowjl\r\n=Sl6Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQE2oMGaOdrW0d6xFyvfs75X3dKiimbEebYPemtkaOgQIhAK3DM6hrMXuQH8ByhvqJUJ0/wYfu5S3E2E8nWKOk+B0H"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180713074226_1531467958227_0.14556230927598568"},"_hasShrinkwrap":false},"0.3.2-command2.20180713074528":{"name":"@atomist/sdm","version":"0.3.2-command2.20180713074528","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bc46cd99bf7b8ea5256e33fcfa2a561d5acb4da1","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-command2.20180713074528","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ff4rQZLGB0snX9mcEOde656vPBaKkFkHPTG60ICLsCd8JOFxsvK9lWauT3u5Ki19iLTi5X6M+p+D2wcpmowBCA==","shasum":"a1e1512e945724a4555181cd5adc13c17f9b81b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-command2.20180713074528.tgz","fileCount":762,"unpackedSize":1717954,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSFlzCRA9TVsSAnZWagAAPSYQAKGxzQHURDmGBujmjMf9\ni65Jn6Y8w3u6ENh/SrVpD8/50814ESrBgfYrP9kHD47yAaQkIA28mJDcD1jC\nsF2TBVm9/RPdXuy+2KeypbrDLkInzAyYRvE5eZD/+ZyP/EP6bQhBZaWc24Cf\n8VS7yAFk7AijzGIpSfLkH+KWeBelBdkfUfFtH8WL+fIUo16CTvITyC7s5XBW\nXcn/FSP+Xi4Z66uYoe63C7n2aJd9WQu1xDFEXzOiZbUztcBP76EGok8lBMsI\np+R6pRGIfnrS1Cd2mwBif3E9+odBUsS0CurYezb+rIfmioXN5ttL3l0eb7nD\nIcc/h05Uil1+kmuKRQKGhZ3YQKHoZh0VEtgqFeylUpgULCrigR2fcETLrhgt\nbPteUtR90vRrgWZu3uV9nTvTSUlIX+zsmNHIFumqyiAz3CupoJ+7X9VU3kOY\nRltvX1zgr2KTHRWuvwGJJYiMnBLujm+vteNCBn4yP2bCyLyG7K5AkoEDY6dt\ndhfn7r4ss4I+kbm/DKzYuYc+EBU69afUXZUI18++4vatQ+18vVJWUanp5vnG\n0kb4ZXQIrunYm5TVIVoJQFgVH01aJsp8c5U8UdeNYRJL2hAiPZMH5UnDhQRg\nVuWTd9844KVWjl1KV6/rjf0Q+OigaltWH/IxfrUAX57/J/rBQq1YPy+VbAGh\nEbov\r\n=Fe02\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGNn7rBfGzwKEqIZJh+MuwQJpdPaNK+/mH8ZjdHZaomAIhANTAFUTCfJfqo065sWJh4CXv05jO7shPlBxiyqRm+lrj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-command2.20180713074528_1531468146953_0.6210497356123317"},"_hasShrinkwrap":false},"0.3.2-20180713075016":{"name":"@atomist/sdm","version":"0.3.2-20180713075016","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b98d3d032150caf59595e7a2f0fa58afef090b06","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180713075016","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tAftiL1xTEN69hFluEy2XkIge+LCV3KdJipXZuoaPjROWyj2lb2MeAcpnsTKdI8M3OjN57h1K0hSffNk9wW5Jw==","shasum":"0e00cbcfacd47c81cc879ae363738bd49d8926af","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180713075016.tgz","fileCount":762,"unpackedSize":1717945,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSFqTCRA9TVsSAnZWagAArdMP/jIdTgx59r0SrqAPjk+S\nuClXz4YvUhKWNlgRx3Lsf8YsjDQQbqnZz7okSyEMgxzkqnbKkheubYodIkJS\n8b7vQdyrytXA7kdoRcTfLXUHvHBahwjYBFmExmdX5D19aGtpraigjybCgSIq\nGYemhqRj2IGBCnmST9UGtBfGpDy+CAMpz9ptMqj675aOieuIfl89xg0Ba3mZ\nn7MN/ehn2d5yVeliGWKsfLfIlUJLOQcROEUAvqE7CBNTqajHKGCL/O3Wj1g8\npp5ij2OjPjzGZ6FrkYQ2dABcrrjC9wGUcV+UgRW393MHqsQmlE6qJyC+t0KB\n+8jFZk2zBVFXrXJ0JdL6afxBFhT3huml6nbziPryYCQT7sn2C9/lSd0I2yfn\neKqWQLO6thyWh46wgqPVcGUl/ryRYwT7BO/UJQ7WwmR0DWG5lBcuT1pB9MJ9\nIpYLi7gr/oGi0itYn6FHvUYIDBYVKQ46MQDDhxXVAVNGVSDuAowSmbQCZ8mc\n+And5EUDp7ABsTn4GXWs9TU+J76/10UL7VQ91C2D4qkTTIKG+fFHwG09hgZ1\nkLyEX+rNnTp0Zjmzs0HXCy6/7JJBL07Z3TjyFnch6Hzwl11lH82ZTvFjiPiK\nLgNC3ALcmrmTdFAobrIyV6B/29L2XdVWyNYcPspXe1k/M/oDgDOHg5Q/BEcY\nnUwd\r\n=pCyx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYPRyZTUFBp5rjDdADbd6gQsCruLxILGJWLb602hJK+gIgaPFlaKIcgtkARrVmZzfzaGNXGn9zrsBf17TBKLvHa0k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180713075016_1531468435408_0.2092023981137492"},"_hasShrinkwrap":false},"0.3.2-20180713081422":{"name":"@atomist/sdm","version":"0.3.2-20180713081422","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1c2f258de1d4c8525773c1784a3b0ce7911616db","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180713081422","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IGzkKkjMWEX/AC9g4hMsdti+l7jpC2yEbYOz6USZ6ZMD6g/icPfDo8BMxe4Myj8HfVPOz8KcDy/bSpy8IpJZFA==","shasum":"2e29ad36701ba0b06d8b8690453cca1af6a38674","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180713081422.tgz","fileCount":762,"unpackedSize":1717240,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSGA5CRA9TVsSAnZWagAAf/QP/ib/WKHeloqY7ikdyVqp\nMT8stWbgld/xkTiSKoN/eB/XR2u3GJ4pNNcEywfKEut6MCxzr19vgasW1YtI\nYjxKPF1nth0dfg/uJOQVTT2tqOiQwbtG2ygyWNUdCZMtxxte0/AA1g9ZStO8\nwpadNIjzpddgQYnfOd4jg6x4VbARu8lWu70D2ZAKVvS8z9+e8vEVgz6WqrKm\nJs/R0NhkE8Wg6bBP2Qj0EW482l7VCoREfVQUGOh22YjyuLhMVGSlHIKBFIS6\nm//Z21XF7nRvJQAZmPuYqvdXfTFStJjgY6Hib0OO1vPdA5TPwgxmaJcQVf4T\nDJdR/Iun6VoVbe8DY94xEaPmxi0pV8VFb54tzTHv9Kq78KK0bmS3/FEBluZl\nzcuvAQlT1b+yxrhm0dePZs3rGUcNzpW5LALiftgMtMhBMZg/iD66nZ94PkNT\nLZhZKD2I+q6EnjsESa6Az6uyI901z7H7ajRWQCNrF7Nb9v2cIWjGUBwZAnui\n6vU2t3WH/jZA/eZs2rbEVuhce1m3YrwwY4QRxtSw8DsUY6dOCADAImh2d2ZB\ncx/kOo2fakvH4JiVsA6jz33Jd9CZMqlG622vwBi2maSOIwfS2dzfpH7FYd65\nVnhTPhzCNCRRac1Y/UXpfe59okXqOuKe9m1aDZzYJSHvVJKw4Whctbi5RO6p\nsYXd\r\n=QM1A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCquMee8onwwq2zpmuzCgj42YinhIEGqMHeMt5gTdjp6wIhAITkY6iS6sXwZ6LbJD4dxRXRTd7cVvnb9LwQJBlbKr1i"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180713081422_1531469881337_0.6063144288790681"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180713165947":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180713165947","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c20ae4037f02b5fd5b292fc01ae81b2a1ee25e24","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180713165947","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HhJ7iq3g0b1gK9kYzTdspH3ujW/iApiOqQ4BIcKoYptBi3er16GkHG3LD8oZGRoEOGaKTcRcK7gN4D+mBC9Niw==","shasum":"105789bfe34d27cf8dddc01f9b3ee6a53e2a309e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180713165947.tgz","fileCount":770,"unpackedSize":1725017,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSNtSCRA9TVsSAnZWagAAAFwP/jjQO83sG07su8A+pbwi\n7z8dIF2UrnooH7gPrvCZOco47ULHftLwCdJwnw/mTca7l49JgUoIiIaSUhUa\nF24Cc/IIO6e7LJvKGN2giZyu2l+QVG2/J82VLFLfxG52eprEPmCf+qQuRxTp\nVP0FDIxY5Xi4+HCc0lib4LA8evak4IudVLW0IDOfjScfhClgTQ1YkKF0qvaD\nUPyNyMrzKifKzrBlS/cZNS9oTKhIHKad84ScrvAeU5ZnS8PRv4UjP5eA8hat\nbArrWiddETpdi3BLjOZWNQofiNRnRCm5u183rIAt9MUfp5PPeZ6QQmWYwt7Y\nxawvB+iLypkHmfaux5dKn0SDpgCt+bJ3Lxqj8whpF9fOUPQXnnHHqUyZqVwH\njvNhBHeQC1y8JlLwJ2mCdnY0xiUY3NtPjj6r//wHx43XN5+gGoTOXNUkTDjk\nbSfMpwL78JRv+vUdwmJzXjzKMNQoHfkVL5Ofs3k9UVLZAB5XtSgna4+aKhXT\nSblFSBD+snxqtCvTR7EwniE18lVcOwZi4dbpGs6qa8A9Jxqm35GrTLvXgCMu\nREMeqvJ45MsjWAhVjFUSuy8YVg5kIkYDVRIQWoQUlNBVZ7g4RYnAeIwiYp9k\nmtfjGeB7R7RdWcH36/6b51yii9gEyM6zgm7JyYJ5i+DIlxz3Nca2QyfzqV/e\nMjDW\r\n=ueEL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmep0lvTUUgLjr65ksWKDz5CAtBPgXmCAabFu5ytFETAIgETQnR28bvwic+61qqMR9HGur26FrAdymIGbu+2JRYfY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180713165947_1531501393969_0.2713746617923607"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715094910":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715094910","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"aaf6af50d10ac9c16c49060b6fb77e9e4f320e44","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715094910","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-waFxqFHYS2B4GplCQ/UXOmnSxMKfeaPyVZymP1FMSZBMaI2KSwMuVI0ZWR79lQzGBDaW0h6zeW1jwQK2Ceb5oQ==","shasum":"d7df9c6864c5a42a915422d79dc1e45614d96fda","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715094910.tgz","fileCount":770,"unpackedSize":1726044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSxmQCRA9TVsSAnZWagAAdpwQAKJxc0O4Fa76Y0LJ3EhX\njIVkMJ7YQcWvFtjvWB5GvxFlwPD96kdTrghiTV38vJxwaisBSaYg9IAinJru\n8/JUd8Ks9jvp82ABsHAg5Cj/AO6kPlm6yigTBG4HWCYQySTvtEbN+KcTD1vK\n3UO/X/4KX1tWpx01Cr0mOT/jSnO7GZH5WjQI/tCWg3TCKEOTcl7yLW2hgvyt\nyJnCzZrZ9Bbw6KHLGLbO27/pRjbrhTZvghfyrcWlabvtsC5t76wpxBfJ3tFt\neA+3ofd3eIp6/gj1EWdsWQ4hweRjzmLblSITd/5xT0Jlua9aLCgLyiQUcG4+\nUFrhhBkYXTeYBEC74u4F6Z7QJmA67C8kBy4JcjME/YLSmHaZUPEguHnLTEEc\nOPyMO5BN8iSk7gfN3VQIEbTv8LaJZGxYphGfVkyXgGFWmslpOI0xm1ClPim/\nmw4so8qoEc0db/lOMjNXugfnBuqXymm+6qGBZWcQ5XWq9FMoabKWyCZ/itl7\nncR9i6yvZroJgT4YKGPFRStJsnpmm8JhfsMq0mlnJJHEqcP3XaqXjZ0+Sg3C\nrh4QRI5+/FY3rsdsDuEroiOrdfnBVo1Mo6WJBUU6ervjfuZlQTeFRf/KOe5X\nGKXv0DrPxJ2q43i9BCgpW9o4YY68Ymn2g4gIWk8FMKMaa1+25SBkQ39LILHB\n3uNf\r\n=nsMU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjlkRwyhmTiY8e7kz6ZTwV2CFetDHYpgIP5iqdwsPRGQIgKs/ybenKKklTWyhEZu8jghGdHO1lIhes5V73DawVQJo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715094910_1531648400817_0.839201992412355"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715095014":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715095014","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"63752ad45cbbc34c7d2250b56076ad1a026538ca","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715095014","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZqXY2sw9wvpPR1IR+0lQP0MkmhK8b7bbx+CVTP8CjsenRsgKAJDInOPjCc6bWlVEESnF//Miggt+8zLpcisssA==","shasum":"a34e8f055450f431582af4d59fa94719f7d9a1e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715095014.tgz","fileCount":770,"unpackedSize":1726044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSxnUCRA9TVsSAnZWagAACaMP/A0ucEGmFXbqK9HUfV65\nwL1yPp5vwQFDLb4NNzg4Cnpa35U2nNkJ6ieUm9aTFGU7rznr7vbejl8K2X9Y\nGTGz3R6bi66YMR5lGoGrNDtaWk+PMFbNXSeVYU+0+fQ/ZsVbklQfAl6ypqky\n9rwg5aN+psCsGcTu/wqE2xofrevmcxxyaHGe6qFy2lz6gNMIMw/rliDqUDdb\nzSR+LDLh8ODXo8943mud18tJDuAcR79J7lwObkwfW2Mc8pVqOr4ZHp8dpRbz\n7hOQb+HU5XLBPsovjnLWdd20bWomh7Dr4SZeC6p7iEAsmYoM8TL37Di9HXuu\nYGvqfnISr3xUeZvXXMSXFH+6+/slSYN4qEGqu13+MgxIb4lQC6+BOCLmJOIG\nOVW5tTxjyPoFdtdlsRMRAbRy2OlFGTaKb9YWd2KGKDPPwaK4OJFoE0F4ovuy\nBu0T1Dlavnxvwz0jEGoE5JvY5u6s63K1ompTtgQj8HC75caqZ9BldicacRyQ\ngOQVQ80+b9vmjCtH9Yw6JBsy9UiQtKbBSayNa0ZXRJtT8/kQNWOHXKod9egL\ntyXbdq9D9YyZ+gf8YQty6rppX+7VI48cGs7woqEHrTleAvOntJvSLRjZrgOc\nM5XxwemoOzELMr2OiFXQWRP/j+dr6mbvk+v9EERYsBH08GPN8S8CkQpUPKzw\nj/B8\r\n=Ne6H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDJFwqrlTMSMmxtyV0VHOJ7hs9MiQX6uioNn8M8LCDBmAiAFI24aJ87JdCdOOZ1raaEKnHSC6wTMtUmEo6X5N3/TXQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715095014_1531648468763_0.11086848769775459"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715095023":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715095023","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"63752ad45cbbc34c7d2250b56076ad1a026538ca","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715095023","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zfRwhos4/0w+yebRM1sJqWBQx8lTd6GOWTiAFr3suneGrP/nmjG4llMx3UKZeSAG2yqKKumXHrKTwP+ElK65hg==","shasum":"3404486dde34af82511254bb72891fbce41a371b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715095023.tgz","fileCount":770,"unpackedSize":1726044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSxniCRA9TVsSAnZWagAAdDAP/3NQgZmbnaHnnXaeF9o7\nnV35NLS8KYEH9JZf5lCWqWTKMUmT8wVwDdM/v+eD6QrZqc6/N8llgKY/mz+p\nmyZ/jBmJaRvfPt2Sy3iJP2mFWi9aACvOaER9iMAXf1C6Mw6ZEzwwWFcDVK4e\n+HzT9rSGFvjCKH9ffhhZ+B/ruksFEQCN/iTvnf94eFZmLygaTQaDV/Xjp7gB\nLYiumt4PcohlksYQ2TSqlnV+ZHJhNPvxLqaQu+5aNMJutIH9WkdqRm+BDQcL\nFyOqd2m/34vP+6xisKwkxTz7w8Zarf4Lx4l0IXS4g69/n74IJ64ys7ql1Run\nwbsN8YiQhlhs9jLypuiI++DmWl6bhg3aMHagjVLbTzdPV8RKl/LNZeNHqiJN\n8op14hpwcTcVgm2qlL7nzXOKqtzP7yyxA8rigudjPY7iFntxJyA8ktk/qLi3\nwiqRHPzl2Qz48MXgC9Ab5uKFgDV6ic6AibWYmilbjkvYwIqnXVFwo+8Ny+qM\nwqJTkiLg0+HusCsGpNYdYjQNkjN6hkRv83Ok3Uvw9h08eiA3gaJGPNWNaPbn\nYb54Pa6/Va67olh16GNuA/gOTd4g85l0ILHpjEIckF+dzMJ1ArD3nln3yIQb\npQ2jLPrslgpF4fp+CFpNa73QEXs3zFNtYM46+3rFRyH47m/d1ZNW6hnDMuje\nrYIE\r\n=c3p4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQDBV0lPY6uPkRzul6MwZ4M6M7jgQ1DMyjdAw19d1d0bggIfGrPNz5MwCQyCc7oHEe80mLUSq1EWB41TdUn1GT9+5w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715095023_1531648482065_0.4570701375393673"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715101132":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715101132","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"899c2f54f83fb78057ef1cc9b62c8d62120d8d1a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715101132","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-72nRy3tq7ZgTyO0w//XAevCgYwoRefCgWVsjchFNeY28I8U3759LmahG/to5cO8D9veEvw/8Hsdx/xK2oHjV4g==","shasum":"83c864eeec83f829f07f1b6d6aaea13729368973","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715101132.tgz","fileCount":770,"unpackedSize":1726650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSx6dCRA9TVsSAnZWagAAHLYQAJG4I+c9PmQal+aXZKaA\nckbx+Tr/2SHUUiXmDTIGdmhTFLAJnMStbVRABQv0+B94cSIZRIuCpKoHT0Jk\nwax9LCggtIOocVEIVDWA+9L9Vh9ld3+8kPKCZf0eySKac8IKgRcDcowVzWWx\nT3r1osyB4ffavDVNYTk0x+UvIfLrvhzmnIcKc1kO1MXjM4MpFg8Be+PaOdEK\nNY1PJy3vbsr2A0fPtHgOVE+DMHeBTzcEkWev3yifnO+GyolE0FlJHKd8gUTD\n8fWC68k+PCi38loipr8KPK6jgdbodvROdMYnPKLLM2U3hs1+nX7wE1RpYrz+\nmmeU9FMvE+7rCE2/7VaQI8UAXBgOxfk+9jRnKnx4m8KXNM7PHbUOsks2v1wD\n54WzzrvjOeG+EY1tjX5haNxcrO8Alcth/c7M/X+VF8z24K2TPLfPJtpYeYG1\n5eTLuKYgn9xcRFfJ6aO8B6AsWB1rqSY433SxU33Vbqs+3yiMOOhW21XL4kbK\neQapIs+TANW4r1hpjDt0beo+Rabt7espgB03QAu4IrNYRwDb12f/NA688grF\nJXQzt3pxz4i5f5Lr3y1eAJFzpXoUFOUyBByAoOAGGnjhMqiy7ay5Rq00eaVf\nN4AlRAmfIBH59fgArj1zJscYVzL0mLUWKKMAsDAtKIgRbAnFYKlB4LNa/mu4\nb/5h\r\n=U1lo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4Soto3nVLlxB7OLoM+nzuJeeWnGMTaoBTYpTR69WU9gIhAIYEekQzxfARhbQtzORVIj6K23fE4SZsMFp3eZHmwciu"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715101132_1531649693208_0.2940872800786076"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715101233":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715101233","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d7d4aaeb3193e73e9583fe7122b5bdcde6b81a7b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715101233","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T0xLNdH6YlI6FtnI/htE0rMnKF2KQHs09xnjX94MtDkIISfUWA26WUdh0Q4AGvLobn681juj1jqtt9NhvlwiVA==","shasum":"f29298126644eb1609056e94535e07bf14a5e25c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715101233.tgz","fileCount":770,"unpackedSize":1726546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSx7YCRA9TVsSAnZWagAAfm4P/1JgZZ6RKpyukPCWot3X\nSm1SEPBWtT5g8agd38htWX2W2LS7spHJUEf+bCmb9PXjO/IiwH10QjMwhL/X\nNGU0mrMp4raPM7OehxnrTsiSTld/8Bn00Czvt9KghQvnLUrQpm9bqqFt6MHi\nayZRjSiZYX9cTdzS2HEkKHMXt0DHs0oQt253+Sw9g+CvoXbZ3/9bPA5zSyuI\nJlr3vf/uDYZnh4ARUa7yl6m4hucaDy+eJucK6SH5aUZ6y//CMxzbq9tROWIG\nMdRMm/Q1RwnBhXh3ORx35Rq5YEAYXiCVTmTwAjbm56G7ipvNw6kuFLPSvYDk\nMQEk4kIEJjGXJ/azFwX3CrxBXEmuKAplAIMjCJx6lhI/Os07oiMbPEblGM5+\nM7+2EMYDADiiGJr3v4JdBWt9ABzRJZReV8v25wbKz5X/kXJxhgSIu65HVr7q\n5WImE6+ihMcbaOHXvM1KX9jAv+E4L67+E95tY7ELAjLWTdyAdGSWkrFI9F0I\n1mAiS36FCO41aC3/Zayp4d58h5RCVQ2Vz9jOfGoQvu7DZlVnbXZ7HJnFtG7l\n0sv5g97suRpaBDD7OaeffoVPM6yYf2yN/XOlc2RZza5HIpL4HH2NZi1p7SZi\nY/kJugRN8NsUiSIyAC/X6fQOzhQp1HgxNXT4tZzJUKv3PsLmUoPoX3BXgGnv\n5/kd\r\n=62gW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmwBVryEFyW7v8c5Uu4eg84hSw421VJxUKrS6PAEQA5AIhAOGnXP0iZonhMueMP/HQf35JV9ILL3XozOuEhrXqfGg8"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715101233_1531649752542_0.5531766381737477"},"_hasShrinkwrap":false},"0.3.2-progress-report.20180715101232":{"name":"@atomist/sdm","version":"0.3.2-progress-report.20180715101232","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d7d4aaeb3193e73e9583fe7122b5bdcde6b81a7b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-progress-report.20180715101232","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5hmEAV+tX33niF2ysF2T5107Sfo+k9m3pcKPTsjUXfGhMXJ6K6uDpeSC4aCdEdbgy9S/yxk6K3zhzXUhYgb+gw==","shasum":"45b3f1e8475cbe12a27b3208b71bfb352859eb37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-progress-report.20180715101232.tgz","fileCount":770,"unpackedSize":1726546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbSx7uCRA9TVsSAnZWagAAjusP/1WyW0p+OswfGL2NUSoA\nDQUG+pvFlBWE4J60/+ITL697MhVoxKWLYRKlAhDHkpVEMmDXDrM35aJRHi6W\noEUiv/hqwC9x64ZxlgKfsACnw5891Sw0vRc+myD1JYSWKK0B7PUmrW1XVBEr\nwau9s3ARl5wJwgmFKenBeBR4AX/smOnETCfhRmv7fwvFQX6N+0MgOB+wSX2f\nhxqoVizZcIsvleaCULiYewe6gp5bx5LFItbiqQq1n3Ia/62YskMhcaF79oxL\nL4nXYJQRwrI6N2c4jUxx+VTILJQ+CLFNnYJqyvQmRb5BnmZGN+kOZlfW908C\nMZrCqC2pziBoJH/nTY+MmblP3YsGTdMyKUlLZ++dcbnTUQ+xZAyEs3YIltWh\njMxgpMinilM11oizrtDg2YUMyj6y1QQYL8s7emXtPn0heZ5pK/OW40nYeR2f\n1y8O+mlzqc188WWrx0KY0t5lJvzbQuFwAC2ce8l8ROLQ2nOM1LhO0HpgjM5Q\nniuGrIWsC9Q1NdgG42H9zx5dm9jrctk7bjIWQCmlG6ofmc1+0FTz6PepzTfx\nv9Whm0a2qT6f9UawLqMkxGCPQbPpMyPujLdykm1TjPOLF1Dm0cBkQmQ70nXM\nu9T65q6yezR1NRtbXwMvWo7XUeOkayzvjrlZUCUcxaRyWvMSQXOpaHKNm5yi\nI6Sv\r\n=vyxM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAIBqQjRBOM/kkG347o58jDRQAROKodpk7hbCRuebnwrAiBu+RQwUf2qJZKDHKt5mDk7g8RwPRv9vn2Tz5F7BY4v9Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-progress-report.20180715101232_1531649774216_0.17122556357129315"},"_hasShrinkwrap":false},"0.3.2-issue-456.20180715211127":{"name":"@atomist/sdm","version":"0.3.2-issue-456.20180715211127","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ac93807e3f089a4bfbf6ad3213824e0320f415fb","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-issue-456.20180715211127","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zpWVZ7UjFpBgRDOlJR/qgLlCHgbfya224HLWglFyjxF+XsjLj9QobJlB2DfmnGkwCBjleMOE6iTPObNCZbq2qA==","shasum":"ba54ec6b673c5c8ce7526dfccc9acaf34309326b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-issue-456.20180715211127.tgz","fileCount":762,"unpackedSize":1719143,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbS7k+CRA9TVsSAnZWagAAogwP/ihG77Dz5KXrOrJAefoo\npJhToGv1JTd/aHcrSZD0RlcUvFqPaTrFvOdxAmBGC0fpUytZYAfcp7Xwn5MO\nMBSFTC29SkmZM/Kf85zHZgdOs4dvdWzehqlWuQJOgo6lVezVEZQ59MHpP/Zs\n42LawWCy/mFNu3qrBistGk81XQ3KRPOLzZZf49vMfRyy6f2ofZD3YYtSkBL+\nlmf4ujp9MOyu8h8XjwYUuf4tIaFE8BeK4aBw0Eexc/RPcjQseNpNLdfahKwW\naSF4XVZ8HCo94zkKhuN9xJ2g4NxA1yu9vMyjCBeOgxvh8mHTjbZRZ4ZSxP1S\nhTP5ky1HmaksQik+qoY5SSbEnRW1kuLdf/6HObqUQy6iUKKo8iqw9CBBmXhv\nEIt+8zZDFNZ1cMgfAYic5/wib0+y8hADmQ7LEdIqXYHzvII+uSEZs455Fa4o\nA92aoyorqrkDuRpEQgzm6mJ5MzsZgKj9JlnQ5qoZ+Bm+2rYJcEXuTldNy2s5\nVwORkPnBvqYcxiDIq9+XlZEc/wdq3aVrwSYY7dXxZR+gFhoZZqitW7xxnHEL\nmI0UoTGmpuXmw40/VXeB6aL7j8meDI7OS4DqlMNK8zTt1G/Sj7I8zifWHSDI\nMyKnqAq/ujsElLQnAM7E69R0DTW49HAGAvnrjZtow8FcFuJoBtOdVzK5Mndq\nEjpB\r\n=ZIKY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdTzecaboXPExf5SzhGqI7TlLWSjaluCmMUK2T2pE85gIhAK5ssz6wgOn7LOjiXU6CA73OEVjcC+KD5NagMzcQpk4A"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-issue-456.20180715211127_1531689278403_0.9244335015388903"},"_hasShrinkwrap":false},"0.3.2-20180715223627":{"name":"@atomist/sdm","version":"0.3.2-20180715223627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c48954f3be9b379decff9430655fcd44382921dd","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180715223627","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5qiyJMHlR+uc5nCbkndO8YGN9K5F9VKbMYuV1t0G+4mhNYlv9vfI8jenkZ8aIr3K7VVvEkd5KHV7V7NLGXGEDw==","shasum":"85873ea9e90c8a90de4decd5d1923108e74957d3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180715223627.tgz","fileCount":762,"unpackedSize":1719133,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbS80tCRA9TVsSAnZWagAAKsgP/iyTOD51rQdIB6o+GT0d\nZWGSmFFBV7TLy/I7KH6C/JK787fA1OFlSZtk5gZKhgRy9EQr+t8CKB2UmUoN\npA0v3G1NGNRgSfSr3F80PgQI5yoVXivUH0AGpKtsbVVD8QvQNqqmHVzYYHWn\n2TmxhYKZxhupMGwtkjvdZIB9vkFQVneCuXakiyk+E378weIVzZFAttb/kcCA\nNtG9QzbE+rSICLDLkwiA5ZSB+0Y82dSgn/XHRZw0P0/M1fX1pIDeFhfgNAOC\nDtFr1gm52gT4NreaL8Auaaf+UuTsgEqdwgmUMI7Qbw9ZaS0wmsEWumoDTg2w\ni2lkv/zN/8NrR73+MmiwJjeFYOBV+0ceN+Ro2oAZQAj0ZKyS/v20OOvRUyBf\nesMC3DmLx+S3M6aTqrU4BS1Qkyxk/GudKRV/evoOW+30YIngPR9UKhIk2xMZ\n2HiHVTm24hX+0+FfVA+tDBQ5FZ2uREYtqvCQa1pleV0ktmBHCAwMXBlTsP7b\nLo8W9jpOEpe0FpONsvDg53rSanvuJH28EZCC47jrZy9rwotpK7fDpyS/tHf+\n7jdmJpWEsvWJAuUIGDkzcLGvqzT3kq42p5v8UieMVHjKbDQg0IHin6jo76H3\nPOBPe3OKZOefYTIaofm7B11DjbrsNNrEJncnicZj88gkuhVR/KxdNsz3i/xK\nymRA\r\n=0u8n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDY0IMy15Pv5L7S+N8tPApp4PCHI5AfKdhqbJVjbAA+7AIgEAZVYhYV0zwJXtgUbZgV86smMOR+uRNgUBW1UbnirP8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180715223627_1531694381053_0.08943378057581275"},"_hasShrinkwrap":false},"0.3.2-20180716044057":{"name":"@atomist/sdm","version":"0.3.2-20180716044057","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dc84924c0f88701ec7ffdce775d8c7cddb0a9631","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180716044057","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rlAsKgBQrQrS6P2sl65SvrMZt9nJbNLFtAfqzC5sXWIbTvjjfrCM5GegzyxMRqxQ6ti1NoclFLmvzSIj+OP+FQ==","shasum":"9b23723a92a713d9788ec3243b987649c4d9b2c6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180716044057.tgz","fileCount":770,"unpackedSize":1728527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTCKaCRA9TVsSAnZWagAAN3IP/jDfXM7g+DzdQsXVbSki\nXLeXO6UZKNF7u+RhLg3BSR1c5deBQuugdEQUWPos94lcy7z21UzDC9Q49awk\nsl8vNGCX+q9Tr7l1xB1SlmqQ2v+bsY30fXj+RGFCLkzQCrg08gqlRXnNlmDT\n+QHOZs3ljzZ/DzJbDxraJOqyCqUs4RpVQdoAkF6cr+4NicpMFaYCll1b2NN/\nXQbsvw814Jaab+P8RjklEPxqqN7hB+gCiZRNpXU0hbxRcY5zTCCwVrLm5rBO\nhm6MxLJBlF1gkuZek2YcGl42Z6XXpcAi81N2rQXHk2cYgz3/cc66XBdNJi/Y\nlzMBtMAQlfOAX6zGvX6+guMx/+r1/CpbHZIdSL8xbEcKTZ3wrEeKHzNrR5h2\nSjXPmATL3KcaA6YWU4GDOM5O3wt+4mo5F2PlxNJI5AZ7jFVkjsuigDtSrO+z\nEPoa96Q2E402+kZv5aYWMefamprhmyQr0Cf/6HTcD/rtePCQYCCrnKS/hQsd\n/jqmyKpW2mGoaDnbvBUsEKZKY1mZDZdSbBelGfHkhBVjeOLk8HJ1LBQ2n9oT\n2mQrjOVjorbFI+0ao+j5XDXxGLQMfumpab2qWj1HxRsG64kirqHb3wE/cUhY\nRpBcvZJthFdocbVS9dEEE4X90NKStXlPnkoULl9kHNZ1BkzYZDpv18GkTWGa\n6ri6\r\n=61DU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBhVJ1IozQrNR8u3IH9A9jpIlHCDJemgazssAk7IS3F9AiB9cPxGx1xOUzge+Je09E63MUDfCnPhHeuUtjUDD4IxbA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180716044057_1531716249686_0.40764914922108786"},"_hasShrinkwrap":false},"0.3.2-20180716074440":{"name":"@atomist/sdm","version":"0.3.2-20180716074440","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"801be89d7b598b88dff0e9fa8a9cd86d4509b898","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180716074440","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-To420iWUWvfgVjHsB9Q9/JZIpVWq5y85myUDIdrQxZ3sI2JGVzAUSPosRJ/UT0QGJ3PmWSPHbdpi4ZYbHN/qQA==","shasum":"4cec3cccc53db065105c760f0a728ec46e5d1fb6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180716074440.tgz","fileCount":779,"unpackedSize":1734087,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTE1+CRA9TVsSAnZWagAA1aMP/A3udYNkWAy8WlO4LXjf\n2d0KZZS+c0gMZ0nywFx5zNIjS1tTYqx7UAFOHa9uqLw9Ib3WOM8rIjwRouE4\nLuIKjdSvqcUEWz0A8lBMwPDdPCntmdaqe6ITBcoZmlHXhUBja5oiE+IwN0IV\nAfUcceHhJeerTs8R+RKGMO1wKBQjpSwFr82zfxr+WqikJ8dsjHDYJJzwanRm\nOugDOQtW7tc34XZoxC6CSwGhmA5Pb9ph4fSzQ3k04UPEBJqFlMzzZQfq1ByD\n1IDQUW10RzGYsbiXTh3K6ao4VBrU8vuuiyOrDsPw9oAkGbpN5hJvAITy+mmx\nRbAUWYQynvUiCC4ogt984+x9C5aCWX+0FcGQfq/gxuR47V//MQ5nTyPPqsud\nbi4YU/d4Mkhie/v00K7lQaZz+6dTOoKSZgoIc9x+wx2srClKMILuVE70ZOlu\nO0XEX7YSn6TIoiRX0HfMI5NJF90mk/0arqJDSgML63WrBAzDx+mCrj5Wi5ZC\n1DADTpbmipckMCMCyzsYO6I5cTIjtvmXjCXh8R4FIKSdXzNJR40EYUC00zN8\nMIaNugDxcW71at0Fe31gBCJted84hZbx6e27OBs7/wKic/iqAdN/Bwq0WjvY\nq32YQm8mJonyZvchL7NiRU2Msv6AVe1dZ93XTuUg9PEt0GEG2yIK4m8WHVL1\nkiSx\r\n=dGQC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEsR6w9mtkGN7rpFqdNPUfSLiB5QDYstMiM9+IF6c1O2AiBoOar5bYZMgvZxzpmawJCTIg/EbNKlsE4VN1Qc7e0aXg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180716074440_1531727230105_0.17559808866998905"},"_hasShrinkwrap":false},"0.3.2-20180716080725":{"name":"@atomist/sdm","version":"0.3.2-20180716080725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0164fd317a1631d6e4e8e045442d2a0d924f67d4","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180716080725","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JgqVR4ETPlmjL1b9SEBICwsUfyxCO6x9G8JgZ+t2Lz6+PR+jpinK48WIxpGcuvneNrIWQeTGXvT3W79sEK6Cwg==","shasum":"34bfae5c99162ec06449fe15e998ae4a49ba7bfd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180716080725.tgz","fileCount":779,"unpackedSize":1733917,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTFL6CRA9TVsSAnZWagAAcJUP+wdkcUBfob3urll9kZFS\nw4fct+sITGgGw2ioYsDyJTZzrT7ZMz1qqXXshd85DJ2jsBLNGIw7dRHMPnxj\nNuy+BMap9oUnwwT8GrBFu9zb8y8OTmY8SsaKzxhOjDagUDOX75CB/mvxsl2t\n/SqW8HHF3FWLC21oi/D/3HjNuAxGgEy0sCyHl+4rmm7n+lN/UoupuutvhhVs\nPpkPLOrrvu51GNwAyYZIvXBV6Lytnscp1S/cQio72eKUSWQW86Wmvw53ZRj0\nUEDy/a2bifYncp1HEGpT0KCAjQ/bXXP80IL++MytRFscowykep5oaWpJeymT\nUvSxHuIzc1YCVdRcHnSVZ91eYm1bY3wIcJ9CViObKO/0dlv1f8SzL5CpzdWV\nfAsu7RNzaDSGA1JGoZutDUAd2x7JBgBEsfo4BKZXm1+RcSUtllUPT8ljaJjf\nN0zx2/1/5aCyt4HTsKjXRo27xmGP02898GGh8eE1XGoqpLOQAuv7UEI3uz0g\nnSHpG28ie4AQhTF679KkWaMMkeowZipUHLdAs2g8WxXNnFd5dCYIaNAiNX8H\nDKuG3MXBsGqn5V6zqmqn7w/aZfk8a2t1H8ZgCWRyxkEI8jQPC120zLZWwpdI\nVvz68sELBYZ+akM39/OE7TN74IRUR1SBJqI8OHlrKTxoNMjSIUeuSg6k2zEv\n6M07\r\n=ABAQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH/PmA7edwt9XnpORvQl5Oit4ot6StzdH99qSV2NSu8CAiBjaOwERojhqIyAfs2X/GxBZB3P9PFJaIYXZG/sIUsyEQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180716080725_1531728634774_0.0055993473399185145"},"_hasShrinkwrap":false},"0.3.2-laz.20180718005131":{"name":"@atomist/sdm","version":"0.3.2-laz.20180718005131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6342cecaaf979108cdc8fbbb7c4af986fe6b145f","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-laz.20180718005131","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dK15OZEX9wfshJ1pHUZxr1g6GqvZb4N4J+LyuOHPWfGPiaHN3E/m+OC6VKFQOyAwmQwTwOtdSdp0pLmW5Y3Gog==","shasum":"d87484e3a3955cf2498a54df8f097d5247a744da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-laz.20180718005131.tgz","fileCount":783,"unpackedSize":1752991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTo/fCRA9TVsSAnZWagAAkM4P/jT7Hihp6jThGFuhtZrm\ndEvaduprGYp8qzcHtIUDoMofFvNm2AcK49vhhFBm3YooT37Mj0+FuFCsm5Nn\nP5SM/b4HDYP0heCExAYJ7s6eL/fiw9HeOJi20veBZrTmjqdTxmHODR0fidQa\ndnOPAmcmTFOjwshf4emvbdS7SLq0JXrZNf58vJvoCvxc8ORJ/l2mEgCaUowG\nDJvq3nTStdA04PTFv6aFTz3ae8tRFuDr5AbGMZo1mY9Rv1Tx9kleRai/blP3\nReULY5x6JxcEyE+auZnKl8j8HMzOThfvoYJkfD7phLI5s0O/IIK0H4jGxZZQ\nyrF6Gjim/0jC3JoriXD9roa19zx/WBrwZ9ASxvAtgHVHp2foF8cES5gkujCQ\nUsTBh3S4R/AKE+irNXdJQEykW0Uz7dovw4FHk+mw8tfe+bA5S9c3ty1d3YIB\nSxEqQ1Gnsc885580d58azd52w0HDD9r+zYcZO04yiIsWn5W4MecAV8k3/QtS\nlGDaqDbXedHI+oC1QZaknZXf0J1SYJhiCGHNji2gQHcLcyllkubKfoejhYhP\nuW5Wo0pvaQvzLhv0e/Gorh2QEN0osOwEhlj1KQzS2Q+OF0m7ndXd4xWPNfkp\nizg6CPs/rtmZiwIuBSzuWy2retnnKpFXBVVki+EtRc1zF4I/SZFg6Fr99SIf\nmyb2\r\n=+fSL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCLVrg9OudT6/buClkSCiI7xwgPtx6TiH8Y5OhxejGeYgIgGQB11n2ofvy/8dBGPlkhP6FAt+eLLDDuwKegtvKu+1o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-laz.20180718005131_1531875295405_0.8824882602691764"},"_hasShrinkwrap":false},"0.3.2-laz.20180718010840":{"name":"@atomist/sdm","version":"0.3.2-laz.20180718010840","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e39f50bfa40af1e78828f4f4f3f8ea7802a4585c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-laz.20180718010840","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TyeU9Nfkg/8+/M9CfCeocjl1jnPQn+A/k3XiGALw2qnKZqdXLYYTbpKslHRKnRNHPBdFmB9NJL7tx2WXOd2uEg==","shasum":"803ffa83032146d21db83ad3f7fd49392237bd19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-laz.20180718010840.tgz","fileCount":783,"unpackedSize":1753172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTpPjCRA9TVsSAnZWagAASdoP/38+eteYUCA1Oy5pM2uZ\nwm2hztl/LlyyI7EjtCslTmQl12mo3vGuH4qASM95I2FsJmIYF5n64gsfxMWi\nQoUPq3R8eIyGkeY8ERbZhaxis9eaVVy7i9O+A20xM791qJO+jgN4eEXJttR3\n97NjlKwb7bhjGdIUhOhpHCWPos/9pA2FyoYC19iiPcuhg6AUzNAAVto+nie/\nie1WgT7uP5OQFJDhIbysbablVYCCguNezbsxMkHESdvqWiJkOmuqPqOpvdO8\nKgP3WIfl8iXvrmfwLfBw2gW3nBd9jvupJTdcDi4RFDWppRwWId66MFRTHP+H\nZhM3hPCz6uSUnPG0mw7HKtFUKlgW31fthgm4nxgxVzcMfQJ5HEpdGdGcw3SP\nJviLKxDdatMifsDtAiJEm/HNnSCKS45f4J+q+lCqoi+RSj3EUnvhzhoqleKZ\nvIFVuWZ5okezVb3zZiz4c1TOW4bCmcc5c0FNDPMdFtvf2TKkHyLc23TQDAnd\nIgo7Vwh5bI4AOyGurgLoosAZAX4PeMkgDvELxFBcoP3n8EJQa9VNbva0ShnI\neRbocm8zIzpBPkVGzAdP+yaAWtQPGPg7E9BSaagVAHhBOHkyg+nWBZ3jRXsj\naT7xX1rM2DY/3yZ0jracD0AkWhCOsuqxpcQVqHL07zEMn6ibucZE0C56kld5\nyO49\r\n=0KIu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFVKmzBlcfAfhJ6h3qVT9V9makKLbaiNcX9Oz83g2gg8AiBzZPkW+MV6JBwBfNYKVH7QjB6c4Q2ffQHdHIcwHoXxrA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-laz.20180718010840_1531876323415_0.32200275050333005"},"_hasShrinkwrap":false},"0.3.2-20180718011920":{"name":"@atomist/sdm","version":"0.3.2-20180718011920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"53204a257c5a8f02a7c94efb8e86af6792aa0738","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180718011920","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KZ8GVLyymsqt2D9tzf//285juB1/GMgnHJLZtZ3rvDghHxdEsN5t2d9vge221ynQdm17Z7c78AmWqb4dBy/IxA==","shasum":"151a96c210e6782c61524a48c9b2fa0e85329f38","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180718011920.tgz","fileCount":783,"unpackedSize":1753168,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTpaUCRA9TVsSAnZWagAAwgEP/jafC3bsHj3haSQNkW+r\ndMPmuFKQPUbitnmKU0r45xrDdFx3eHSQr5qDIALM0e1qz7WlN0oOcEbLu+5q\n4ywIwdib5ax4Qt3ehZKtInQAeB0OTQ/YROsITidRPh1wrMihB92+yEdfhgut\nLA8B5t4lSEINmebXfI0Y6enH/n0vemhl5ZTB991FtH4DSYDpotHHm8PCjuk9\nm5JDbjjPCbQ49GiDFsHqlIqj35LCybsLn/FPDWxuqlnldSMiq22KGrPYup5p\n/VeH0yiDjkc0gNNaTFxlB+AMUp+aVB1VFeG9oSizT++0JSNLSF0dXmq+MTjq\nmtexc1xRF8zck5Z5quLDbs5EQ3GT7Dy+5UHhXES3A2VQksMp90Rx8AjZYwUq\nIVnOgRNnmWGuEe3Qln49ua3IQGGFpSxA4yMeJWNQFtpEzGScvcvYb8yaXaDj\nn3m4EpoCvIM5wvncnIN6w03jbHVEYyFQ+3tZOLJmvAmSf0HN8hrTRlTCzSV3\nytWfiR2Ws4uXcNzhogtg8/moY/cIBDpDPjQqHfpos3sH9TsnJBQa9ZmcdaxH\nDqH04LHSJlyXL/IqpSrgrQQ2rmJYx678VXgdYRjADBKvGqWqNVITVSYrcEeJ\ny7O3aWxNuT4ctkbQCTv1b8OHR9tnjcpKpEwo4LHA909VVYMbLFv9qhwuSDJr\neHBJ\r\n=mvhN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFBPA9Jf42FAPtX7OY9gppizg4pdfVamEtZkajS7ZyFSAiEAq64EJE4bDqvOyteN1qMFZEmNpUdg5155SMIUJix8r/8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180718011920_1531877012239_0.6678569256760019"},"_hasShrinkwrap":false},"0.3.2-20180718025643":{"name":"@atomist/sdm","version":"0.3.2-20180718025643","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ff09e5c66501611238c521e397f5c136715730cf","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180718025643","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aPvuYIu27vGJZHKY6NN+NXMUhhScq9UPhi7V3PQFI/COdw0olx58/hgh5h+sAyChrwfD/pW+WHu1vu9IqiMlTQ==","shasum":"6f471f7adb034b9f93c41a0be697257ac66ac0c5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180718025643.tgz","fileCount":783,"unpackedSize":1752369,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTq0vCRA9TVsSAnZWagAA7fQQAINU7DTUko/ipV//J9HB\n0379Ml/mKayZy+ajQlx2aw1ffVKiuD1xkLGBOcqFTx5jnWt3BKp97OHAI45Z\nHjseCiNG5po9qT4TmGJaW74wDuoAMmc+ZTTsjqhcMsUwbnGC4ureFRgFmZUa\n/Gn8vNr3s8O9KhbVQzatvJyIL0JUva7dGO8FNRaUWBlQFbjay6P3uzjAi3dz\nyQ6Zl455J1G5I1lE0MFzbozqXEOqIk61LOFPcTmiZh1WOhBf7TMiFCQ75Zxe\nGvsAGzeDpdCZ20gL01koTa6unGzouRywWUgQIWLrkxS4+dWLH0XR2HiVtKjJ\neRGxI+fdgk1DgwSclC/dQuvIAAyf2NQ4Hu9xUyQcXru3+4lEPYWwbtFye0NV\nDpmnWNsd/nF99K6QfnxonX0UelW97vLZjmIHL0Qb85FR5ae+TbKhiUZX8yvQ\n/h5kUc8aurSXx1hdN0MsQ+6SqQFSqMNqzbZDOh0KAph5R6cgM3gr6SLUnWiX\nFXmR4ObhMAa+MxOFlWuZsWF3y15QJcxCTDL05AGshezgkSH5HuZgNl9jnvb2\nZXjdlmBLUbIfT7+MognqQeE+QbdCpWazO3PP96BYI5/6GtRduafEvNm7cxdz\ndEpQltFneqFqbBfqrR0U2Gk95RBTjifD+TSBgNkKMrza+C1MTivsxI3FECiq\nHjh0\r\n=CqRC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBVXv+fBJ/sEIN+FpMqqKqjXbzUIB4OjakE7kM/WdqltAiAfWznIMb+G/QGJ0PadMSz+IuCjc0qIomfRecYXjr9y4A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180718025643_1531882799178_0.9644875023863935"},"_hasShrinkwrap":false},"0.3.2-pm-analyzer.20180718085338":{"name":"@atomist/sdm","version":"0.3.2-pm-analyzer.20180718085338","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1d7c195626ae9e3ffe5519196b825793f204404c","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-pm-analyzer.20180718085338","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KgU7VPXwbY1Jxhn9GOB93nQ/YvjTMFyKaRHb3jpOYch9wnvlkh1XCFqQCxA7sPmCt5Emk/2+w/ta6ju/c4M6Zg==","shasum":"e8e17236a01d157dd31dd90ce6efc0aceb468804","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-pm-analyzer.20180718085338.tgz","fileCount":791,"unpackedSize":1761561,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbTwDUCRA9TVsSAnZWagAAsiMP/RYaIDoCabm5aMtGiKoQ\nP3SAVgtzj6uNdHV/CiJ39G+xQW6O63p9yTfb/05eDia9IwrFm/m89Vk+c5Q3\n2Nuk7zzacuhd/N7lgBOXzreTXb9pea6p3ZU1fllr4XTshRQKjFrW8CNbWVtb\nSjl77EUG2Yz6fb1gos74zE8+oBm0slth7A9ydRpN37yGXVAiaRwi4/9oANCG\nNGl7ozNjigHk+bH2qPRzweGbTAaK/6/ksmnPar3+VQB1il9FOVJkrDHYudAi\nubYwbOeFUidFPuuuJgBNmcr8W2C76wWeJ3ONOyaqrSoBZ09zAGaiecJKwcfg\nP5c0BrdAAlM8nuGTxBMtukGQ09RhafCMVx/dwje/nQ5k/TLpri24MhtqQJ0F\n3gjykQSWjqZU9cs0d66eZWciECspLS5G4hGqllN/kE2rXh+84LQblo9FzIht\nT2N6fE5aydRdfjcNzlEFmmCAxPr1JYZSCQhPAK9WHiycj5IPH74jLdzKuGzf\nnETpqr8/JFe9tWQVSM5k9myhb//Zm82GFetudzpl92CMrvZMRzuURAfoyMTq\n6ots5mFB2ZqxTYLwbwnchoNLqMNZOUmN6t1pUyJOC3QzaLGj1IZvvTONeE70\nwCqMfmGbYxRoPUBV6aPKfQ3b515+YcGj5gq+dCUdyx/I/wNL8G1XzCVtl70r\n7dR/\r\n=mMaD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2tk+ioNQityNnj0rk3qLDl9orp6FBAiL1LdIqXgbZSgIgeJ9bb7vTnwdTmQftVrpUUjIL9iT2S3x8SCZKeWOgRvg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-pm-analyzer.20180718085338_1531904204045_0.5325970856835069"},"_hasShrinkwrap":false},"0.3.2-20180718223432":{"name":"@atomist/sdm","version":"0.3.2-20180718223432","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9cf54751180f7f9e0b6bb3fb52e1d54e800ca4e5","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180718223432","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-56jcQd2O1y7b4UW1z9VrydCpMh1QkuhKHKdxmVPlziV5Wt/YwRc5sTjkLSrl0jP59YtYPW4Wy1lfGNyfpD6WIA==","shasum":"e503656262bafe553d47a16f223d18da424857f2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180718223432.tgz","fileCount":791,"unpackedSize":1761549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbT8E2CRA9TVsSAnZWagAA4iIQAKRZz1t3+PFryV6w75ao\nBucZYITsx7XqDu+eXt8ftF4X+CoeYHybKHSasLG0mcON3jFd1XdMFlNKGw1I\nwvpdPkZvnAZ0ns49pnA/dBNTh+yCO1BZwZfpaiwb4b7v6VvLJ8fK/CPecz/2\nq0WxKxb0IZptmQ/q9wzzQreQBThXZfR5dyGtJ45VOIBDSp6B9+BZzlGBET0r\n+F/MO8bUaBARq5aaHdBefkM96NVT4sqWFGqXsAFRuSRg/7wqorKVVUdFhwVb\n9XW9P0QkHVFzBBGWXs7G4JPr6gR1P0vFbYp7ieUb1t5WOwhXdFk9SveIyNuA\naPXzPr7fF8Hv1LINI+NQ+vOAcT0HsquHNn8RNRG6nlxqABNkWYvC5FHkQive\nCuoQ4oA5ENtBIkP11adCqjvEc0CUac9oBUYUeDPLkpDD9OLryTGdapu8VhFx\njnx1lKCQd8g/ZKTxpOjtHS6TfQblgIABxMi//H6+SjEmYW0fEbyvNAxHVMd4\nSTif0MtCMQ87MxiHQrVlfd1BSuHjz5l2B17WkXWzai18+OTklqzjSXc0joVB\nJaa0txRrgLC9SRUBw5TF53wlkCH81ApPiKIqaIGC8rsg0+B0srxrvcU4v1i2\nKprJ7rOuvNGZ3h1xKuW+aFWUwQhVSfQAXceq5Jw02eNKm2DgzbC0EO6jenCX\n31hx\r\n=T5A7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGvpVpHClOFeRKetk947PIx4qdinBdHQbCQ9hyD9w+EcAiEAk38JO2xktF0YJJ1Fva9R8qpKpW893fM9lI1kIra7NOA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180718223432_1531953462090_0.5922487406634409"},"_hasShrinkwrap":false},"0.3.2-20180719073654":{"name":"@atomist/sdm","version":"0.3.2-20180719073654","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"96a8a132e71d4ed611fa78af111afb047709556b","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180719073654","_npmVersion":"6.1.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tpLiAfBREjVASvNK9EDqj95DR8tXHBS9LjjYjU84LVSyQJ8Q9FG4dcRNmzKAXS4mNO+7+7aicP6wMZlm9Abv8A==","shasum":"762a2380e8ec88e73858bc2031294510569b1b78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180719073654.tgz","fileCount":791,"unpackedSize":1761590,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbUEBCCRA9TVsSAnZWagAAnnYP/RM7pwjeWDPkQ0EC6fSe\nXGVLGq2VMCCgwOF6is+sxd5vl2AaJolPxYqiFC34QLiSqnVJDDMeQ3/HlR4v\n9tWFASJIwF4D9XWzKZzYxYDjGv2Xkit51MPlquVut5GuU1Skpx9No8ZvkIuh\n+PLKRWJ+M1XK1M/ImHTKucqJErmIefgZhFIY6SsGlvFatIh7oDm46I6ADbwA\ncw9YMJf5bdRFIqlaJ65CWCH3/iMorpN+CZx5iv6Pzb5+2MYE1tKLGcmeMLia\nZgdWVrcLBVNeW47lQuLYoA2RoSeIQhUE/+alaX1P35uWAEK81YVJ/E/fcyh3\nQ9Mm8asRY17pK8loHmWJrbSvFH75JvE1M2zt/Z38EXlkAN+4VsLYNNjisA0u\nqNADVS5L5qv+dBIF1y8CkNkQDUPTOs6NxXxnwQvi4SLlbZqVGXgCkRh28I5k\n7h1hm0a7vV2T76eytokmmcEBQ5gXQBy2mXIxQI17UBrYeGr5rAAW9Y6sOn+x\ncqSZTMIUfj3D6FQ1NiTYpst0W2zPI5rBoNCFdb1XSmn1kvLSlIbc3aw8ANOk\nzx4I3SMDkVu6OgeU6EehUSYeXUtm69FilEC/OTPHJg+GGe+f3rTPyJNNK7SI\nC+7HlYtN2YeHSPuB1JDjHKFplVOcwZ/BlEo+HjE6NqsNhVneaQmJm+sbNKNL\n8+3C\r\n=AjGI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB5OlCKTndpSaS295/0iR+0JG74GjnEVPivgsQBUT6KsAiEAowXv4OdB4gnIraO3JvUPf1AI/jqGWg7orvr6WmFP8aQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180719073654_1531985986340_0.817399180681825"},"_hasShrinkwrap":false},"0.3.2-store-push.20180720060041":{"name":"@atomist/sdm","version":"0.3.2-store-push.20180720060041","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"79cd74c03c1fafe73e0d5794c550d5a131c1c77a","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-store-push.20180720060041","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KmEiaGps8omt+QdaPZhIkhpVq9Pflcag93MN6sk8KUBJSnT1hNz7sayd9Bk1prs88i/HrYJj0W9I/epIEVHQXA==","shasum":"503beb0521a048749f4c52a8cf359463915de8da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-store-push.20180720060041.tgz","fileCount":791,"unpackedSize":1762013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbUXtJCRA9TVsSAnZWagAAD2UP/1mNNZzPWb7WqVeec0ET\n+nId8NcYX9JVJ0Fps4BM7IZk+SniZKTyd20qA+6L3IT6OTfiWqUWDEIFpTaQ\nqKrya+dlNF2QyL79S9gvWJ6O8L7RFwuhCTpfbkAGvNLrFrETCe3lxdOskGg4\noaAsB8+HxrYGEKsuAQVx/XePavuFTfUlbTN0Ytk1dK5WEnsZas1Y2V2Bhw2J\nLHXsHmcC+EfsF8gtgQeSlQ2x2c3EgJjARCI/iHDBNCknm0yyFPCHQbxjWzg9\no5DEF4tF1k/0ZIHbtSbTSHMhfdIXc9hl8arOzc+Ic8lIRCBm1KwsvC7t1t+O\nU6l4T0f+THHgzqbdpRmJ3qZzYDMyDo5WjpgBo5uRiSKENmEvr80jjbXNc3GG\n8mGNUmtIFQkzuYwfwdVJYkA7sv3uZB5ZrN9GyYmtJMnN1mxwf1c2Npz+XePi\ndTOGDHhcqSfAlOMPJDBud72wUaOjfs0vHs1HpiI4AiJHd/VSppZo8lSoGo5H\nzAKpJ3zc8B++ELzZtlEQnkoTOc+LhgjlUIVXIHG5LsuGsskjltCBfkjKEYiC\nu07rQixVRPwVybFuXjMHqLvTiCLxETwUs5dZmk4eE1OqTL5ffNxh3g+JlMWC\nJK7Bdm59Koz9Q5wjmyWzDhCQsiXMxXSD018oE4vpAv3MgOhLrVZuSIOo2n30\n7thv\r\n=CD8s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDMiD2+vTu6BB0BMMq221UUesFJWZbKPrP1AreGK66x5AiEAt6sjmwpF0keEdPfphnx1yd8ArphQpSeJnDWihGQr7EI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-store-push.20180720060041_1532066633466_0.10326955343427091"},"_hasShrinkwrap":false},"0.3.2-store-push.20180720070631":{"name":"@atomist/sdm","version":"0.3.2-store-push.20180720070631","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7e10bd484f8680866ce7986b8b55574d07272d40","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-store-push.20180720070631","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rMHxo8sHUdPpW/HdfemiOZv09fUalYUbttyspSOwoYb9DWdsGax9bcZ2Fswn85DIIX9b32zXwum5EXFQ8/rKmw==","shasum":"f5c2d3d300dc4106f4ade68f6647429854be1ae7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-store-push.20180720070631.tgz","fileCount":791,"unpackedSize":1761549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbUYqOCRA9TVsSAnZWagAAQRwP/jJamW+VR61gnUHaow5I\nAJ0G7lppH2bJ5GOh/2Ct2MBG+RJxbMWQOVpYVUQgBVZzOjLMSBCD2lMEF/fz\nr61QEsZJYwRIhE0UUKl9MWdX0RCJV5atZJzbjJZkq7XYU6bymDZIDaN1A/OI\nqy6sFbJWvGwpJFF8guhjJ6guU0cXr5eXAgghAnsr0aH3GgjEDm9RTXzcfnPP\naSiQkH/Tn4UC8uPwTAvMWiFCuZPyqW/8b6bghUbXuML8drIH2Z73knMrtMqr\nLP0i1MI4lOKAjfik738dGVYzIhRkFvcAhtHoa0wblhDiwRlx/+Jt2Ssarm94\nS8FgEUjBes4hdsFnC86adYU29w1P2sfCndqom3EaH11uGW4fF7JOJUSwVsYs\nu2QmAjkk4LSufEZ6B4EO1HreMr6E1omI0kdCPBBMv5lGjjZmNTp3CLDJ7OVy\nrOhpa+uDzuKTbIR0+kPkELOX8hCUzZ4XCqdetGivcRz5QacOX7wo+fxfTO68\nu4Dk+zGoDCEqig2ZS5uBhADTsLI/F+IiIpxACCJYTo9WGazRIvyyrRZV4o6x\n2lKpuWQh/ojaugJZ5k99dc0FuHNWXQrQAwmsPWqRVJg+sJsXp/4w2QD00xTS\nopVDKuPVDppA6bA9oPk/K8ILWpb/4DMz9K+yE7dpNmoyuqnv6ovh5QHnO1el\nCfWw\r\n=UjzO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC94Ejj+kO5MjSEs/HxapvvJide15eLUUBCt0aq5y0oFwIge3sj/EJ0g9ub6ghWJ9PnJbafUI9aWpFoQTZ+zH6ovlw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-store-push.20180720070631_1532070542603_0.1508546967703297"},"_hasShrinkwrap":false},"0.3.2-20180720074808":{"name":"@atomist/sdm","version":"0.3.2-20180720074808","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2d9f4d806e686f9bef8861a843839310d20cd028","readme":"# Software Delivery Machine\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180720074808","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2yWBFH9WLhe7G0ozqXCyvo6Y2REk3InEC2HpLxWIl7KCQjDag1IGXb+lALC5q2kerGkdkZDYgmM62sH5VFC/+g==","shasum":"145530573f89e6b9bead107717e0d28701abe852","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180720074808.tgz","fileCount":791,"unpackedSize":1761538,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbUZRuCRA9TVsSAnZWagAA7ncP/jFHwi9/s+dlOk/eh04q\nQo5xqPv8KaNFu28ggnCuGeWWgy164IWKPox8/UpIBWc3jTgqZZaOQG6bkE/h\nS9oWLYBtdbhgOSCjmRsvCyrDc8ImpIx3O4UJUOT+zM1O/YPuztf6iIejHkJd\nGiZ+gfEhFmVcA5cwvxonZdxtkfzr1PLa/uQXijik9b2kN2ls0vXzKiIReDyv\nKpccTJ2Pq7Ni04rbgNEDRkp/iW4TCMavBBSyYdnTvd544lxx6NssE6fJOg4h\ntAxfqcXYMqX3KGdFsIEgCvUJQVlgxApCD9l4Glbw0uXJ7u/ecDbncfJ6PQIj\n6r1lnEylsu/1lO47Ek7Wjg7Vs18JrM8P3JTw804QjGsvLZST/Z+gwqkQ4byI\nhUQrRygqQmCTn7i1ypvCfBJmpagNX6OS1jd3xoLp3NIMxyIgY2LUuNMa3+W8\nLZEoyH9bQwSecWI956F5QBaUNX4vJGu/le4MddjtWDmknUF0Hy6duwfPHkqm\nRCDKbjV1EsRrkfLJSHBz2ESiwEsRAmXt+08r5hgfAIMaQ24VF+Ppi4ta57LI\n7qqrOZQD5Otoozq6q4S0mqzpiMG1bMjwj4gtl03VF20XZUw7bq+kShXtF6MJ\nQwsL0En6O+cAG9TIExA3RYo2kzFyr6Su3EdMlb0h4tkUUp9Fwdewp3+FeMLL\n6jj7\r\n=YP/c\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKgBZ4D7UqDRckxND8/QogvXldnee1Ku00GW8bB5VLswIhAJ9BwsVbWhEqSPdx1AAN4t6h1GX8BBgJMY/mtu1BAldU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180720074808_1532073070188_0.7225870497629103"},"_hasShrinkwrap":false},"0.3.2-20180722233612":{"name":"@atomist/sdm","version":"0.3.2-20180722233612","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ae00b73c047f718ce65afcd006139b1cc52b0871","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180722233612","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wgwkiTSWYelMp4ZmvdtmFGJURPo3ZGQSd05+NUdPEDQ05Xfpc1ZXea3KFEcKu7C79nrU1wDZf3m6tj9pWGMifQ==","shasum":"c9b97be000eacca6ceaa354cb7e4405943fc1883","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180722233612.tgz","fileCount":791,"unpackedSize":1761945,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbVRW3CRA9TVsSAnZWagAA3zMQAJiZe9KAoXi2ZUYKohht\nVrP7DnLLvLahDh+3/Aj1oN9X5IGRt43IWSi4rLXztgFnOYiUKny40xVZwibJ\njCtF0CW+77DtJP8S3qcs7D3LxWfCsCdUVzjKYk6UGdDuS3WYeM47u5pkYW9v\nNVPNZstnozlr2H3T8onzQOj7Snv1MI2e5xeEDyyHI9p+FE2wPwQQl7z734DM\nwvSjQzJPk08DFvV9yck8TClyWoJaTFLjLE+FQDrHD6bA4Cb9OW03CNtTQB/B\n3oBxrh+1GpQXrwoe1OKgOptjhPNWyFCpbTM8kncTECdrRLwd+njYLsMxTap9\nQ8EE29wQbPZBZlH4p5tlkZ8c04jjw7Q2cl3ug7pv4+zLBx7AToyJN24lo1Fr\nF5f0PYQAaLS2CyZURrp/g4BuoDukP+eiD5buttk0i0EmsedcvswyCR/Ir7CI\nMCgmtrYbMX85MgrfHSmVOzaJti5Ln4q+E4In8vTwiied9Mis4AeMzoVpwKiI\nmbJeg2Ls488qnhnJbYJGwwqUyzTC0OIsrQtuD4gMUPtjJBbTR9eHWVaBMzTy\nnsge8bjIhfnqEsXvdXQmVMpWyK6+t0cynUsv1MmYDtuEBVwcLTauh/l5EyDd\nYjKxNiWCuoITE9p5vd7FLrunTGejPYdQ6U6hfHa11vnpU3GcxvRPKZXBwWZS\nehW+\r\n=5WkG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB77gdsy2EWMweZ+61DCjV9GRBWBhLJ3EzAa/aL3CqeZAiEA5g3GcA4j2nzkhGdkh5azNoQ0D9ls6b6abdi+puIVu/0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180722233612_1532302775223_0.045853749053059056"},"_hasShrinkwrap":false},"0.3.2-20180723055531":{"name":"@atomist/sdm","version":"0.3.2-20180723055531","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"011c606e5480c7f4f3e14d048aa608fd02f38509","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180723055531","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QV2iA+Zw274mq689A4bL23DVZ1MqMGv5kE75f/a/8HHQ60c1GqEF78uqrUjlRPgo0yUGGGCoIe4wq/aBEQH1tw==","shasum":"918a36827201048428150b4d8ad321eba1b336b3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180723055531.tgz","fileCount":791,"unpackedSize":1762006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbVW6VCRA9TVsSAnZWagAAyZwQAJBgz6tXq6huZxL7qIW1\nnoqEyzfo3Iot1mkmqnh7CGEzx1gN5QaBx04cm0VLiEYwAij0Qz7qF6+qlQiW\nGlB3lcnHe6/VwFp0sTRJimZT3d4t7FHKkYjsA8P+rKT+gK9+ZEwr0iLx7Meu\nP2ETXJBbne+ThXn+UErF26/ANBPiK6/9C63i9pcQJN/+coe8zVn16XoV6V9+\nsNFi0z+kNKFs0wNM9EjS9y5EVbF5cVzB2gwNDfSEdYCVbvN1CIT6YaIOqroh\nXyl7YL73mbet4NwgAmDsMktCZ9eBziaNlPeXP3CwnXG1oIsSdlObXDnQuAxF\navATo7Fh2sWNw2lF0Xs0rmmUQqqnn8duE9FCDQ50Zyp2KPH7glqf8Ew9hSa4\nz0MVqqouClwDiop/eHXxWYsoJ2fe4Rz9kU9nCqGZiqzlzGcadcUeSsCWuAGY\n5Mmc+TIWBaYoCH2SqijQVmcHjWl0zhGOLv9q9vuKgbEEWHRSQy8YBBDwGKSE\n0LIRMw0XnwOd4+QLb850ltKF23A79rkv9HJ0VfMyHRGW61qElN9JBWssvq3a\nR33caqHMV886Fm9gS35GkA5fP3sYWrENW74J+I7LgqK9dgfycggo+TjAwRsW\nF76wdF7rfssP+E+2cMA94O/VnSWlLSzuNA637YZ79MN6dwWnGrUWnC4dk2ar\nNfRa\r\n=7xs3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBrUG4yK7cPeGS9+PbxTvvoW3cisiHn+mhXEMhhhWsagIhAMjgBk1ffrEh/++95iJBt2Wk80Mq2W+IKjIgjlBToeRm"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180723055531_1532325525069_0.8706147025821658"},"_hasShrinkwrap":false},"0.3.2-20180724035903":{"name":"@atomist/sdm","version":"0.3.2-20180724035903","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c0b8272b944723765524f2f94482a7713381a95d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180724035903","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kMVz/8aoojbH7BfsACPLMr1WadnrDug2eJM8aEM4FO+hS1b+nmeICbrjOMT5JOYS2npWfOJQx1/Fb2tOetK/gA==","shasum":"613777a5a37f784d4fab6881aeea6c5e0c10b6c3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180724035903.tgz","fileCount":795,"unpackedSize":1766647,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbVqSuCRA9TVsSAnZWagAAHrsQAIw6WBM8rTUPWKXf2MJ7\ncAm8szaP2+HmFMVgCnO/3lBp0MYKeZM5JM3tnGw29AjWYWD9XLvdbFhhIuWm\nQ1qkZ5DJefuNhQeZiSc+1fQbE8VgRCL3bGPIAtyKEyVEiB/9uER/Rk5G/nEr\nLfuCQuLMdb92+ZcVRREjzIPdeDcJZ2F/qk/9eY0mJWHcbAKCjKpfIAZCKnjh\nrhcyM9pSzXo/ARPkbIIVI0+tqsM+KzbgbwtgV68RhlNiHr2KDdzh1wkCjwAC\nOhcgjqK5O2GhorceDVimxkpRjOGJG5bK5dPn51hcteII+qkuX3TTKUpFXfwF\nYmKqSCrYst/CeEbwGkhDBWoSQKmMxeHMCdegHoKl/F7RPax120YwE7afE7Ge\n7QHUfCh9+/2Uf0FoANh5jtN5rl0kKF2HH3UJz2ZUpMA2/uCrMK6WwKoTvMYP\ngQv9vxc/MtmXrorp4xPFSNOR+Z+AUGMZ7sAEYDiaZcAFstvdjOZaNVJXpLqU\nYDg/nAs0AkMSVrMFblV4mfNtbLhEwORCebOUy70ZIUkllH0Swrrokt+Sq2lC\n6rCi0jdilOZt2A+BwX7NA4LObsE1/JVFMz+UxVxfr3c4B2lHU/c628/y0NI0\nSkF5ZgJiwCu/V0S/V9pz3H/YRbtwo+97otGzVSX3LOJswN4NhmqLtNEat7Uy\nMuSK\r\n=2xA1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDw/ZkyXdtFayrF9kZMJr4GI4sE7UcLOv5VCZOzerTUCAiEA7NMIgcAh4a/ghrHFyuIXwwPKj2PtTfzPx6LvG9BfyUQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180724035903_1532404910116_0.6083530098985557"},"_hasShrinkwrap":false},"0.3.2-20180724040432":{"name":"@atomist/sdm","version":"0.3.2-20180724040432","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/2348702439","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2a9be6df8ffa67c43357b82b64ab842fe3de2e61","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180724040432","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rY66GKqCRogAf3MNQtQnxTKHxzQ34q97zUp5PFd7K00wAVO78gNIbvnOZVDL5xcX08deVHNIgMzZoBpeoqDAuA==","shasum":"ddfdf169c2ea80c9c8baa24c2e721ab6c9627cd0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180724040432.tgz","fileCount":795,"unpackedSize":1767225,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbVqYjCRA9TVsSAnZWagAAVbAP/12u2N938wVIxIQCqZwU\nGuaXR09cG/psbJq7mlYk7sDVtXcAFDXb7qYXiJUpU65w84lJ8INFLLpxw7hy\nesG1MOvKOtQga9i1nKtnbrVWVe+yHm10teGTqpwsyzxYz6qcEOe1nkrG9dO6\niooFqD2ZGHo0q/+9f0TMH0IJMCgF0rydirLHyIPEti1KGu8qN0xfIXB6F2km\nVd3BtzT/dkA1aTRlIQxqtAZXbunK4B64CoZKQrvc/isvMMurTAIvZBAAFgvB\n+M5+d1GDjM8/ix9bRhuvA8KrcWsanrx64vBNSaWt0cHF4pM2cJBUfAn2uCTf\ntBVUgdz3k+Om2gKB0h+dchFwPK4zRTPd7vhKplQfj2yBIbi4ryev99Ts4CT+\nLMph5rYhHPMpXsmX7QW8vZKtDZKgyVTcFIInVqgoHoUJfb2NJGIJ0UNPhjfd\nH3YHss0uJ7sIVqLzShLSgBvPSk06snIZ/UiwKmtzpcVQVctMYh90r9npQicU\nKVKODq6dBYVgu13Ag2Haj3kp/fDuxuvg0BoUovq11h74jleG7m7bLCcgKXVC\nWMis9dlT4I5B/PhxQP+u1xiBhgzX/Rd/z/8O8Ss2jrbXm4UBMPjVH46ReB38\nu13U1OwQ9uss8YGndiEKlbhecvfT2ELHCoTxOecsW/EEX3OLgbFDK1jRUVOU\nuXI4\r\n=VIWi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoz2NrXseeYLGKWCsjVazuU5/xK7KN/WVV3xTWIkxddgIhAKk31hlTXtXldm3PRzD2yMNmfacR4YUVMAX5XqW19xe4"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180724040432_1532405283811_0.7436555548290671"},"_hasShrinkwrap":false},"0.3.2-20180724061156":{"name":"@atomist/sdm","version":"0.3.2-20180724061156","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180724053014","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9e5e99a68f9838d91a8bdb62658be0b421ddbb1f","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180724061156","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-maxQcUh1qP0w0sgHphGvKW/oJAuBGQGqeJyfX4F2QyVL5EaiokjlRugqTm/JR0U3zIVeHWvwfHwChnuhwqh4/Q==","shasum":"913e45322b636b0d5441049bb5b6298c02c264fa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180724061156.tgz","fileCount":795,"unpackedSize":1765360,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbVsP7CRA9TVsSAnZWagAAIt8P/0pgRUNyG+MjcjfWeljE\nRs6NnIQ0EfONCVYgcrwDPE5qyyqISmtK6ZMw+4J59KFjFg8Az6luf1mcLDgY\nvR8hroD9Jj02BCayAU5LmaVGYEdt4ZRWoH0/r4U/7ZshWNAoFU/j3N6uxjcr\ns6C9YjoPOZuxsgcwUdmmFkmWQ2nyUytxFBfqa4uCOjngkiK3u3gStcMY4PwR\nCp5vDaCniory5tdODgaYOin5wQAOYbRpE2VuFazVXgC8wmyajLhF6TATtM9x\nBICcGsSkjTuhIceBUEGZkZnTlX63LPZcj3fqUOUgMy6Fep3tk1Bm4+ELI5DF\n9F6+0Y/pt9C9SRHdNjmos89RvqKMpnJg7Ta3p252VJthHkR/7beABtBebuDP\nHUUcGpBLkL1RC3583Tj9P+srTGZ7lGhYUBC4pHJ8cE6T1sVffqeer1kz1/ue\nzWsTWvbDe4hI+GDvmeCr/TGLm+roWJx4HHcjh6UrMCZaqRvVKY41legu7yoI\njlGY9nBdLwd2bOr/3EPuA7OU88Ep7jeaxN3M3tUAiT6eTsOnCU/4m8WZcOOd\ndoRVohrbMAogkhGRkvGNESe2T0azs+AaAnijho1NPx1OcIoMPEKy2q1rZYsW\nTW8LGHKrWj8gUw+95c0I6WUVjWCouzNGHkM7vGlU1e8/gjD7hpn3OCxaeoL4\neK5Q\r\n=yBdO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCtSk+vKHvuBtnJw0nM8Iwnar+hlQrT0IPKWd0XqDy/QIgNcPrOi4ci8D9Rg+TrezbIwgXbsDo7SMZQU87CR7raMo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180724061156_1532412922865_0.6488260400550472"},"_hasShrinkwrap":false},"0.3.2-goal-contributor-names.20180724150634":{"name":"@atomist/sdm","version":"0.3.2-goal-contributor-names.20180724150634","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180724053014","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"111c0eb3ac9615834f19000642f25cb46f7e6587","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-contributor-names.20180724150634","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5i/lythVr3Kq290OwOu+UOsLOq6GPR4t1AkSquavPP3poFpflzy7WYorYo1+7syGP1yJ46U06ccgji+x0kOs2g==","shasum":"d547c23af969d85bb2f13bfdc8b9f65a15195a0a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-contributor-names.20180724150634.tgz","fileCount":795,"unpackedSize":1766883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV0FwCRA9TVsSAnZWagAAJQ4P/RbIklnfvj5d1TC2dKO4\nLgpBNDyl1nwS8PCeLSR/ajf1CiLkQSMYrAZEMFvQJIM8OincrvvsR5Mo/Ul5\n6CtFTESpUOzokSMcoPE/hf7cw9viINm+uM20BGGG0hrf2M0a1jDtV+PfEZYX\n47Evo5T6meK7cUwg0WEQuhR/H5o2aR1V/ndRl9kITOL6NjOzDZRa8KJndj5d\nmUvPTqLjVdtpTkQxqZGfTFPkC1mzoJjtjBUGIWofqC1sT0IpCkTGt23BW6RK\nmOItfFKG5FJpsmBpDtD3VvtbKzlYD2dExaRATbfYFH7CTYscWQZGs7sqJw/t\ntmGenSbI4HKcHlTyoPING6mbPImAerKjSTreQ3OlruruX5NREi5fqQ+wWqWa\n3YK18BgRn0rWVzMGnfVeVvQVWUh5aZA7haoOzUtam1raHAhYGaohLvKOZmXf\nmwaJLmaM+b2xJ4VikURDdMyhn4a29DrB/slPNP5WMZllmkeWZKq7z5pL3jqX\nAWRjHjxqNkFk0cclS9rXN+virM/KBsq3jO3iD9ShK0Uw7SxWyif4j19vPK5U\ntSa7QsDYkKVb+E+alqxHCOSc9orGtaPe3Ow8hlV4H6Loeey+RwExj65I7aFh\nRWPlzILGPOD0NwzJNxctWLOQr788SswNSzEzVsTO1l3OBa/ZndyJSaweM02A\nA73c\r\n=5A1L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHoC4HcWVeb1HvRt9YWq/Dzfm/zZ7WIk4EZ35HwArGwXAiEAiaJTIZ2W7H1rVTq6roZ8uEiVQV1++963J8rib3R+B2w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-contributor-names.20180724150634_1532445039273_0.9271635469893267"},"_hasShrinkwrap":false},"0.3.2-20180724205737":{"name":"@atomist/sdm","version":"0.3.2-20180724205737","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db478a66241c8c6da2f0bb3e4ddd3f65d6239b5b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180724205737","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S0iRspZCXM8seBcaYCMiiSCi3snDEZFfHlk+TwmZeDhx7HTRBsfDyKAGseAFqwvK9PwyaV3ez2cvauzAE01+QQ==","shasum":"8f6406e719622c24a961cfcdaa07ef152a07c558","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180724205737.tgz","fileCount":795,"unpackedSize":1765361,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV5OYCRA9TVsSAnZWagAArCkP/1V2mo8g9C6akOUPRvbM\ng5JHqsyvV05y3oIvxp2di9otPE8hDGbq0Q+ID6Kg7Sgtz25VOC8IYC6557nZ\nz/SM/Nt9zuOMIv8VrX4gj3BxFXdi+oTG5XJTwPSEUwbngzVeSf3/Z6p7pyJj\nAnx4ZoT8qquPg46IgwwkZhyxj3O7ctamR1sIXFKYujE90N0bMImJzulhJL44\nAmRxCB7z93VwTuAnKivp/nVp5pdUygAm0pJldRvh4Ah24kVWy+Desw9ASxJc\nwve482dEOJfizcJIwM6L1uF0fkX2kVQuv2kx9b9rADBHi+jBmdBGXZ8fAnnq\nssAlk3Vy6xUQpwBPNbQW4/YMhlXxHeBe74/sXcP+SDAPlywkcDj1pa7+6xJu\nqqm//7lsHfiETND8lYABxrWvbwPk9cyteK1BgbvXzz+KMrKDK0uR3KZf8Ftg\neISW5vNi1HH8p0TuAatbmGztG4qJlpnJHvir6XjjYMtn0YgY5Jf1Mo2svxr/\n4wZiX37uajR+KjF4gcgROuxA9vo7nHwj8mlwiibs6tt9BKvLq+r6VdCaMOig\n7mDLloEh20+nYmixY1xvrIpNgg5YAq88FEI3ROOlp/9FhxpH3vRFfDrqmN5M\nQNBMp7+hjtwkh1JO+IFN4eNsF56R9sLRI1Ad6LdZUlZvE03xg+oEmcHLf4tR\n16zY\r\n=NR3+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHEePzIV66q1p2NGNFDYFws4/PwVPx3cTV8r6FGSdBmdAiEAtK13fvPgZLD19nm2EOnotF2Z7EeNjqV+3Em2oHS3Img="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180724205737_1532466072339_0.2619958090114509"},"_hasShrinkwrap":false},"0.3.2-project-selector.20180724231905":{"name":"@atomist/sdm","version":"0.3.2-project-selector.20180724231905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2f68e56cc13b23e0db2f8a8f669cc1342cff8be4","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-project-selector.20180724231905","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LVP3syFpbyaFqa6E2+Itu+NOEnIFtigjyNd6NRPnuS2zole90XYCPPje/GUTnZFTvkz2n08Uyc3PUGe//mavQQ==","shasum":"f7e094b415361ac16096d5e1bdef78a832d619b6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-project-selector.20180724231905.tgz","fileCount":799,"unpackedSize":1768230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV7S7CRA9TVsSAnZWagAAMuIP/1enbDm9bZsX1gis9dON\nnqnVgz0QuEWx70GreBtsLMEqhtahGs3cTCI4rEfVac0i2fU1+BEihq5wPeOK\nqtlkOZpE7ZYG3XMyD2tm4rFBtL6foOr4XTh6ILw2HasanNtqor28awFmPOOm\nquYL72Ouh16e8SlKnWQoKkt1L2xl1uRboJcTep9f2nj8bOb46gGJH+otDuMX\nMM3B9qq0uPMVXJwXg3BrTMaXksEv0LI2Mi31f44uQwuX1rKf1TvxXwBUUoWp\n9fbXcYXh+TB55w4UUBXkd6N0cQUFiP0LA+hZKy3Jq+ktf1gGSjtxwh6n4kOk\n7Vf5oQFiNKQzhTBJ0iAQNVw6F90ZcUV3qpFnRZKpNE1ClZ1fuC/E3sdt1Y7c\nrvdKdvTt3od03knsw8XbbZDT2av506sOpRtzhUm+ly4T08DjN+GkUIw3Apxz\nssi3v5N5cUnwmRRL8e2tN6q6H8fG3Yha/ppDq5fM5EmAKB4N2vEaywPHPHGl\naTRF6mQ/YT0xyQulrbsFqegYT9HXzuMyj5ZWSZq+Gpph61PlgMNoKuJa/igx\n6VyQP18ULOJrhyFfW2/yypJw+pGB0JeSe3WjgR534xPoxMsZQtuM1QMkq86s\nu8VsOIY/Uzm6+0yza7BeEVTPDmWO3RTcUxHDFAB6Ns69rFjm3K3lybq01ZUx\n2K5W\r\n=Mc6f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZJknWS6IczNcxUwwmT14S4gyTjbC/qqcAzlk3vVK9oQIhAPc2k3d65ad+78B1bJcEnwq++oPljqjDZgTdUc7maHte"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-project-selector.20180724231905_1532474555731_0.3755727493016463"},"_hasShrinkwrap":false},"0.3.2-20180724232530":{"name":"@atomist/sdm","version":"0.3.2-20180724232530","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"481b259643913c6af0c4d42e6bcadb3013f2eeaa","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180724232530","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pvysxXcXWKwC23KHNx7j+TX/CrgRCE6i11Yenth5qZAXqnkMJSmYJSxq1UaMeQZfTqIaDDYyeyeG5ACd7XHYRg==","shasum":"7433d98653161ad8db2c3d372b23d035397737b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180724232530.tgz","fileCount":795,"unpackedSize":1767324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV7ZECRA9TVsSAnZWagAAvGsP+gLyDeo8v1Kw4pRpshVO\nXg7fw2J+hUr4kzcBeTj4jRfkuFMLzw14w2KBoYEfWj+yqL4w5KNZ6uKGSKBy\neKsQ8ucPH7+F/KeNSU8CeWxG2R4dXOOvsnYsSajdpruZRCqEyl5GIUYoG7hZ\nK8yLPu6AfAtATs06XmS4YL2pzNWaf0VDrfIlOhVmE6O58KooKxabLD1xXO3I\nB7FN9K4xr2xK/lDjGXrC4x3hWBWVE0cccLnkogXpCPEh+vh7Oc7HrsqSsycb\noXICsOtMCjUFCSLenRJ/8F4P6NQIW5ZLCDi9c33XWItbnOdRjL5BJttHe+13\nT/6SUa+/4Zz25beJmrOSMV7vooK03HsKXqToBavmlJyXksxZfnUyvzNJh7kX\nDYSqS1s/fDsH2LCGBj1iAmZS0a6zhsGj6pVw7UjkvKfs6tSmdyjOIWPZbOoc\nlVbZqVBwBtzSmmKhc5S6RiAkgoZyFOYtRL1sIJtjXy5VVplM2E8jgDrREN7/\nHDotUMkL54O3zsgyw1BF1dPsY1grwaPck1qyC+b3/M5qLvIkKaulxki3mF39\n4VGiZiYzeQOcANv8z86ikLmo7omOcVYhB+6B7rCq/c0LIuQmaIEmr9t89oWu\nETfLmypdtC2GESwhE0Db4BIWLGgEzZ2P4yBWp4oJPYfKKpwvA3iClXC8p/Oj\nM5m3\r\n=0FVz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIETSrdl99Xn9r6LtWZwoeCVh8EkY00O4l1xebCtJbWxJAiBrhXdbk+mJAbBAG27MklEVqIfundR3gCBLfYlvAM0XsA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180724232530_1532474948025_0.6239509104755336"},"_hasShrinkwrap":false},"0.3.2-project-selector.20180724233536":{"name":"@atomist/sdm","version":"0.3.2-project-selector.20180724233536","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"340a725f8a9179b0264aa5e0904cd837a9e51daf","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-project-selector.20180724233536","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mIdzWpZU4Wgaua41PEzfFY2LgXaRWYaZzcFy+YTvW13zFd+xAWU0ljKpFw79yZtvpcUd4VS8lw+N2w9PH913aA==","shasum":"7aba9fbe7410776b31ceaf05aecb926284f2613f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-project-selector.20180724233536.tgz","fileCount":799,"unpackedSize":1768287,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV7ilCRA9TVsSAnZWagAAM1cP/RNcXAWFfOui99PK/RYk\nwyYJ/RCfKU+nfL4N2ULcHC0t6ZosHMsoNGEf/c2829ZR+AyqQVpuRxObVU27\nnJ77xR6r6eiC8o/dyT1co//yeaBA4D4lwYfS1R89ko90Xi7rTkrtzrhhv2BT\nu+W/gEX1by89flJqYQjzx1lEKkqatw3qJmdrArhvo+rclMN3NuExvFaftLtd\n2QOMl4K8VOjaMdSKLckpDkM4CMz1y79JrRSwV2v+isesV6e7pFoxZdshm2mu\nZjQB7LI1ZWEMYucuX8cP2jC+U3p435/g72LuPnq/xMCQBWko3HI/RSf31f7U\nYyAODZSEAphRzlDBQ+7+Q1rNeBW29XLr1uSdnWEZDivf5A49mUXCbOJn6zKv\nJwMIENPKY2tYZlTvHzn4DwcEx5E7/IX3AAGTu+9e6SjeVy1djFp2PjplCnrr\n3qsAKw0tTsZ3tuP2nJO7jUShLv1/dbx06304CDwimvUcNwoaUvcL3d0opwa/\nxwtgpxd633pblUJ0YzkJvB6k0tf0CsgLPH4pBPpQKIDLc6xzHTQjXat445GW\nOX5X5tIU5vPon+d7DJn+kAzyiIoQuASpsEjV+CY/TMcYINB250nAYZ76o5qQ\nFj5G8SEG1saoF48O2lx2xyM6WUg3TO0pXvvj6gtmLasdZulFUa5Ro2EkLbuL\nWk46\r\n=Uzf3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCIoR4Rgm7otc9AXQKFokpvvK3FvD8ELsG8y7re3IDueQIgb55zTrE6ibF+zsVlCxZS8m7uW4iU9mb7EyKgUV41GtQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-project-selector.20180724233536_1532475556926_0.2968020033981995"},"_hasShrinkwrap":false},"0.3.2-project-selector.20180725013924":{"name":"@atomist/sdm","version":"0.3.2-project-selector.20180725013924","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9ae849189fff5a03431d080f929850fd89bc4821","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-project-selector.20180725013924","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2eEJvSStThxPgNTHgJ6E7HlQyrWG175i9blrlzQi3n+owQeSSxrVI31aENy/AKOZ8Nj48XsbG6XUMX4aHYdvvQ==","shasum":"3480338951db0f7f81cad80a2097b9bab15ef562","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-project-selector.20180725013924.tgz","fileCount":799,"unpackedSize":1768453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV9WWCRA9TVsSAnZWagAAXgMQAJvjXqg4CXvKoQIqIZz8\nPfir9LHIVFYycMgjwTPQ7JX+9YwQkovnvYvqqcBRBI9fDGzyu+yuuYdxmd6w\nNKkZbKCDAwsv6+4UhAaX2G/A5WQQaEr6GRrYPPfo3Q2J50Yq5/aX64QFHSFC\nO7uE0S/qU+7RTrqxdnGuDKWcvYFGo4t+VSnWOEnNYHlRnl8DSQbgYqI5etqy\n0W5lEDX9rQsiI/YRZ91uRbhPhxnst+6q0E4b7JDgKVKfhk7lm59XWFfz96Gy\nvp+8K/FXaE9NU4p5OUyCeNecwwRVoplhdTCtjO+qEXKmPJy52lg4L3SiF1p8\n3T47fNkOViTzHvYqnko+hers3ACfe8n4KoMweuiwbwN5luzTsftVTg/YD5Bc\nTtO05hGySVp+TM4OQUb2Q6/Fgy6muISxer8qtsbC7zqK5Ael/57LUSDKXc6F\nX4ZTSI9qfrB767vSQo+OvQ7bov8BQihrmBKlpd5VqEg8fy/+KmSeLf+nB5/2\nuHTViouWKWJUiayJThRrAglkE4Kofe5PsOI6Jqb5sBgvCJPDuZH+B1MUv669\njlPtyMUkjdB0nvFcZkFbmpbWL1Wrv4z5SliAnGhgL7Yd3xnvuZK+NyZm53BR\n0IYLsg1a2mEpkfMHyjhLbNG7w9sSuNi+pICY5Mhqh8YsvSn9o1uKTMR40hnC\nzynJ\r\n=GunS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDqGxnSJplHhYRzxeLFynH3avlBsz7ie2KUgG5pwhWWaAiEApUlB07UEV9hSAcrW1+EmYE1kZfFnqsByXirSSgNmknM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-project-selector.20180725013924_1532482966471_0.8518906425091695"},"_hasShrinkwrap":false},"0.3.2-project-selector.20180725023436":{"name":"@atomist/sdm","version":"0.3.2-project-selector.20180725023436","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b77586a608ed018264a7b5fe683f817f0b6d2699","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-project-selector.20180725023436","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5yAkgPeV+HG3TRlX9PNRJAP9DHEqpQE4P+EtW4CLdg5ZYZDlEiH5bXh4rhYtZs+sseln/brZCLoIDF0QoO9PYA==","shasum":"04bee5746f0bd771405ff8720d8954a5692fa4a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-project-selector.20180725023436.tgz","fileCount":799,"unpackedSize":1767617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV+KUCRA9TVsSAnZWagAADNkP/0w3nHGhlHYPD8t/NKY+\nHsS5cUnoaZfC4K2yp82rwhtEbDhsts/47fkBlloj6TDdC4DMGATif/d8D4kG\nZPNRlDP7DgrYzBATpiZDnOJB7zlKtY1/p1FcGxg/2MAfOZU5o1z3LzL5O118\nVjevAvul9UDd3OIafB5gBIgLaVuKBk/nac3SG2LSqGfdn5W00qaYZPwe6h50\nhyHk0k3K4zaNPx15994V4gDaq1hWuDcwgpoDR0ufVQ8EWzSlY0sem9hRwlUo\npJOJ25zFk8/WYQ1zXtVspLLkpHhyNDDjuTBAC6CXu+xjUThymXXyTK45OxuJ\ngaoDu4et36gpk/9xqAR7dF5BPGSGaccLx1Z6EkB6S4KhEOwDCdwYDMHFlwH9\nFw3RQu35lZRmb6I/ZN3OoGAnS78KQvulGZqWSPJW9sb7U9eDACdnw7onM+bY\nXfy689bmsDlAygnY6hbbCb4WNdevdWvsGRHuay3rLgyzTeG1nSuv44Clhgj4\nrLTZoxwqe0xr3D0Xmx7cg7twwZfVQMNCzTi0KFxAiB6NY3xhhox750tIixAC\nskTgjFOAskBMzjmClQ/8bE4CwQaj/1jKytyHiUJRU09HsAopulQ2TevU71gL\nO/JkN/ZwYcgiu2E+mmB2GkUeTG87SdxiSnIDBQUxjPi1d5ilFIZHXJnkHiLO\nTBG+\r\n=sjlr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHwocdsH0CNFBlmpoXwd43qI3gXiyAGXn16c7bYmqMmUAiEA9EMWE/r7TcAxWUMbfNYxrJJ6m+f16tnwi2LJLN4xmik="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-project-selector.20180725023436_1532486292172_0.9016582147925549"},"_hasShrinkwrap":false},"0.3.2-20180725024250":{"name":"@atomist/sdm","version":"0.3.2-20180725024250","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d34ee0371916245cbe26b83f2f645c3a027b5b4c","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180725024250","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XI77XYhbBtme0Cgqjdq3aeyF2aFVAPBFrN9NHK4xP/GRPEGH1CaUZlLRWg+6gNOGb02hzkI3x8Kn98h8t93mFw==","shasum":"47610ab7dc66caad5df61deb6c939c46c30d3885","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180725024250.tgz","fileCount":799,"unpackedSize":1769563,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV+SHCRA9TVsSAnZWagAAsDYP/1T2Rj0XnbJy7NuJ2MD5\nJMwf/RJUKmTXUmUIHse//3AeWFa6vLu8so0sZ3qCXKlOWV5CGwr8CreamtC1\nkDZRYSRja9VTULtaqcMQERSLFj0aqVODIa0rsBRs4vRweofDWDAKBVU+vvpZ\ncAZ9JGvajAoehJKUO7wQOBhyVN/zlpp2aFCGo5lOqQizdSafumxPHSmr1+Mi\nNdmjPCTa4yLbImGcJWctBbzN8qnnFAGwrq2VCRSxrUqom5ShST3C9dIWwdpM\n8BXLkDWMlf8MS/yAbatK1dRUCOXPsZa8vrMlg6Q2MHvACDt9Vw9ZG7EpsCaJ\nFVlERrws1aqQowF+dZ4rFVGHBAZ4vLYM6Ipu1qAIAKgWX70Vu3d5U3NTxKBu\n0QH7jPg5TGJPy7xnuIII69970E72dDIWDBNoReX7/rmOBZmarbCvSYrJL9y7\nMguPY+iKYPgdNMYlxgy6Zypof2iv8SleQL+79rcklNikU8JUQoQmGub9XHDC\nyepplWzxUM615Qj5KZoU6WCrcAV/wvCXhGpj9fbUkar0WmDJSAb1QaTL5pYr\nZ0+UUVQtg4c0up9d9tfZZESGw5RpSaSQMcEjEADr/GLee7cECVaCU14wd06O\nnd4ZmJP5j+QfIYV8lWdWIIGLOXAVmn+ca/xD/zfhuF4PM6qJDNluKa9qVOjb\nEtOv\r\n=RMCF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICFyuOgAw+//GpUfTK3uNGyHzYWiD+usr+UhG1OG1sVjAiEAtH3UIMbe4Rzz5yiKs1OxecmQct7Z/p5CbvfageQzDXE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180725024250_1532486791354_0.8620588866311796"},"_hasShrinkwrap":false},"0.3.2-20180725024434":{"name":"@atomist/sdm","version":"0.3.2-20180725024434","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b0cc0c6d5e25e83d31ce3450d64e12360ee66401","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180725024434","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-k63m64VUQ4PZbmgUS2+/L9CeXSU5LL5lAEWbCL9s+uM1BJoG4Nj6O2dkdhtAp6WD7jaqPc027fKwmS4qDIc6Yw==","shasum":"d0ae8a1ae57005ea5e16578125516e0bb1f090c3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180725024434.tgz","fileCount":799,"unpackedSize":1771063,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbV+TdCRA9TVsSAnZWagAAfmYP/2R+iUGdftKKzIdW9oA/\nmq1drTIkaXb8EG4u8bjxJ19YZaG/XEn46NcXGUgRG4VEGTFnT270Mp4C1z7R\noCECvmTc0E+n+p66Sv+hbmtJ8juJKLWwCRJV4kgz9nok5QMFKT72eLVdQaSb\nknwHQsE+MfPqdoqG8CPCtTwEQ1k1qdqcXeRxIvNCXAwl7f4xzI+VyYiE9tdn\nLeQUCxhPIHCvHI0VLWRYT8cGuCELDZeS2jywX6pOx63lD3pBYorth6WE6nwP\nNPtMjNFPrbPUCBsccTE5YBk5scQ1MnSrsCY+zU+tknl6AKswXXIMmu+NDvUW\nxAWqWRiO12VwXYFG0hzSc8tn6gl1zu8zJZTJ+dATx2cYhI6COKescKb6Cjlu\n5SdfyBTCsSwWRLhruJwCRivfTlWPNZZvwkwGvdEvgtSsm/vIEwiY71h9hBAG\nrWXlLTl80L6cExWi/lY3Z6D2R/T4vDE66LQA/efBd4rutz7JajxsPSvpBlA0\nXvQ+bEEdPLq9OPXSk7iPUljBgyLUG7EwaTJSmAY0YkJf3+9TNiMjMTCUNYys\n1fe7cD9WPu8mykCYw3tfcyehhc0LoPMb2L+5oaF6BJjdTJE1MEFuL6oIohzB\nP394esrcNVTB9RokcMBr5nGiyN5eRmjnHVBSNOEugGOqEURDvrGrWkiNMllM\n7iQk\r\n=PIuE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCTbIE0KNJLUkKLhirZrjDjpe8GJkWGSWjYaqLg65T2yAIhALrShyCbauBoPU3THl5WaP1fK7jrQGrasVSxHKrqdffI"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180725024434_1532486877609_0.9754900898483814"},"_hasShrinkwrap":false},"0.3.2-20180725124734":{"name":"@atomist/sdm","version":"0.3.2-20180725124734","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ee04da08dbbe73b610744aacd6423067044f07e8","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180725124734","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R2Vd+FuQyMcY51ypJlNtdr2wg4ufIRCIQStUWzZ6Fo9g7g60u4mOyFdYSoi8woc77FPKyB/dfb/iWMufPQD01w==","shasum":"539a24a3150862558f83f449c0d86b6f83223682","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180725124734.tgz","fileCount":799,"unpackedSize":1771200,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWHIhCRA9TVsSAnZWagAA10QP/Rg3bXKlSzv/WouXgG65\nKnduuHF4lvv9vkXjAXEloetfIQAqqtD8vQG8XsiodY4ghZYptYcJUvZCVr07\n9GvDP+KBe3yqSsUAoVuk3Kn4f2WD+ebTlTL7yXtz/FYDNU5qvho6b7Bo1EPR\npxdeL/WoJyZylnNY4XuOOKBR+tMxs2IS9kT7/MTP+qAR/VCmyFsIYhrGM2aY\nl+4li17Q10XQMGH0Kst60KifbFK4UpPVFx/dlFW7WATSaytOUO/x9TR3Io+B\nZ3I+Aqlk5C/d6Q9VISUN8uudFvKA/b01t4qJhivkHaQxOBUMa4ckj6gXQIS2\ntgbYnyaVgmtFoUBcPVaQ7Np6PXDqsDakU4cFx+YP+kBpHB2DIP4AssrjkjH5\nbEmRTlhqZU8pMJueeVk9EE3mzboB7Youo+zHmy4ZNSV/sheaqAau+lGjtmEZ\nlDs4k5EqV/KIOTlV02VwtMQU70JQ/cf7ZdKkvmT8T8P2MaLk5orpCDtfmdMo\npG/s2d3ubwp18FydzdQdt4zEAeJ/mHddKFmU00k+sJrMyWN8RhXHcoPcFgkp\nvLSsqV8IALA1bxYez/FbyX3Tmagraf2v4/kSxbh5S3lirP0IdznEomrpIg0V\n31VDjmc6D6GFTIWbtf/nit31Svs2kv+YlYzqvdqKglshlG4TqkQUYC20p2zr\nOBDw\r\n=zcqe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF7ellGw54wf/YzVOcKytM0h/bebPwkfqYIZcz5JXMNHAiAOFIo5+rCz0dk9H2QdBjMOkDbe0Fh3xXPmgjle9h0FkQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180725124734_1532523041498_0.5974673413009925"},"_hasShrinkwrap":false},"0.3.2-20180725140911":{"name":"@atomist/sdm","version":"0.3.2-20180725140911","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b5dbf42a6ad77f7f1a5319ab75c8b36de6efeb43","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180725140911","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NN0ugR8xOzpau3E1kbsd+yVamrObR7GopE3p/81tD9O66E3Q5CUaVNsQ25G6kkNZ8pilBkt1rbErD1sm+hjsmA==","shasum":"b281d66eff3ae187ee0cb359413b32a214efd7f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180725140911.tgz","fileCount":799,"unpackedSize":1771344,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWIVRCRA9TVsSAnZWagAA0kwQAIepB/kwr/nolg+41Bh7\nHuDfj73VSgNDZxnU43Bkaw0oGGIEkSO/8CgwqtPNMvPowUQwI8h1l6GLyn4f\nr64oH69Ai737f/asJox/MdRPtSBwZfE6maxIubwUIan4U+1acEc7FMKxWmti\nTKWZKeiYjw16XmUdXPr2voIAPTYbEbdkSpxgsDCVOWMA/8jKhi3bsqSTURGU\nZUPJTE+ZUvxePYZymsnRALJo8l3RmFQGjg8+f1i9Gxg5m/mM6+yCLE/3cN2m\nVjiCEhU39yhy2/gxWuSVzpzLZkL0cPK9lVwZ4IvBVd0k9mUOEYoXspBHiIKq\nRxtGsPhnXS02SaYRLSIhQuPR0dIpTkawy/NqYowZyzDw1L9xejGCu1GnHADC\nofUetekaW/trxzqOreve6+o1geQi1dieXM1utGZ6QjuEXlVCyb99kTVZQCxM\nmRCz1Q4xOadNutPO9iTI4bGUneXTIq1KR7U0DuZIMctiuJ/wxW5CXvw7Qp2v\n8z3Dxy2VX0zeNxZhAAAGcVRNZ4LiRk9C42MxLYUv8PuvkZD6QqBh09Blncyi\nimrbGsoZ2eTg1dKoXlhHPNZFOilo8XJk9GlSnxEYy3tfR5fon40CJ7O+l4B/\nace3OLL5GBESAfPIKOatQnogvo5x/bRhcfwcLXStGRSvNEO+Z0p1cHKNRQxe\ny2o3\r\n=cCtg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEPqOQEI/SIw8zE7zaCrA3WoDrpVrpPvV8oBKUGVaV11AiAwHhtI/FBW6N1n4h0MVxccbC2sDgAX3UAhGXbvMrYlEQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180725140911_1532527953515_0.12429210411320257"},"_hasShrinkwrap":false},"0.3.2-20180725153700":{"name":"@atomist/sdm","version":"0.3.2-20180725153700","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c5f5d9899badcec079cfdd1eee0300a5d7d9944b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180725153700","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KmhUT1jJ6Ed41rArPjDsRM5jsd1fDlOQmGPxnCNXL7WWIQHEcKv8QNM0VdfuXZETRiOwrHXuzYcbEHI0i1REAw==","shasum":"0e254ccd3d180e968a439755bd3c0d28734659cf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180725153700.tgz","fileCount":799,"unpackedSize":1771364,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWJoGCRA9TVsSAnZWagAAK2wQAJQUJaeRupQc5QaV7Mhl\nplWxXOS6zwDOReDBefPPH/fkGR+b4r9GOKysv+DFoIbFLnn9xI0x5I7pLeqy\nUOxW4HNcjNsD41omxULc/UKo8jz0O3d6ovnJ2PNceD/aWlplueOqPr1iR3zE\nq8WkvV9tDW/eL7KduvVxoZeg6B61STvWi//L28cvjDi+aUQ8MwFca/tfC42E\nnamnLSD15t62oFEmdaccaWEcjKPR3aps203MmB1eAExEHRaBrDAAvXwK22wj\nY26nBtxhRI0LVxzoCKl09pLxMHQHDEk+b+WxwxosfvjVEaZrUcIh/6Ja7w+4\nDTg0clfhu1XRy0PHhJ4jE30bS7ccLtfhAjKH6bAyCJMrVAB6ysPFP5IJ16wY\nqz42gPUHD5R3rAl/NAD2bWZTyoalAZuQVg7Vce8OqZjnkX1eRRiILshN2/on\nBbKXQ8Zmd8nZ3HDxO8T9xd8gWwyx16M1hfPf0XXDByWOZvArHC+h8pojRGK1\nSsyve63IN23ZkzF/6YfWlcWstZqAHUjaNBmVsvZRMJOSRRFHSb8bwQpXypWJ\noEv6LPopNkRNC4t2cgmgX7RlJ5vKNeAHLeByp9JEXIxDEcMX9Ud2gGqOFRix\nV7y7TXvx3SwAsPejyySez6P1aCmT5fi5ISdRje64nDcfPpkokz0zrLKfOIQn\ndoyd\r\n=qEeI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEIpOJNYd+XXXUXVEmL30m7RCuZhuICNxJzROJlQp5MQAiBS/m3Y2MP7SfOXgpZ9BXQzdG+bSkGF1sd4KITHrxgs+A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180725153700_1532533254079_0.20781404939637738"},"_hasShrinkwrap":false},"0.3.2-20180726115716":{"name":"@atomist/sdm","version":"0.3.2-20180726115716","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e5372ad94f7474d1591b7a2d887dcb5c2124451d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180726115716","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BieNnNE4NBqSYLp9b5u8/y+jKktpIFcU1JJ8ExsGj+DB5u2xK8v+eERHwJy9oQIBtQWJ1PLCQqihBNR+SRkaUw==","shasum":"65a05dcb7450768a0831e421ef458bb10b93dc67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180726115716.tgz","fileCount":799,"unpackedSize":1772119,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWbmZCRA9TVsSAnZWagAAvfcP/RZdE0ThYHpPLMHNLKtR\npt/O+GogGYCVu9zs5AD4UuOvoWRva4pypyXscpPWIi8Fz6tlDP1NDIlzF8Xw\nyy51Qg+MzqYSvYP2igqJ9qEsGm7dtZGMb6M8L5g/pNGGyWynWeYoMdWvdo16\nzfqDoFrMkR6r9HiJzJSTDPX6RYrlw2nCxAvifI+8byqFDVri0tmWgYD23t77\nyHoBLngm9/BnGO2TIEYcHeLXFiSmXCo+PNeJNAgZ2OToNfxJCVdEVZg5XVih\nTE5iR3Oc9jkhGz9lAVvHn6W5PjDBL7nLuw2bqMRHYAM/o/MibVQD03Ls320e\nR+/h1I4e5mYSveRlDPppfgaJFpG8mNe4uEbhP8LlzznzW6mDiPEX/8tWeiQ4\nWigNfz9JjjZL/7udXOMPjMQS3u0v8RkXit0LYk2flKmik4otIC6QOWI8bPfj\nMYVlvjI/seUN49ulEtJRWhuWXbTM/S5JVXcvkvhBUMIhY6DBm0gJM3sQo3Gh\nMt9PuqhW2gJ+xjxW1O9lczLBaaFkHfOGNiTrDj5prjtyYywbT2YHLvFBQ85e\nW7IthzoVQswi32uYkV5b9jvBt883+0USnBCf2gBHmmYb7timUtGz7FwYFR/g\n2eYwpSO9VqlKCvWBbi3bOmBwkB9eh3422aY36Ei1/csocYf8YMvGeF31f571\nTOlA\r\n=795/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHL0H2WLt61Si0jGul6CjwOAZM8+zbJcMZXNhKI8iulgAiEA8oszjyTrYOVRxw2l3F/9uB2DbjcSufx40/eAynejwnE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180726115716_1532606873210_0.4597993919073755"},"_hasShrinkwrap":false},"0.3.2-20180726152101":{"name":"@atomist/sdm","version":"0.3.2-20180726152101","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ccbad1069dfd92796a912fe7d11c988a9e372fc9","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180726152101","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ad48qI1eTYL65tkezQDiFRq42VtQymG4M6rgg9BOhIKrxqBsCq4S2C9SU9JuM0QWjap5yyIfiGI5v9Hp2Jw1UQ==","shasum":"be570d0e08771d093f48ea86e21884b9ff15f574","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180726152101.tgz","fileCount":799,"unpackedSize":1774325,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWee7CRA9TVsSAnZWagAAWUYP/iFjUznhBv8weG8kvK5e\nhHcU3yoRPox4n2SNzH4tOUll+8YshYusct15nwYJwuD0thbeRK0ayEWu84cP\nugGqOs6AMyk30Nd4vCh4yyoceelDIS3eX5YMLkLnZ/JaBeuop3p55FWTpXIt\nNeQ4qB6tZazJr9jcT2b9dH+4jR5SQRQ7sw8qImlqVE7ZzgDhcJEMI/zm+yzQ\nYgzE4I2WYkDqKFNqAQyPmQAqlenxKcQvIl8bOO2EmQWtTwmXx1kdXy0169l/\nnyt+N+TjmBwY2oOJOqSpNU4TyfaIaWUH+aH3g5ytJmJY9AbfSjG4gQg4CTFi\nV/HHn7Qwi3Pev0cmCrjqU/YAsYdxgL2w989Lb8n7CIZn7ueGG0/Qvf/K0A92\n4bbdgCtZp3ocgINdnQm7bqKd9/B6nPbSye4e5SRPlUp6HNo4EMBmxwmfPvtR\nwx7NBQYH8pqRjC4pLOV3zjHXs1VaVTU/xIZ9n71xBNYcrOKn+vZU2sN105Xs\nwfhm6PR7z+Z6AArDMpCxupTJhBuOrDCIqLn5xysE2VwtMYEjiLN+ofZ76B9q\n09ejlRlW3HGkXDeOHahI+ZHhWmo0I5tHOBHmcMlXsck2FzJwjDaPUEcDGtx/\nBrapoyJxuCmUd1GOC1OvE6cK/0itrLjJPX4faELuZpyL/F2cD+LcthSPLz3S\nSYdr\r\n=Hmr0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDiDf/Uxo1/TFdNK7X2VIax7H/YEdYpvS7r8Y+aeL8fJAIhAJFMJKatJYERmoEo5lsXlxKy4Q0gYI9h5pcAHsFofLBp"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180726152101_1532618683180_0.728571879129277"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180726204640":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180726204640","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d85affef520f59cae4de6d99dfe31bae82cc31fa","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180726204640","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kb2D5MM7AKFGPivqpovklHqIZB3eozSmawpLBp+84sBFGwICfDfrCEpCHJHKvwTIi8h1taUs1RBBpVQRxp+OoA==","shasum":"745c49cbae7020e3feece03929cec187d5ab11d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180726204640.tgz","fileCount":803,"unpackedSize":1778912,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWjPvCRA9TVsSAnZWagAAh/QP+gJfW/1Jw4d7pznJi7+T\nqaI+Po2XZu04nqPn+V44U3rvPFsyjAd/bjybUbRodMBscxhX69cvWkSBzwcL\nwEBMFnWTxCafznHhTjjZcplobpnffydrRe9QriXzzUlO+W3GZK5anmQFyX9H\nv6v3b4Hl0Qb9ZcjxmRGbO4NWN9zPV7OXjORTn5sW5rpb4mYnpTBPkp23eGKy\naLknIwp4kRBfAbkgRmvSVuCGWAsOW08oslHvk3T+bGBV42haeRaQyxLyqek5\ncUiFmlSKL+g2K36IEQ0J7m4IMqXe6GAJ8qG88PoFD4w5DzfDUlM8cmzAaO5F\nbZ6P7y+Am5q/eVTJyccCGrNOJ9lmokaPWh6rpEdh/LFjBAql19EwKnhKoV/t\n0FuMxDLvOufRCkoI/+ki+rvS8iClTvvQzo26dz2NojifT43uSjEwDvx9OvLR\nbkKpfeWR/v1gzCi8qcQUGXo7AgkSHaAx/XqQEPdcDJzXjkgUfGuzfNktUTyO\nVurMi3fY0uNTwuy49cn/y8ODTCo99AxyMs56rKsle8AS4Q20UkcY/xVFKciL\nuBTwmkSt8h9V/YWIBrCGsqEEgg07uIyMOzf9uHI233DR0GnRY6tcagtR6omx\nPshj6hgR2Tk57/fyln4EarGK+hLmchIxC238iA6Qp1ktXtMyOm2OFod5CeHC\n7pXi\r\n=xxBP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJ4ooa1SdUqx9ugScm1LGXY9Z9WHNChbGfVaPRQJe0IAIhALuFg3JG7NM6P7Pfs8idYNsDCqdR5LqYXsbWmR7REdlV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180726204640_1532638190866_0.5824391881592903"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180726213627":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180726213627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"987272c621795bbafa83650f0cea70b6b3eb4aae","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180726213627","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/r5JBPGza7QCBRKHf2raM8HQcA0+jcI4IVS14uBdAmH0XgE+YvAXxiYq7IEGFGjSq3O5nxwKm8QyQttYY8e3BQ==","shasum":"065513d690ad79e5c9ab5ab917ab508c928170d3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180726213627.tgz","fileCount":803,"unpackedSize":1782068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWj+kCRA9TVsSAnZWagAAizQP/3TnHasbFM7KPeL7UvrL\nKJB4R69/uFiFDuuZB0SeHgB+a3aEttDpPkPXrsyFcYGDOF8TlFHsSdTLKRCq\ne0ALHZIuvfBvUOm1sOLl2NxT3eQgiiw9AyV0L6hhNtxnA/RJls+F/nq4tjoU\n9AofUYqqS/CNdG2oYKuBHsywTvAKcqMQsgbX3fxz7nB+/QhtYdaFWGyNsSPZ\n1A1OtHBGfNjrQVMtS9990YeOfwhcEH3AZ9yipuWwt7p/CJYXAnvkFdqII6lP\nAAvGYtjbifHMmzBdkrmDbNhBodZX8yinC36j97aI+SgFOLyVdXf3klrY8yds\nPn7MN+Jcocl5y3T8Kp+uErj+D0+/xSgKghqTS8F/VydAzC5kgzOuBFO/WrG/\ncfCnVx+eZywJWZczlvnBOxmycsLg6G1v5+vgj51kLrs7GHUQYkKv8RI7ggxe\nfChjECS5lcrR3u+nnVbD7nh4GzsvEzcL8nyNSuXNIpR2UsoVGwxChp83Xj2Q\nsRRmJAXBxFi6zeyplTzL0DjdO37pUB8JkJmYUQUIZ8/WIKxSDoDByOmYue2E\n+yyQN6pa2yVeNV9HSO6MwzO/fCOYm7ZE1nzWsfaHpRLVWgb6MUWWggW26S+R\ngRqceHNBT39i1aYau8PSJeEYOS+znSqFV5TL4pOLChSU0JPAv0GkR2h3kXBd\n5YFp\r\n=lr9+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHNIYvxlIXBxbG9SPwpCfPFDFaPGkEMaaOMymofqzNpaAiA9nme3Z9HOu+IxRvEBMLgNN2jQOwmZrzTU6npTkVxiEQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180726213627_1532641188486_0.9463915125382216"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180726220957":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180726220957","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ba61890815b029a502ca16398a6670242df0e15e","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180726220957","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+hVjywv4uMprTuf8Bd3rlFQeUlrNh/2uErVlgkLtrUd/DiJt85lmTJw5NLJxRQhQmzLblPnTlm9vt5Sb43BXLQ==","shasum":"46429c3a88ab742372492e7aaa27e41a352fbb52","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180726220957.tgz","fileCount":808,"unpackedSize":1788234,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWkd4CRA9TVsSAnZWagAA4XgP/1hPN8Z8kgCdlAsh9/zZ\nE9ehXUe3sPgsbNQzbSAhBmkBwwjNPGhDuiQxMJBsuoV9wNZh+QBs2saXgYhw\nVKXMLRZ1UdeP0Dd0SIsobFC0jLRVxcABiU8vrhBdHuE/PZ7mu8xnsj5MJvfw\niz2+McKI8lG1PGcwfwdU4h2JBFLPkilSEKarvlEkDLSD7Y5ObAkCLAp2yzKX\n7NJBVyiOtqbRvmiogDx/SiYfZEezmP/u/Gf7RCJJck3dNMrZmdmoXyZE8Zqh\nwoaMMgxT2QD4dCPYg6g/iARERrAWPcvHX8Z1N6Y9xQr9w2/Pcc7UlPSmIEv2\naGZPNt10ya0F82CdCBD4aqk62EvL5CgEVzOl3RIFesQFOzAMOAu9NytQiBwo\n11RQ232wj7yrUgvnEO1NhQaoHd7mVF7wPnE2IRvy2UnVDVt7yM/V4Cjuimzj\nrhcGCe+GpsnsKDEi7Dt2onvrc9nt4lJbLphb4NAmawQEkDGokm5GohLoAweS\nLrDeshpCNEzgebZa+0BaRdcZa3+MNWNCaEVOxNYpiKNpex5jyqyrBJiaftjv\nlkMZZZr2D0mS7pBSz8OQb6wMLknoSsQ+TgebedLoXeNCNjhs7zZpeZocPAmf\ng1zP65/z3wfqr8QOaXDfbJyISWV/5z97P9p18vRaW+5qxnkWl70YaJPbhcNX\nofSh\r\n=wNln\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICbiZw6EkBhILSxrMYXCghAUW3jRY6tn7nJ/ZFvK16PbAiEAskC1nhOZSsn7OIH7u8EkTUPveSseHTfG34gFcQAY+JE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180726220957_1532643192603_0.27147223728645287"},"_hasShrinkwrap":false},"0.3.2-20180727002548":{"name":"@atomist/sdm","version":"0.3.2-20180727002548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5f0a7d0e75500e8899b06c0dd298be8835eccc52","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727002548","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4S+BGkKpB0oW7QrUB+albC8Z/tLextYwe8+GMwkV6f/T4rmsRU5yDdOCp7WaNGyPoIctWEBJYYe5lnpydpHT0g==","shasum":"251bbf36da3e224c3a48d2b17aada5329070c679","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727002548.tgz","fileCount":799,"unpackedSize":1774343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWmdbCRA9TVsSAnZWagAAtS0QAIbmvLhat4N82Md1lEdY\nsImtb6ut70m4QufVclkp70YeJ3i6yJc69OjfD0+1A545D7ofE1lrIMg2FPvq\nOpIDUVPaWTgW2J/gW5nKBrWpm1Rtl5Z0gvx3AJvbHCgtSz31dit410LqHnxz\nw2pwjU/iePi+Emd9VpnaaVl2MIdPgt8SF0C6jgODb2WkUsFnCMfuR9fD7hTN\nnN8NI+2UGBgQYER+9NPLwJzL75N02bZ+saT/E6ReQlzgNb0Mmm4WS8b0R4TQ\n8g+kSps1vdzEsB/CUpHV3mKwhFXJ5B4dYY2hejo6xnl1p/sPJ8/Pugc41QU0\nMS0SPYraW0/Fb7ZQSkxsOukNfMb5RR/1WF+HMffqJniVvV+vjVo4aLJ7+v85\noN+LNrZX3kS5ixNIBiTyTQKh53NBDrgVvvsLsXMbNBpZWGMM2qTnpW16Nm08\ncqTbNp36EWfpA0+RJ9xUpSJTDkB0QkqIzHqvcDVCSDurqupN5pXTnN3GcpeZ\n5DTrNX4xzpGRZSLwMW9xPNUipOLtZSJGLIH+eGFc+A8plwSJr+SHytDg9lgm\nUob5QzcDQ2CmVU/LbPSc9TrHJlDMd9jPDnDdXtNLMXH+9G6ckjUQLjpHaH7v\nbQ7qYZnkWiTkfmm6LwKy+a0YIMSw20W73pvQUev+87zzQRltXIy4USnHu5au\nh6HX\r\n=Ymn+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHM2/iwbwBreDxIstYo9Lsu9mv8M8TpP3vCjea4pQB1iAiBj/SMjiGPxakYG/zCc/ilKdhH6gY3j1wTI2pYz5kIPbQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727002548_1532651354866_0.5491370348482161"},"_hasShrinkwrap":false},"0.3.2-goal-status.20180727014407":{"name":"@atomist/sdm","version":"0.3.2-goal-status.20180727014407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"efff6048760bd1b8652753fa5f309ab77327d5e0","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-status.20180727014407","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7DYg1VVinXlQz5RcW66+zpJSSZQJ0eauHCXrQWc9tQzao5ycT6JoE7uRau2zoUxpFAxXT71T6qPvVHAYgoYR8A==","shasum":"726bf6f49c1260380f709b4a226b19e7707cfdfe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-status.20180727014407.tgz","fileCount":803,"unpackedSize":1777091,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWnmMCRA9TVsSAnZWagAAYVsP/3bXj2NGw9E2dYFukonT\nqEdXU3mLIRLi4hAdEcBgoSLEIvTUc+YZAZ72aFTdxyVIKD/1gKirEkZC3Bos\nRiX50hYgJvKe+7ziS203yhQfuc1xiJYnRVmw1ASYXnWg1K/TLZTUKlcPN/8K\nFca+jTCrCOhYqtJYvGuNQGwmkDjccIw0MQdF/4IYHtz0xLeKFzovIdNxy++T\nSviv4oiCgp6jHOoCUlZHjTPPrMBavoIKr+FD1LXhwxXGeGDzQA3Px29WOJ2+\nzHgek9WZA2U9hrYWq69QPnzYfpd3Fy4E0b1cbOAtjYQwiNRfmN5tWiD06Qwd\nRu/dAvSntHdK60kho4R89DaWI5AmZKYkqUjfQwsJOqNfkUT57SHUIUT/GMAA\n7XyaVL4bcxhy1TosSddHSElZ+3fJfwCwHC3vm3aSitsHYhn/jo0Lv0w/Nerv\nQC1XmvGCCZR20ETXVYbAy7awlonC9SUSL9bMc8dvgSUh00rArxOjhlE9yx1q\nZuZaaCgsnDv8eHKVE9miYfozvm4oZeYu6lLfu2caU9PJLmg+hNsp2kdif8vs\nIN+M14BPN9ZfqpuP9MH0UfGtKOQhUmewikivHNqhqq0xEltXdm1IVVy1sm5l\nDvA5WDUukwsXkPCkHT+XWASiQE//o+2HH65I69RNRnPYaM6ialW09PcVPUWK\ndr/H\r\n=wLTp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZIp6W1GTem0ustSpBDkWfouMsrwno8qn+mAZmIMqU8QIgaguyX2VcG283ovpiqbjJ9R53CD2hbGMRLN7/6t4wT+Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-status.20180727014407_1532656012064_0.04875991174007188"},"_hasShrinkwrap":false},"0.3.2-goal-status.20180727015906":{"name":"@atomist/sdm","version":"0.3.2-goal-status.20180727015906","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"43e8f733527e4985b881b24a8ed023bd31c31fca","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-status.20180727015906","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l/JJygZ99228Sf5Hqed3r3PhPOYdpNtv7oCpwT85CNaZyoHUa4Sle8sHCs5ch6zBq9T3SS5W3Td0YMEl9FkzHg==","shasum":"c85cbaaa9385aad88e03830568c7c3b9b625bf77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-status.20180727015906.tgz","fileCount":807,"unpackedSize":1779321,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWn1bCRA9TVsSAnZWagAAK3wP/RdsFKn3jtHt6viScP3/\nA30bvTPd8pFzg33pt5guS+wDGJzpIYZzfI72YfvjLuBq8LX8G7i/DcCXXleJ\naT9WvDKBz19cn6pWUmMSZg+2y8dRxnJX5SNwikhawQ1wOXuPFYwMCaLT5DMU\nyNuu6bvMwhDGDnIskkgDlpZ339jrMrQQS4nu5aXw5YQkVpWrL0xMApCoDWlM\ndHlCkQJq3cfbPuHHKQTa6mq19lCzoF+dGXPdDfYqfP6/6J8PRLslcGX+VhmC\nfvgTYZlqTH2iCgvutXpRvv0Hjqx94nPkFNwOFn3mMZVoBvMOMTE3qzGhmkvq\nUOHm2ng7tBgBCQCcoj0yMQB5Swh/zr8KyQxWcEXfqEQHOiMSS70OH9dDavaG\ng+gVhzo1gZFY0ti9bZOL48TlEZju74aQh8jrxU/Jx5LUtUv6PxoQaFU4MTLr\njjiUBtRg+hlh8Xmaw2w8njk1jzfYkeyFQdUMV2RIYCQLj4KLwqwnoChqbOYU\nAjPvYZ99Iti0w8ISIZcEUJczI6cnvDkVR1NXCYvsaektBTV731pvIzVHcvUe\nu7nKQaMKG3NTMbGPExp3X2vzf3N6mjzGJQDlq3nmVPwkjnp9baMXJS+w3ob9\nAlqWd+vcIzQGNyK7kaVeFEfKz7c70RlyljPhtZc81lroOKMpwTyBS64RNFhF\ndcLS\r\n=HRw1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCa4Bh362OqCzV2Se16xtWAnTSqInNBto9mz9ECJAhP5gIhAJfmz6UZfhdwxuKZI7iwrMroPgmbNtKfIxceIkdX+srf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-status.20180727015906_1532656987400_0.4614135240517532"},"_hasShrinkwrap":false},"0.3.2-goal-status.20180727020042":{"name":"@atomist/sdm","version":"0.3.2-goal-status.20180727020042","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"faee68274ffdd42ef32bb95bac42126408ebde6b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-status.20180727020042","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LzI5IjD2Ve5cCGkdoU4m9fgv14zngKC/5OOjUg07CGieTHIjVsYNFSQkweJvGLB0p53nfS+R/+sTU4LvqGTMgw==","shasum":"9279050018616e8f67f6774730fbe953ca1ca14f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-status.20180727020042.tgz","fileCount":807,"unpackedSize":1780097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWn2VCRA9TVsSAnZWagAAxQcP/RMyWXAG2a1jfcLX3ljq\nTI4xJiBX39cIVL7BI8WeRCDWJQ6KNl6KPFp2RkFLbRdOmmnbQIP5B85GDyHu\nPViGD9AP3PcLEg31fIKu8XM+9MowO8CdUiJ6x4OGdHpm34wwXGnzbBPD5yV1\n5mj7mqq+MEACZsoxalSGVHi8QJoVxRPRKrjK3bzXX93FJnmDM4sTj2gWDN2N\n1uKX9ppCnJKwMfGWBIgo95+K7anIdnhpmqYhVOqwE+MIaiCdEQjRpCBwIKkk\noN/TJM4GQbko1kgvsy+Ci84ElSoAbGFIx3QCZSWQEAqSRME7qecpg0Xfbija\nBc/kT37LJy5ml2MBFV2U1gYPs0VUmf8+2EE8HbnJPfy1dK4ERDyY0fuk50LC\neC8phVZFn12o+7Xr4ow0ZT5jEzr6KF1/Qo4bA0Svmg0D1BQXMzBq2C8ZVXXb\nX2XbUvqAdvy/9kL8iQeS6XLUzMSxc7osC07n8SugzvN1997piYE92dDIgXP3\nnuN3oZ2+S+4tXyhlF3Zf9Fsh/Xq/cfDxPtBenQg2byE7r7YNkLooKSCjneZp\n4pYe+rj5D6YXpX65t+th/cFivW7lrK2s1yZOoHP1kRwwhSl7WeWhxk5eZPpT\ndo93j3KrUMjs5FhYG5C3AuNzWQf6lmyGG+4xt+HIf4N6QrLPMUBiDCaHWv0/\n1FaF\r\n=TXlD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCL9rO+en0V596rUBEdTEmb75isdAG6QHpnkHErj6Hx2wIhAMby0anq1Fy+sjUlcIn3sYVu3K5UW2F2HYebtmxTmiJ6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-status.20180727020042_1532657044960_0.07997655885291088"},"_hasShrinkwrap":false},"0.3.2-goal-status.20180727023826":{"name":"@atomist/sdm","version":"0.3.2-goal-status.20180727023826","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"04e49966501541fba283384bfe417d81091f4498","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-status.20180727023826","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UabouZFA5y+dRZOo4fHmz0N+oWS1jPrrOkNDiBROgCOmAdGigxmhIn2JQ18y952+W6RXQx+5954hZrtUNfVsKg==","shasum":"da094ed261da476c1ab87bd77cebec879b7b57a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-status.20180727023826.tgz","fileCount":807,"unpackedSize":1780308,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWoZGCRA9TVsSAnZWagAALcgP/36mOSYY1lJQiV6MRzBj\nGrcAGBmVkD+UP3Sl+J6Nf51lB4g/lHDxUtOMWhlf2ri9kITVKdW7DQBRMi5d\nQ31YFU2Iwax+CTnYPNpltE9oWmIo3abghzYPpQcOZguhazO83FSqj8d8hqUf\n1/70Lrj7lGEqZbxZwQE60GJmHBo5/b1lRKz4++qLEfDs+oU03dNMunKHAxIB\nIqfP/4f5RZ2DUyUat1ite/4YzTnGO/5cYbOpDWmP+s/BlJku/HAtddJCxXWU\nOaKXe/WZr0EQO7IIC1PCPydrANqVvLupe6/N6psTBXIjjcXFGpFrA0s3No4e\nsUATpnDQWcTXWCw0gG93PHgwfP2LLKPQgNNH4l5WfK68ihT68iEId8ZdZ/YL\nbAJXPtazIlGHiFJJSviuT+h0XOf3aPQ1odUJLvgrMH5op7oD5+VqHmJysaML\n+C1lpc/fCoxg4dKCa8Hf+jUxJmWAZIOiuMzOj+fSLD9RuSsBHQv1fu8BY0k/\n914AchaGawaUSFXohPcSOKJ2KfUEyUN7hEmaWh5+D1q8drRkPn9fLHS0h9jl\nyrtqsCAczbwDXiKvhNhmQ9TJkON7eU9qC+KI+uz4yxlz1P2s4R/huiZ2+cYj\neenWP19rdi9wfpqS7toa55qaDZJTfK4v6ga+RpiNrKFX5/gGG/vNrTgihYO+\nv7GH\r\n=Df9f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHxhlt6mxgTH21AB2I04w7hFjSh4vdHR6dzlK5LREsSvAiEA/IKtmR2XI2+0slE8jeAd8WqnNFxof2EBMqV2ORQNiaY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-status.20180727023826_1532659270566_0.3839572859622815"},"_hasShrinkwrap":false},"0.3.2-20180727025011":{"name":"@atomist/sdm","version":"0.3.2-20180727025011","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4ead15793880009df5d50a9dd292d7c9f28cf091","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727025011","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ed6O5q8t03gLqXsPUcem+GTKfYRzYhEe4OnRBtfdVETqCRtP+e4YGct9xYqIQUjl/jVCnPdDBjcLNgN4+ouoQA==","shasum":"d9fdd8ae9fca31405a3ef85a7fbf2bcc36e6fe7e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727025011.tgz","fileCount":807,"unpackedSize":1780296,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWolCCRA9TVsSAnZWagAA8SwP/3WZr6NMxre1+91sPpEz\nzZSURQfHQRR2M9MOnBKY+jVv/fB+eKM23Wo0iUfzd6/A3z4bqoAC/jQBhu36\n7A7t2MGbtnsXDaMylq50T2edfJTmScjXyC8lbOWaFEahgH5JJLGHky+SXCv2\nyXNrcH9q6Dk4zW3zWbAN2PUgnwhlN9NV2tvoa0+eMxk3c5Iucz7J6GeTj0BP\nYdTp5Le+EoyYjQ5XoWCv2KGU/yo6lPK28N8Wp5glrtFC7Z3CbAlRW08U/uIY\njBzZhEHCaF+c3xaRsr2jlRBOQsJ1mdjYfuUA58MxwXw0oAKDYM+R5CtkM6AD\nzHe1midyccUjQdKq5ZY9VfToHVDtBwfHc+CZrW2k0JvzOYxs00hWabKpWwPT\nnZfVzzZeFjPFgiS5mozQaZHVA5AkM33Th34ZLruSXGc4IUtk5snr5yNPx45n\np10UW590T+iiWhfS11tJBc5l2yzVwy8WzMVvElBPbbsyThitsqYpswJwMPh/\nvKGqQ67dvJAWEIiWf/2s14++WiebKpb9e0AD457gmTIvAiN8mJTUW0jJTLfo\nRzpF4GIidQizmdG1her2PpLVk5I0WhhkvLARNjEH3awLQ3z37RuHg/2NtUU/\n0z2iEeHIBvszt645o9LEZcgUwGlnv2ImD5RUMT5RgarEKvxR6FFPFjqkVvcd\nQC3H\r\n=dYmE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDIrBxRr/0jeINeM2y8uRqeaTkyvDOKpBtHwW18GjkMAIhAKL2bRqgO9AR+VFO7GfSOX9dmHm9InXmb4++oSg4cR2s"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727025011_1532660033722_0.6259192682293195"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180727043112":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180727043112","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0535092f281279e4fd1459bd65be2a51b453e711","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180727043112","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ye2DfV70q9ZzuooBZNp9xV2xnqdwBAwXpmIl0jDMRkLomMGeUqEYHDm0SVmu5lobKun/BqG1KXrE2XUAnJALhg==","shasum":"8170f2af3588e3f1e46c61da5b1cf469481d62bd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180727043112.tgz","fileCount":816,"unpackedSize":1794205,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWqCvCRA9TVsSAnZWagAA9dsP/29+KoW7RDpjRAP/bpuK\nP9uDrFoYx19Or8PXb3q3veRcsHmK6RwUb9igVQkYQTUn6iX9TXi5D2l9JlbP\ni0YYoMFGsoUgB3S1cDjwBe1h649V1WcN35rvF8h5r7eY4OrdH+hzMRLe99d9\nEh3tZZHSl4WSHr4byzE1MqGuWY5ZDxDqB9Zj9DR+hED77q0Phcv/IxHJZ7Yt\n9XceUqqVOc2moqNVtzd3LpGFK9UW6DroNJlznEtjlw/pApyoQrAJVWxFFAOo\nu2vLFstacePzWGdL3kc4poUvQxbC1cXZRfQ8X+9UuPE0hB6FsI9LTPVfpmwu\ngD9VS5AlU0am4uEcaYoJye42PwOTu5dmW0StVcpkak4GunLoAVhBrPiTMBv6\ntbRznSn8xA9S4uLGPWXp6+hE5ZYBULp8qwBgdqWIIHfqZxjwCXd7qiogwt3H\nSMHZOlK3sKkSsoYY6ipti56IocH9cV8nSPgtz+2ULNes9a2FIN6AKxhW/LBK\ns6CnECc3C5TmWIVid+9mDICBXbM2RowGTbjOV0yyjB2o/jhYljmk/IOD/6bn\n4SqO577d3iNflVuu0IlWPRm3QA/kR6D6+k3j4b6DT5EzHKxC/fZE1c/COLYk\nhonr285Fy4SuMUcDLVv0Ex1Kky8YlZ3DrVZ+Uso9xmrjpCdF1s75ZLrtV4KR\nuehc\r\n=sNln\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEdSu0tgO+yziax3akka/SfHyKMPSMa2IYL8EtzXgVc1AiEA03vLq75uKDeweL+ZbLE4aa48T287nqvCmI3qKm/PDsQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180727043112_1532666030879_0.0005096606116401503"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180727053802":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180727053802","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"19c92517cf9ea0bac4e7e9f8cf2c3340642efdce","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180727053802","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bwHuAf2B2BC1LBM+izfrXDVABsMdcAMNTPRJWcupSR1/maMXTDy6BCGbioCIa92GbvDD9Kr19ZTGDwdehnZ7Ng==","shasum":"11204734bfcfe2ee6ab5b680d18ac36c7e67bc05","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180727053802.tgz","fileCount":816,"unpackedSize":1794209,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWrB5CRA9TVsSAnZWagAAytoP+wYQyVYxbsPjdL9r3U5J\nVoI4FHwuW5BCzhPAPf3VR07tTv50zP7fc5Z4GMxZIhTKSBAZOdpAR60FYUZ3\n+JC76KAbiHLa0xQVDcTfftJSjTZxEsVMqF2Lx5ksAuITu8iTMiEGg88siLgW\nrmhv3B0aEjMWMUChPuIp9qqPYy3bf0mcoDCaz11GtmKl2Ksb9npMssqsnfM4\n4lQfJR3ddRHNs66RP3D1O/8tNrWUza7ptdqwbw7pHkzL1Zk3+X+AOyPvshpu\nBUu3BaCY1J2HQR5phuvnSQASjC71g4OSBDsItkYofpORYuRA0rPidxN23Zck\nESQnCjcQOJyjplhWiQ+HfzcRiSy0CG3RVPa+yOEClFODeyIX3U/QEFaBO891\nUamC7W0IzUs1DSTQAQJGVycKkISCt1bU6pkJpnf664VimCNXgjek5q/C04p3\nR/HjQEhhfIQ9zRV7+u6yhXpSEkZiQYiGpdXic5qE727x7gP3biTZ2rbzQaA3\nP0LfAaSfqedK09M9ITSoEkLrq1aIJrNQDn1dvy65pALSQcOX+uHtpU6Ee4xo\nobeZ3LE6wVh4BrHLh0WJOrFCryaMhc4fRZMuX3wLDsdal7c7LW7jIuYKEoa/\nizNXZxzuTOkcyZ/RsRocvJLQ9XwDz0tl0j9wpUyk7OuvUseasRzbHoBZnqQf\ncFRR\r\n=/iFd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICK77veHtmc1ZbDkHHYGh+6awYKhqWVJc6V2N3W4eKlxAiAmrWzz8rYP4z+KFJUm5cx+EJP3V/0xGywWVABeTcZvxA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180727053802_1532670073835_0.06942477685971538"},"_hasShrinkwrap":false},"0.3.2-goal-approval.20180727065857":{"name":"@atomist/sdm","version":"0.3.2-goal-approval.20180727065857","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a77b5475410a7be93f0f243dc18fc9c24f726065","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-approval.20180727065857","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KymGkDCzsoDmZa2DLjQbhan0tsvoCoo3arZjQMz4eViVEjfncrNb+dp+nXEYV7MUQcMjl8A1pjHg4YQCBJQ3ug==","shasum":"c7f26d078a5a2ae2e0f583c7e740665f3850126e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-approval.20180727065857.tgz","fileCount":816,"unpackedSize":1793834,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWsNYCRA9TVsSAnZWagAAuTYP/3Zn+tHEnBFBliaXb46Y\nFZ4dfpTeT81+LZEDhWfYNAT9w46lYMBRJf/xmnWiVKv0xrhDqy+C+HlKTkTH\nBjaN9x/qlUbQF160Uv6fvsPlU5dXSA+SRSXpnlufqUQ8Gbba5swGSOaLgIb4\nEv/wIT62Voj9uwGPjL8dGDWEvZXk4wJYk9tcsjZ6Kgy3xyhzejQ0qWzOj1D7\nZkSQzr3cQUrVL+44o1CI9IAaVNKlWzVhsZtGln0nnRWZNcnnXC2T7ZJ7VCtp\nDA/xObSkO92Td4yQd2IIAcWKxHp2UNgVjTUOUTzj+gMIX4dNLZihqShZIpWZ\nn7/dOP9l85HH7eEoGqla6r+0W6qraqEGcv6HBksH0ttAKdIkf2kE6+xNhdO0\nPWyxt7g1vp+zo4nflVA7lgp5novcsaS1wTRmgC0pNFf7AuBhEOmG5kvQzYcL\nB91yEFrYDZKGiX6JpzdWaEiNt2XGcNswX9Menbl74OLrIr/hBWhwCK3iRn0p\nHSQ68UAcYd3ZS2APh1iKwQQeYUrodZD2+N/TXWEEm1uq4fw2iNxDwzZ4ebjD\neeJUQ6X5y3L14X2LPWM3GlSZSdyUQeSzxPA1odWgLzexbk/nCtxFdnjIN893\nsgbwnDS2I59gfTdOhc+075Rce2iQ612YVSjUcBUGMl751hnrRBe6Njrmc3Oa\n78l1\r\n=qU/q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFVhssL3yjkR0dBtXbKUCW6m/GuJrN2aUY+GwW/VgBUIAiBsZFInanLSEl2liinWL/ngGgDBm8tORoT8tXU9Ws0p/Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-approval.20180727065857_1532674904337_0.21543960675570895"},"_hasShrinkwrap":false},"0.3.2-20180727070836":{"name":"@atomist/sdm","version":"0.3.2-20180727070836","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"^0.18.0-20180724200455","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"28449f05b80eb8cb5fa302ec458e5a1ef73e9480","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727070836","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w3lq5+npwiTeg07Kdi8rmwf5Esusf8Hmvza1mIyBAn6JeSeAY20Qk2OGI8u4X0wXbTzo+jxighyGqlKDPR2izw==","shasum":"9da4db8a1343a838fea54ce888f1e25108276141","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727070836.tgz","fileCount":807,"unpackedSize":1780800,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWsWVCRA9TVsSAnZWagAAssEQAJTXUv4L+E8kepxjcBYn\ngWUg1mXI2UKsQrCsuCTPTCVxGKLSNoa7/Gid2grHG7vkhMeko3cnkeg9ET5B\nFKGJntHNERLIG8+jyCnIpozqYrIvYXyGY+52+RFtHLHZCJTKtnS4Q8wT6/R3\nfaNsyM9VvFQfVyD7wFhhgjuvQDtx2DCarhxk41EONrqNi0ttjAQOZ2/UOzt7\n5qU/W1l56kvkxzgNFcWc4NYMGAXr+FZqQPdygDLIxop3LlmMQttHDvYQASWu\n/2FZmnqK+5z1NDApeGsWFtX815XGWYJLkFxPJwAXRfzCjpFQJGB5nvzfaOX7\nvaftbC9xy9DV5sbCkiRq2g1Bzxp/cbmSc5My6IPQZJo09Aht9QyHwLP6xrs0\ntgAUYp55uc9U/Kvp+Dqxoo/ajhUtmjAYf56at3qftgs4zgjwMyt1op8XT/M/\nGaNND+Ru+dYcQ2XbijVCENeG18XCRKv2LDgPBk1xwbAbtkkfoxTkFsoOVbLw\nWIUMuQEA20lXQXHJ8UUoJAGjNDSDL20VzlSWC7sZgnQ37MhG1rmsLh93BLY/\n9zd3nOdq5j4xNom0VNesrWlW+DpvBl1TN7JTFBCwH2W6M/QSYCIsMxdWiywt\natLuxhLU+r2vg57ehGlR/vZToIpnoMeqM+ITgArtRUpcSFw+YgeCnSMqicbR\n6jgB\r\n=LIlp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF5sC6wHv2lIRRKqSaXK0R+Qkz84exICVUYhzL4kWEkyAiEA71ZF7yEmjTkv+QloNBsCpoVy5bVUteYt00FlLkECuYk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727070836_1532675477844_0.08408789909953818"},"_hasShrinkwrap":false},"0.3.2-20180727072507":{"name":"@atomist/sdm","version":"0.3.2-20180727072507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fbbc23016a9beec039e4018e1e530817e6cda3f9","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727072507","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oo2iqbwCADNvioX38egm8kiFJnjHqcce5hOBlcSsYIOsgokc9IBXCHy0sYMetjOLPxjWPXLU8cItBQesbYjGlQ==","shasum":"cc8e6c6ab66ce83f654195c2410bbf7d9edf7b91","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727072507.tgz","fileCount":816,"unpackedSize":1794324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWsmUCRA9TVsSAnZWagAAk90P/1gYaHSBpdp5onaIL4zc\nNpm+e/n8L9+pbYdduvDM6r8SI37fM3QJkRZfJhsLvVi/BSQpMccrH+mf3DB1\nP23df2zSJPiWazxR6mwdiuyFg3yrcmkHvsZTmIZyW7Odm5J+UUvxRBCe4pw0\nF+HRq+0Sbs4zU9G/fb20dPDdmAPDByDDmIf1JoiIuDy4MZaOWaF54C7WVUKp\nyQAZgcRnWUdnb9YmaCs+3JkqbqYyC6h0byusFqZtqrCaQ26aX61Hdpo+3VKu\nvXeAjOHXxOBHkUSJyrEyz3a4WvHo9HSNv7us/WQUr2K2fqHboeQQ/wAwa1lo\nwrqZ3yVesFcXp9auJZdP6r86JYoGfyT2VlDAIP1+l/N9qt7IHCy+U+Y1spgc\nZHsWBkLY3LstrwhsIyQAv4aNHsDTLuy6RxgYGvtHAToB4InE43cM0VzneY1U\nhjE3zXLodxFv1ANT0hCroaeOz4x/Sy95A5W6+CgSWXBUpVxIR6y+/WfwuDLM\nIQe9iLq3JVPqyiZP5YK9g9DeAsELHjLAe3wq2VFTp4hf/JGAuqIb8NfZrIY8\neIGtKGAwH7Zoua6Act5eZV7DbEkxx47sJsQ82Nzpj32EfCPLCcw/1GRJadYY\ndwRw2AEO+OwS9sRdxWXTYrtyJUKS9ZcoazSWmk83xwFQCWNAzstJrGpP/PJC\np18x\r\n=PvhB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4ugWHl9DctvunrHfzjJUI7tQ+1SGIQ/qQy6E99FKTBQIgJ2eHIEWEWhWx7sXhXJnt8r8PfyUzbs1PvgfeStoyxcU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727072507_1532676500462_0.9809697848375751"},"_hasShrinkwrap":false},"0.3.2-20180727074150":{"name":"@atomist/sdm","version":"0.3.2-20180727074150","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c95499cd673ce2304ce623a68708e1e203a39bc1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727074150","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-CSj3HKfPHBV6B5KsaopIvNoO5SFa8JKlluCyWMqc30HunGgxiwC7LSr1/7yrBaJyyO7vMODEA9D3DEWRvLu5Jw==","shasum":"44faba33bc2ab7a6e10e2dafa0eb7877b2bc6427","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727074150.tgz","fileCount":816,"unpackedSize":1794539,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWs2CCRA9TVsSAnZWagAAcoEP/2U6REcLAmiQL7zaqQW3\ng+gvdFPko8hoR5z1wXyYbcNm3oDciDSOnbeUevyrnC77uhRDv+DNX/dWBPmq\nAVJtoEa0y/eWnK13jp/yB0SQBBkFsA24L4U/m03lqzFyBrL7GuAahQ/hfrRf\nk+dQLe0nu+stkeg3kYbYxAfaWvziHOCveTQDLkdnfVBZhJabZ/TSh3CnFemI\nSRylucWxOLmXjf85zm1YQM7KZ8m16smfLibIyyIM6RX+ob54O2zUw09xK6Yh\nUQg+zWoz+WPZ5evEtcuQkySRcxyb6mNTmalPooIRH6lUZv5G0yodPPXBW9qx\nvuj8DZTkGMcqRV+gkvZRTtlS1zDD1qBeYmkIQOWDH6thF1JnrhjEgLwKhO1H\nmSngLD/N2wmoy9SPXYtCm0moIZDyuVJ9ncb1HgjtS5Xxx8G3wyV6E2NKCL1k\n5ehcmmetYjMd1DCG31zbJ4OqW7218gy6+cMpbGeA0k4lGDdsAQUCUSD506pG\n+c7EH/TO8ctE3ejJTfAZvfnLVgAjUVC3u1Hv/jmFVMEbdI9fxPVse5l2byJu\nQL4FPQKaGDwyBz5MtGijNpgrG8MqUSJ5zMiG50iXuKcPIAz7p/SOF0Q0T/lU\njCafNNKizl1VmJdJcYNrn8oLF0fATS9oSn23alzDnj5iHiYuY1Qr4oKtLfpm\nkSwX\r\n=aTWg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLC6ddvROkp8YSsX0FdamLcogcuzQOlibXwU4GgWz9xwIhAJ5MYDsYNIuvSEViSzSh83dBHi1aDv+FdyIQL7CTj88j"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727074150_1532677506504_0.8041213655448964"},"_hasShrinkwrap":false},"0.3.2-20180727082021":{"name":"@atomist/sdm","version":"0.3.2-20180727082021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"125526d2a10d8a776d354da17ac102f30723fecc","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727082021","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5Zt0aNXHX2nVYSmllRDO35+8n4fm1XoG85QGmAICENFSxDPMZ91Na41t+F4PeHywBrMkEllU+zAMAuSiAFY6Pg==","shasum":"75028de9e338482bac799e2b8b4909c89149ac70","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727082021.tgz","fileCount":816,"unpackedSize":1794571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWtacCRA9TVsSAnZWagAArecP/3EIQxRaNt+OghR2GrCb\n5xMAqbNdgt0qmSB6n+VcDD7DYO04bgCtwAicZhQNsMU6tzo8oMXYOHOoV01L\nBTyTB51Hfu8eIHzP9daHh8uHDx64NlWUVNOZNEZX5F46s8oAkBAI6Kr8T4fY\nOAHHOAkaVZ7FFJbJOhMtOSkPTjQ2RfVwz+CoCgvkVPYL5WxinO27iXiOSQIU\njf9sfSuh8G1CGU32xnJNyCPb7G4f5o9Hw1Fyy+K9wWmRwk+Dr2/OXqB/tKfw\nBhai2jjDCfzv+tGWZPg2M+Bl36QCNmDT3f1PsCm0XDxNwredu9gTNlCBXv4J\nV1ECTLWLMHEkI6ZVd9rsrByuGINGFSIqsq8xKoti5g2bdzfTFp3ec/wuIWZo\nsSnGNb9Zh6eofDM/fyajQvTWTks5kWahxab3wvI8SvtkxZCyIxgsLE0XacMY\nB566Evvo8pv2oyFxq5Po8533OTeEgBs7lSEC4vSojSMe3oWb7nmcHp49+Mr1\naMP677PvjmJw8C3suA1YjqUau/WlQlFNClQuEEFjNCyHd1jPipEjDHepuxxC\nu/W5PebG/iIdDX0cDDc8+mgGYVx3GF4+Xk7HnMuBB4MjWfY0n7RtHGDp686N\nJNc4BnTSD8crm/qnNRGvBA0jIyK4pzADeYy1JJ8auvqbYI4XE77FqLCrIn6P\nvTDY\r\n=0Y4F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMykBX9rPYg+xWfTS7kUhJIigwsePT1lJszuA+fVpvNQIhAM2kUpyhAPActSebPiLW1f/DagaogJWMEQvLNjcDTSZt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727082021_1532679836420_0.8393747765775348"},"_hasShrinkwrap":false},"0.3.2-20180727121721":{"name":"@atomist/sdm","version":"0.3.2-20180727121721","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c622f2b4551cc4a4c181a0fc9bbf8ae1f5b0c4f1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727121721","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fASmU6X3id20O9QZ4qWKSNbH103d6dthDFT6mcopVKvBoVgadCiULDZnaRMtuRr7g1VRByfMQx3hx3kJaLIIqw==","shasum":"a7ff53b07e33ddf47d31f513283cdb7125c5b56c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727121721.tgz","fileCount":816,"unpackedSize":1794641,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWxEOCRA9TVsSAnZWagAANVgP/A12mvUP7ARwb6w9rtiF\nYXI24Q1+onqYNyjKrtKCUt+PTze4ozfZS6H3UbFti7PRLL4yrf1kKrBqUZum\njVk1eJE/6ZU2wIRPXJO/jSJF6Ow88zh7xdmomjeH60Y7htIUztIBSHsdSlph\no+Tes/Qbq5hlxC+FhrGq+Ntv+yKVTHCgzFdJj0uMaVJD66Q95HQeqnu0RxW6\n2DvRFnAfR3W3Z/LTOho3f2ld5T8djJUPWNWI7oAbCmQBLANwa3B1sw4EvA+q\nIcAHMrKQhc8UJwdBZCRiw7Qn8eP8SWubCTGxcdQAdhLOoxVZgLE4J6ZOcI1n\nMrSIL0CH0BXbeJKY63DCzyHaYzh70FRz2TdLUrpk6SUjDIpu63T938bs0kyW\nf5g6IZRASK3EpSstLX61rHTLLinwCcdT3sWCxTCxBSbq/kn89aiKZuZ7KEM4\nPpDG5s9kUtGrFyB6hafXuuOAreDhdMOuqLO9i2Xlm271+9Kr/2Q49CPNVXaN\nJrGCmYE3MgwstzJDg43bWVpMxMuxhp36c/o7iPFciremJx1W6ZHOW7JOaOnn\n5ji8Hf/PxHLGqcOYYoyD3ybMLrJCSXy2n0BOMyxmVCnEpEtX/on1xVPGeDYX\n5Xq4iRXgePHKHmjRY3FuKfPs2PgubZOLbER7ggDwS+v4YqC1HCqQ3GtbYLTU\n7IVL\r\n=DX6z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFd1VgG9ILRIiyA+yasxfWLCLv9dFOvu8XQvZ5KUVWFEAiBc52yGvgUs0sL+1EbGH/52EWOgv4y0E1VC9LORSIfv+A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727121721_1532694797892_0.22862427279943764"},"_hasShrinkwrap":false},"0.3.2-20180727125411":{"name":"@atomist/sdm","version":"0.3.2-20180727125411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c622f2b4551cc4a4c181a0fc9bbf8ae1f5b0c4f1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180727125411","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2tdPbBV/q9uyvf+A1xJQB7bo+z3Lg7iv4KtEGzPYuMNFnGG22asJuIszLF1PspoOfYuo4IfKwDMmUCQ9liYC1A==","shasum":"04a72803e482225a1f969d445d3775f1202ca69a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180727125411.tgz","fileCount":816,"unpackedSize":1794641,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbWxaxCRA9TVsSAnZWagAAPnoP/j0JTiWtiOEFxOhAyyx6\nw78EbN4ookTCVYSH8vbybZxGldqYUQzpLv4Olx8EntnE6nzt0qQbCz3bfO27\nd/IHbir9wURxGcya6BIBmFqawmzqKS+JT3h8NRRLE3m7Wl7AFC/Mp19GS6HM\n8G92owKVE/cijhtq2k5KQfinRFGyIkfV3ULPLY6RkaYjmaCBqmbOyBokvvDk\nxj4bGbaPEooLm2kZaI1Lcy53Zk5XK6HDkSIHINz2HWe9ZutZ9p1gn/snb0Fd\nqumkkAWpR+vhf9lKIjnaxOtKdlcTGdMV1eB8Sg2afvMec8JrUnUdyWhDhQ5o\nZrIBVTDNFLGV8SsxWkKxCThlDw3sPkyT6eN+nUlnVCRl3dGS3+WqzLchqv5k\ncB+QmxUYFp3dYpfpdvzEnMpUqxf+OvGcGgJ2ISCpZ43demnoNNq3WJCNPAKX\nR/PJBNklsA84ACky2r2+QtTZTRtQ5eU0SneTa7mmFF75x0bmRxX9foXleLhl\nBd24BEz+0aHSKQ1Y5gC3g8KgF8zBAp8xt9qNld+BQSWO4+1K6IOTJ4EqjQMP\n3Ydiht24YWUKhJ495SdMj6S9LtzrmIgVq8J1/hNVZoCLtvvOD9vum4nJBfnd\nvHYSHW9UChMvK50dEYA2ZK9G7EJTHVx+ZQAH2GNe2LQ5zvZE6HaLNi/7h7xT\n83qG\r\n=wO+P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcailUcrLhpCx34ZL9XfzyC8zf1KWnoTmoUBn2B3xjCgIhAP8GJTCgUGfzabTgzORhkilkyTI+rXFj4ZLc9gr5D1+E"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180727125411_1532696241622_0.167503608655307"},"_hasShrinkwrap":false},"0.3.2-20180728015553":{"name":"@atomist/sdm","version":"0.3.2-20180728015553","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5566c266ba8d3c37a90c434682cd0950f4a7f6ad","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180728015553","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-g8Eu0o2w3ILpEoIaVOwQ0Avgo9y625+oHlHRoSNREcJMtc69e7uzawi09ewAF0DqL1uxX3W32E/ckXH7IMbp9g==","shasum":"f43785b5cd7ae72f4d7b15fc849fa68438b71de9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180728015553.tgz","fileCount":816,"unpackedSize":1795027,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbW832CRA9TVsSAnZWagAAbTgP/RUNHe7ITqxYo9PmMWsX\nUgd16so+MmuAHF8erfa8zKRpk+usACXVThBpqyFwRWKG4JtJLnIsv1WsojZW\nAI37mJZugv7Z/xcPJXxE/zQ0lDu4YmoSMwJFDF/FEtDvdezGVvOt9g2tllDi\nApvtubRtG0deNzuzoHimT5reWRam4G5TAeHoVMbzkKW30JE18QdjdBG7MhPr\nQ2SLjr78N6FnSimD+4gDqBuU0JapdVQm+9qWkPmJen3YCdDdogBSD8lqWk7L\nejQ5/sszafo0T8bSc8UHMqPwD5d666ruoEnjfPzvcO6a3QAy+rHQbtJV9OBz\npTiOyHPgzclxxizZzUogTxImzKnZoMKi2pWJlcC/xpYUMl7FSlHg5+I/WS1Z\nzL6yKkHI+kcnhcmyrLJ4GTzUKCQtDrr1FyV81XFGKu5UoxhhRYa+ZjLiOcfz\nX59wcH3knNQtjla6MyjhVlJyfO67ApDC96OqIhijZT+xAwMpHl9EAOymE2Ij\nH1NZ6i3T3Qlbv/yVNd3Yw5VU8vKeuJlC1MfBmikRRDY7yfCzHYP4OLk0dRLT\n5JseWOfubM/3bKS8vwsEMAGI26vwEyWdYLkRk0GJB2Qd5B1RJwYVs5Q4T5IZ\nzmZ8dL1RNEu1xUNLP4u5BOqyNxe15FJhWNcqMPwfg/VEAK3kmdFMnYM4Jsk5\n5pOs\r\n=wnsx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICeQaer+C288LI4k0WJInoH8Ydb9YwDrDckFkXl/ZMg+AiAFt/qF08rGKnIh1nYOZVwhOJpdztaRa9B+AOTPtTTddw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180728015553_1532743158802_0.1999532652445397"},"_hasShrinkwrap":false},"0.3.2-nortissej.nortissej.elide-axios-response.20180728202703":{"name":"@atomist/sdm","version":"0.3.2-nortissej.nortissej.elide-axios-response.20180728202703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"0.18.1-nortissej.elide-axios-response.20180728191922"},"devDependencies":{"@atomist/automation-client":"0.18.1-nortissej.elide-axios-response.20180728191922","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"af7797bdea96861a59389510cbd6afd76177ec5f","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-nortissej.nortissej.elide-axios-response.20180728202703","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R1nQfPBZlpVgLat3GCRZT7Oij15Nylv/l0mFiU+DbN5deo8LVYy3W7SNgxaByjyPI2Vf8Qzo9y5BfdFi7vuMjw==","shasum":"9b673c05b8e40edbe6185612947e442f4165e418","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-nortissej.nortissej.elide-axios-response.20180728202703.tgz","fileCount":816,"unpackedSize":1795356,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXNI+CRA9TVsSAnZWagAAR54P/07qRfpxOloqjxEEXO4Y\nJq55VBJm+kAJPo+xjrNN8dJpdWhwoD0f5UdmbaJrM5/FzYOucGKXpvlfI2Oq\nO/z1jU+qlNOfd1ey64bJexkPQTwqnKauHFzbyWR7FnrYqQg8bFb57j0opFMT\n2uoTldW/lmUFEWAF2Lmazphb9YK9NcldknUzsftRSCJ7+tSRqoDruUpyITAH\n/AzzWLOkIk+1F6KxhaFuUiC7T7ZjAbJeH8eN1UPauV9Hf3W79GVe72LN/IBy\nBPdzqPymr21yEfvD1d2nAZaO3aLH5y91mNiBIfMzfVFVVQq38vYXX6Mw+vBp\nE1EtNDqSldN8XQGMii7w9FWSIHE9EZ4e7/lQEIE/0Pn3yyHxfNS8rBlKZrIY\n9aX6pUyGb75qFKExbxVVj8GYkmRUB21o8NIS88BCr64XNN2UAKW295AOc3UM\nc/3EsRMWoRirwarGOMpTUatpaYvLLVkZ/o+AZDeVvr7o88mz4dbDpb6yZPPS\nTebLXn29x7CxZYwN24bmsLi6+1ch0hSumN68JGIoV8JdJ1RP6cWczpwI2+d+\ndSp4Ir4AeXcXup4QY+xXXTHCZEu0P1LLjh8/mUpkuuMBiIpr4bxkGAjz3ycq\neuPCyqzfv3Sm1Uxnn70s6qRPUKird/Oq43WUXD24erXN9Wfd5YdupAkazddY\nH4vO\r\n=j5FS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfJtKujpsCDiTuiLMcjVH1Vu+7n60nZ7v5M2x9om2hQQIgHtYGBb00wKycw4K+mdWZiZSN8ndPiA+PHTqweipF84A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-nortissej.nortissej.elide-axios-response.20180728202703_1532809789839_0.8680863961561349"},"_hasShrinkwrap":false},"0.3.2-review-listener-name.20180729003330":{"name":"@atomist/sdm","version":"0.3.2-review-listener-name.20180729003330","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"^0.18.0"},"devDependencies":{"@atomist/automation-client":"0.18.0-20180726212606","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"27b7f64d31fc338897210e63547414c4248cb872","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-review-listener-name.20180729003330","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-n4C3a8uL+1+TmVthgxwuX0Yg8FypApeFlWKxqB7+i4/yyXpGDizHJ8Sz/cLd+X2tyCqOKzMjnXgAHs8lVdaDfg==","shasum":"a55fe7ccd900bdd861afa032d7f91e297bd4269d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-review-listener-name.20180729003330.tgz","fileCount":820,"unpackedSize":1797580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXQwUCRA9TVsSAnZWagAApcQP/RWdsabidGns9b7ECsfc\n2xSs/YnCd1zS6tfVCu/cAt51IjvNIsIvQ9ib68plkT+syv6sAZAW3nRBw12M\nlKQ7/AzqnxhQ5bhpTO/COaZVHMCjrl/Dp4qP5Azn5CcOYN48/xU8N4q5P2To\nvPsfyfRT8FIIn0wPKY1ls4oqG6F4dhBQXqh/GUX1cQ1EAW728ADU8c3oHhin\nLsudO9PovnX2wq8FYiaGLxBMz6+Byq6RDF8LxhMZkhjkB2HQ3odaYgbpm+c9\nizk+Eolmjj3AQcc2cpqmOlbzPZofsYFokWMc8no5F8B7RWzl8LHtj+AJ3e7F\nUtQp/eaNHUqnquZyXcgwBpgXWoW70A3iO55qmwsOltENokKbHTEOybKH/E2y\nzKOZVMvI6Zk3Hjg+Fur11Y3vUHhgZOVSjD0hlrMl1qgjY1AsGnp4wyFd1TI0\nTy5EWFhn1AzU3xmHri07v3rrHiTSjEWQlPzW7XuILRuvrknyZu01pzY9pWXW\nahP0aNaBW+pJ6gMYZTLqhL2MWuJI4nqOqyGVp18g40w778Aee/PtqTMRoP4Y\ngXzezT9WRzvPtTVR3Cv+cTl//O0TtrbVO49uSyj38dtUuC4hlFt0oI45ivuw\n8ivE1aGeUeYbY0akTn7AQBwjhTSrKtOfXH6r9o5veIBwsZhXro2+31A5vTNU\nV38w\r\n=x1e/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICdjPOsBHo7EkKAXL6XOA9d4JGNP+6MGNZFzmWMVzkX2AiEAj32i/3SlYusfWVq0YhsrEn5LcwAMUPIHv7aqxC87SdU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-review-listener-name.20180729003330_1532824596124_0.8910867388393418"},"_hasShrinkwrap":false},"0.3.2-20180729004006":{"name":"@atomist/sdm","version":"0.3.2-20180729004006","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"0.18.1-nortissej.elide-axios-response.20180728191922"},"devDependencies":{"@atomist/automation-client":"0.18.1-nortissej.elide-axios-response.20180728191922","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d6d5876cb6ce6b3c9131e764fe09134721bb14ad","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180729004006","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ARo1PfVaS70+Q/LpxKD/+/Xpm3mKLzf/PGY3yAhFBISGrc4X23wt/mFz3xEDSfKOHewppCtSNuTx/gyMjtjZRg==","shasum":"3257fa4b5021797a7530ceca1184f667109c53ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180729004006.tgz","fileCount":816,"unpackedSize":1795315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXQ1/CRA9TVsSAnZWagAAR00P/ji9EYIe8DFKbHICJLOy\nK393Zmns4l/uH5ihzuNlHMb3munwWFTjyIcgZ46nbeCRKi8pAc5zgF/tAaDY\n3YGqhkleFQFiOW/L11xtjptOegS5AK7nDSpKozFaB91SewKV7o4XcJ67jXw9\n5S5SHzzeA49PHPRTkJVWcbb2c6z6xHRHxtt3Hm00C3j+2A0Z1rHQVARg0aQo\nzjbPS3FvbwTMHSokUnfRFNecu9XDCHa9BgjEotqNnQa400x2d+vvZ36JEulf\nWEDdOHgbHZBUBMrzsBHaKLYTiTXRyKjhZmQxqKGXnXsnL0OiKqqg94mU+Tdz\nAkMq/76x0h6kV5JpGzjZB6BqEAVjsuDhAA4JHJk5p9e3vFlQmc/Qj0CRcNti\nX4/Ybs1B5hPbSZriWytyPWYPUG3XOeLk6NKESR2Qr+5aGEZ4i4fjZo0kiMw0\no7QVZIW7w8+i/uwv+aqUaetPDzAb8GwCfy6axDfh5dYn0y4MQSpe5UssWtWD\nMKwb07UjL60NIy6FjzN4cOUryYfhiAO4BLwYCAYxRDNHLpPNfEXpa9hp++ii\nSSecpIpEBuVvxXl6LMdD6/erxGPzazbLu+uAEYZ8gIwR79EbSuA6eLnlr+0j\nH65R9z5VR4g3aYkKo14kfFdbmLbDoWmEQCgoOC/6EXcfByCTBi5ObzMp94Uo\nyyVB\r\n=ruI9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBry9Gkt6Mar2ha9E/Op81u5qRP6RUKA1CDeP2X54zQQIhAKe6GVwZukYKhV12O5gjc7qbwpstmkSR+S+KWLKGWYZr"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180729004006_1532824959432_0.3203475718991544"},"_hasShrinkwrap":false},"0.3.2-20180729004819":{"name":"@atomist/sdm","version":"0.3.2-20180729004819","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"16ec52cfbe92d733dc1e8fc68523858e7a8ff45e","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180729004819","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8eMJPvkUP9MPnJOfDKf21h9w9xsOAzf/GV1l7UnENlmfbq0CAw+uU5Oa+JAdxqz/ecphdgaUfxCSXY/6+VUAbQ==","shasum":"aaa0792191c75b5f17fc8c82dd0e4b4eec0de4e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180729004819.tgz","fileCount":816,"unpackedSize":1795233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXQ+NCRA9TVsSAnZWagAA+/gP/3WucBdXuFOuJEOnHHPw\ngQu7B7yKx/4MNXVM0QjcgmeaDLFPa2M1oqs5ZA7G0NoUhLDBiS3qyRs2oT8+\nwraptJBbdAbX0OrMa8HrFJEY1LlBoiLsWssjEXgxbfuYpERIYl7UD44xCAoi\nEr3bfZ6UOb3y4PIWkHKm+4ri2TvQgY2Jcto+gGO20RMhfOVKTFp02qinNIYo\n6QCSSts+yEbBshjyyQTotbwBVE7TeT1iewh3m66lZEWx6epfS7uZJpEq2twg\ndQQ3suLJJIBz3uyxCS2N5Ee3aqcGoerWFNxOo/jQYTcqtMXCwnESf7p7MJWR\nJb8H6oJRZbaimKRofbWOcU649NmQ9B95fng9jZBor784IA6xkzFthUrg/D2b\nD0EEdtLdXV1OkfVZliD20sSueGQY87hzp6NQn6ao5aqzP9vpRZ8h4Ln/dnxb\nnIGly01zSV2Il9Ip1PSPmfII32+XMxqm4uo+Q/+/D1EUimXoUUsiFEnud7gw\nmaozyy7qgGimvcSIs/8vhWqPs1v1UkdR4/mMAk5oS1rJrHOXD4l7u0eqpYuL\np9pmgnQeRsqjFZF1gA0ENyTeAX7f0IPlYhmaYCEPjkwjT+VWe10laZNrZ8Iu\nPoneEQePEBEr2ntfbH4FNz1JZS5TqNvYjFnpaYhU4Lsv2jhIFkpdVOeff0Si\n9azj\r\n=TB2v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC44qfvS2SrTbOauAe6u2UIFiIrtF1luau/Cz6iBuTBpAIhAJD3YNbkeTaOuXgGHAENHFt8X9DNrRt92bRedw9g57WJ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180729004819_1532825485778_0.2223032433575809"},"_hasShrinkwrap":false},"0.3.2-20180729224902":{"name":"@atomist/sdm","version":"0.3.2-20180729224902","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b152758f0069325dfca3a12c655e7942fa8b2e88","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180729224902","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9HDRMAkuxM7iAM8SfMM5WSoL1e2Yba8b37RMITqU/fJQPr123VkYtJ0oZCCWsIhezeJ7PXjDP8062d+uWBRyaQ==","shasum":"4a5975fbb61dbbc71582fbc07fe3abbbeec8f059","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180729224902.tgz","fileCount":820,"unpackedSize":1797765,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXkUfCRA9TVsSAnZWagAAqMwQAJaQJerQKnQyotgoYgJ6\n5DHiWFzTDdTJ8xL6xYn+x3E0LUBQj2wo3PRqANRWzOF1qgOnCx7NgJYqCKvB\nvMmtFl3WfRl/IM8buYr/OsUuHS5rNpOzC9UTZxDr28tJn0SZguY5DmmIq+BI\n9XovcIfKA8ZuujW/6cEifIUNRLxYCif6UBiu5KT/pPJPM5BHPQ4uHEDkUE0W\nJbJfLDSEdm6L1WEU0xPq/aytazHiOB92A2djzqq+MfYFftrpfBgIG/u9OMyT\n0pLWWLjGMISg0rwc1qWvzEgcQL4093bzubvJIfNaOLuPXAwNrCHWTVsx+IT+\ndtBr+nPW8cozCa8HXofXCzlScEaAL1tkCbCXGI7IMEFqJKBbsg664wQUDWuE\na8SVYs+vuOR+lVkwy1UZb2WlcQk+8nKXCxe/iI2WKoI+YNhO8TDtsyuC+Op6\nnzZbsIhTa3N453rc0qa1uOp2df1o5WLVhcg5qiDPS+hGkLy2Triw/u8AB4dq\nEligYIaayxpnoRzmv3/pD2cKwhYeEptljOeU9F1nzwPWl8GRWtBLM70omKBk\npqUnUzJdjpBiN8p+eAHkrPgqnBfqZkjY3QHd3QUPbe1LQh1NYDF/UPcrLwPC\nKQSCnbs+5PwntVv5SHAffaspdgpb/uO7HV9Wud2QVjBAOZQpqMGWVqQG6EWW\nuHpy\r\n=VM9Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEmUA4qKWf8wHq3QmcNySst2YE3WsRLl/B6ZHvpOXkiNAiEAn6Bj27hnSIdhiSq+QGVepu4dE+8Wmn3JXGIwhb65G1M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180729224902_1532904735274_0.3288674821328583"},"_hasShrinkwrap":false},"0.3.2-20180729225208":{"name":"@atomist/sdm","version":"0.3.2-20180729225208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4460d6b5285a1ef4c10c8b648db4c398048a83b1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180729225208","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GRkH3ZsLpckyu2o5Y9gq/964RdMeF/KeLR+OdyP/MKqt6CNTQstrkp77UUaiqRLMGqqGGoSOyTXUF8+S/MW/3A==","shasum":"acbcd84885b0536af94fa006c58d44cb98ccc020","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180729225208.tgz","fileCount":820,"unpackedSize":1797934,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXkXQCRA9TVsSAnZWagAAcBEQAJfCokxgqz525bX3HivW\nbsc8sjE2eECYr6eSZ0DyqHLXwBvnLJwCIaFae+i6XXB7Eig3lccJe1q4k3+x\nblBLwT8FHmTrsFclxiOS0k+EDVjv/KkMbReETFAMoh53Yi8R0PTPFUs7VLlk\np0UlMMPR1ODja3u5WKVYF+73oF1Jy89RXNztXtvd81rRFFiNTCvLhJMePTa9\newTMjWj9aip+4pl0LNuUqCtk/2TZhfh5MGF4zWIyuXDo3j8Fv1lURDERwIqK\nK4VZgFqQqLexGecj14HBQmdKKA9VMJQt0bdvZyrzNUPq9TaxatVa3bh4IVsU\n50u2IrbXzwdLxyFSl0tmgvhZz7fGXl6Zwh++lkIlyZfYnaARrVfz7oESQh0W\nee89w97y2uP+0As9z6wvoXfLAlLOWXLJxChZZHbpFZn6v9l/i/KQccb2wS4s\nAcTIKq+d8LMsVIUJEWkVEiLs1fFQy6jBGSYT+EW7CESgcwBpYHWezmAvynGt\nsIs7x9BqffleAnHESlUhl7jDntupTxIFjzWdCGjji4LoAJYLnoI7nfIovQUG\nDoHNNaB+47oLmi8QoIqbgrvblwlKedWQ2dNa0FJjbAAdR6zEi77H3vViz20N\n5Ul0DulS1HrX3MpASZuaEw3tkVCoSrl3pUGSTN82geL9ucnoWOWQkZ5MkkwU\nCDad\r\n=DhAM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYW3iS6nKsRjmI4QgJAtQ6kF8IYB2zpgqXaskjjYcNHgIgS4ksPf5ePhfOqoaNwyrxm9czBLMcWFgScLGKl2QYvGs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180729225208_1532904912082_0.49923247569209117"},"_hasShrinkwrap":false},"0.3.2-20180730003728":{"name":"@atomist/sdm","version":"0.3.2-20180730003728","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0c7530667ce2afcf92fb2b33221f867da229ccbd","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180730003728","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-akle511C7zzoQcp0I0bBXrfF5aYZp46kKqJMoWUzZR7ysoCBCUdHsYPCu5EfIK2sNAPS1m0hxoI21GnK+tFZpQ==","shasum":"33b87d04f01fc0a8b0ef0c196a9a3c9363e0e560","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180730003728.tgz","fileCount":820,"unpackedSize":1798297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXl6ICRA9TVsSAnZWagAAVRoP/RexDF5PVPZTMXEw3qlj\nZyGRWqeERsYw36VnKmXDiXZpUdo4SIwjvk7Bz37HwDuog3DjREEA6bnI4V+O\nU9Ek6KOXnmFKfIWa+DUssj8BKu6iPQP0fNJLtK7fN064MWgM/QGG81aO2xsC\nUAcwUVhP9u53f0rjlPtb5Xtzatt8t9/4h+zMo1zINz98oTUyhB/u+3kfrqP1\n5+12V0Qm4pEjWw6z3/Az8rMJh4fbrOwCbjUrrl0LejO9jCnjSAn/jDGJr7IR\nr+BUfeDcDBFkTbdPg4rYdZwZVRTNHsigXXXNJtsV7TGXv5bnLLwMRbMkQwbV\ng8Sa2fAbp6DnCSse9hGVrtMKNLy/4mVlYRsN2+kYnq3m8R8OM1Off8cwu5zl\n2p3WyzAh3FHH83RfDxZN7AEaSqPMEET8AJPV6yG0mc/aVGj2JSeTAL8gxfSe\nAfQ6mvLBp3yw+VFp9zisV2k5jspQrlSTa2PILBeoptbjrQV4c4BfGJFTwW6X\nO+6hdDpv0vfIM629Ie06Zk9K3/VHtk6S4BtWLrTBsBFkQ7BOehWEIKbLidnx\nOKsKQAN+FMagB6lb8DV5Ur+HgVYaKobjbF9CqmMnv2Czv1xHHyqKEXAyowDn\nQ53eu1ixkZZ5J2jilWQOZIk2jFhnqK0AkymafI868wvOF8SOQ++cLbA0Ux8L\nKqlI\r\n=9Zgd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEkeN2FqXHu+EGu2ry/b4RSn/V91Q9eQUp6cQtaNvWwkAiBXAgQ9cNM4dRuo+AMjIB/VYMCmtCaOvvlwAPzxkdMQlQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180730003728_1532911240874_0.2155215580924803"},"_hasShrinkwrap":false},"0.3.2-20180730010005":{"name":"@atomist/sdm","version":"0.3.2-20180730010005","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6a9fe9bd76d64c7ead67373ea06760a09d81ef7a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180730010005","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MSed/vHbJgTKsPHbXrNLcDZU6mRJGGcSIzu4Fu7Zsc9RCNQOmEckQcHqooH2SiqFaKulgLJNpP/4Hs+vYzmzcg==","shasum":"30ce62715d81be702f7a9e7bca3bf71e0b517716","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180730010005.tgz","fileCount":820,"unpackedSize":1798734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbXmPdCRA9TVsSAnZWagAARrgQAKRnbPxXieU6levbFAVn\nzwRjCkDmw5Bt1kAokEPHbzlaNuj3sqeGCWqYEOUl/taK0V54NPHQMgBLpX6H\njXlfWrezOxQdO7f4HN/NdQdB92d4kVKlAwbgddK7BGvmDSLFJAKfnFYuUPeo\nisA/2n7w/tl8ln/7ZIHlaXeYkxOyJ4bmqLyN7ocT2UIZOud/QOF7dtVcsIYo\nKAboeuuUOmk6oqwPRsZG8mYmllwL+KMbFtFXch5RWfI2n55ou9EHBc3CvSE1\nWR3/2wQKDZ5059mpwGhxfxVmcsWSv9JaY2TX1mreogb+TuAoP3YYnmFlv+pv\n4x7oleoewE2pjq6hUtPutR3BAwvoZq3oVC3lD1j6AqqtzF7Z/BCkfuUy+7C3\neW05QKetoZpq1CGg3CMOYo3zN8iiGSrGe1ElrUGv0iIoVG8/FejkGPsSQHam\nylcVSn0tJqVou8g8gxauxp3i1cmRGqx5OcZaYlUuKhkESOV2M+8drGibGjxP\nSo0J0gQpNcLE0ct1R8kDxiWX89TWismq78CJcc7fNVpdEbjJJEBoEWwk15JV\nRME+MsW8drD6AcuJA5c+QWKJiI9peeiFGZW9XqAtowegfS4nD04hN3kFJy54\nCNzyZ8F3xu99Cqrikjaesuy/+6GgjXj3qWMcW4Rj+lRfNMd3+YK2CXUhkB02\nS8fY\r\n=E1rL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBC1mnywgqXrLZ1Lscc4ELqPJUWwYa1t4MSzMOAiz95gIgZUPulIRmUeZma+Tg3aC5uXOd2eyAggMADmYRcwFc5Tw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180730010005_1532912604854_0.41787311700576457"},"_hasShrinkwrap":false},"0.3.2-seal-goals.20180731034437":{"name":"@atomist/sdm","version":"0.3.2-seal-goals.20180731034437","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1995cf38725e9a791dab065095d1d0bdc97e263b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-seal-goals.20180731034437","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-COSCfpyaYYYUlj9ihb297D7QCdV+YtwOf7e7sms7+ZG5MTLz7jYKEuYHidoR2OANpT8UG6LqJHTquRe9aNVtJg==","shasum":"4218588f719f429b25045a288f98fc47e9de86f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-seal-goals.20180731034437.tgz","fileCount":820,"unpackedSize":1800432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbX9y+CRA9TVsSAnZWagAA9g8P/20WJd7pP1QDmwq76BX4\n4mopGnKDmxUhYfJcV0X8tTAJ2CiDmKwGABr/uQ8WGbVs2cwDAO6b+7aFmZpK\nlRx15ApH2SJ5OY4mrjBFyG6GCo/7ezIGh44/k38ViukoWOWcPKr4EdTSYiE0\ns+cSMKzSTOqwpYlWDmoSYMN27rLt6jOcAHDYbVwd6WE8tsnTGQdHipGCHxXN\nZLSYi79dBQ1rwteZ4eEViN/6tqEVOBjXk2HNqucL+O0hbXkO/Mhp35QWcRC6\nICMXvTB2Q4IDqbJGjuw7PFoGvIMdT+x5RSCWftZ8Xb4etr+KnR00RGV57kpd\nz7gaJMrFCYD6XyvPawPpqYgJje2nafqItqFSxdW4UmKB/sZim6yH/fHQaDhR\nwrodbOZtOPnZQasYOWmpwhm4zklXXNiOYr+at1HyD6P3aXSyUwwR67uisMYX\nAaHamYwzbTWV3wyT8GphveHJAHaCZSipuRCVbYk4bNfBehmjraWHumkgDef8\nULQEIqWaBRFfM7oG8fP/hLJCqYmP7Ux1tfuY2t93Us8kF70wR+0aXKfkq5I2\n9YZwaIeq/TQGRAycKmoo0gOOQ6s82SkdZJXMN9LrRyZgI8U/v9rTW2dYng69\nVWVIq7wD+XcLjNF50L/zpZAOrm7MEAOr1ZkNqLsuPSY74hQyRgi6InysgP0W\n38ND\r\n=6DOL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD3gpyPnMrp+JdAC7MO8DcBj/NPENXHyTs338onXHdaWAIgMz5fvtMyxbo3TUgMfmUq28Jc7bgDFHcvda6m3fbsq3M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-seal-goals.20180731034437_1533009086681_0.9675147526562604"},"_hasShrinkwrap":false},"0.3.2-20180731092523":{"name":"@atomist/sdm","version":"0.3.2-20180731092523","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e63f3573322515257ddc2023c24602353c50b96b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180731092523","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Bt4L/W8NO+WYZbo2SiusyhVTmW7WQop/ksfu4wSdPPpt1csXctD78YdMrU7VCQmdjqlnsA288UXj+06RWlHtPA==","shasum":"1e3b7610e1fe475e3f8e735dbe59984ed096afc0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180731092523.tgz","fileCount":820,"unpackedSize":1798923,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYCvECRA9TVsSAnZWagAAE6MP/00cGkQPJasYdMes5AEV\nOYUjEHi+L7QsWgioMPHmWFaHO7RtLDXTGFCORZrIUFs/oaP5OUKt2TspQ8hD\nWf+b4uGQvs8H/DAkjczlzsNZtUmHpQRKUvvYHQNqgIlUb/w3F3ugFt1UeFMB\njX+aXI+wAaD+NaXHZHSwAODAAVMBm6eyd/eyaSd0iNZLC9CIc/gyV3fH4p6d\nmt8DJnELU917PhTyHWP4BdYZ1IbFbzMnlKWk7g0xIXU5SP9N3gO70FQ47rRI\nzxCh8VJD47amE2Y17uc05im7tNtN+D4CDNmCuyeRbeJ/NzeMYvSqbpnaPniE\ngJryoO3WGqghqq7cca7LezQhcb7azBGEjv6gUONbx5LJkLdFwgs2lvhvuOrl\n9CdpJNJYWRpu3j6fpwqfkKrnq8itdVE7aQyR+UljKR5nmpHp1qvNVf57Sci1\naKGYo3Wd6umDLpv0qQClQfSulgww7Q9Q531gPQrlL8FdxfJP34JaT9jc+Cyn\niZ/hZvqxUEYXIzVFnXcSI/BWwy8445AFlPS/+5pYlLcEp8z7EPqK30DvN0yj\nPjzqyJu7UMaNrIXS4QwN2zq3R83PxHFRWZHglCIPFncT6IK8XvankvBmAvIi\n1hpBP8j2KpO9nYvy40TPVNRAkQSMm6h3PNOCatxPdLNA0b1RSd7IfFSETZYr\nU0H5\r\n=Ffue\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5KUQVTSwMzGNdFdDf667ko5tl/AjtV+y68pkqiDpiagIgKP+eh4EydLdIUBZjeVpH5LwZMK/h+z+6nOJ1QlPvj1g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180731092523_1533029315987_0.05248081417507189"},"_hasShrinkwrap":false},"0.3.2-goal-lock.20180731103853":{"name":"@atomist/sdm","version":"0.3.2-goal-lock.20180731103853","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b398d73d18ef1758a47572723158b68cfd996942","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-lock.20180731103853","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HkX4dRqNRAAXy5cEPxDE3aj8nD59T1ebOUVGDEvTrqy1b6OWck7eXhaYxZqkwszT1AhuvgTepiB4Q7LElswiOw==","shasum":"f7eaeaf7681d2ebe575b5bae00cc2d642447a1ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-lock.20180731103853.tgz","fileCount":820,"unpackedSize":1801598,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYD0ACRA9TVsSAnZWagAA7B4P/RZeQKywXeRMh4wQxefQ\nDtlNwkTV2LxiqYQqg5ZZm3XHjLBEAMBgSXvvShIJ41ZH6T5/LNXpCmrvAD9P\nEUvtw1HYvk3WbyA9lyHYUQtBJToxPqrsK5svYpPDjAjAtuHd4pIcJoXbGgrd\nmb1RvdMsErGM5zHqWqzI8HAoHxku8tRYLUyf5p5Zua4FHVV93OXwYJjqoQiQ\nDxtsQyHQLA05xyOc4cbNEca1h+avpl6VKiRq4Gik8KC5YV+7MaJRtsViBcVZ\nuJu2G61MYN8qnRhCu7CcUbu/24zoRu85wLjiTod9AwDMt4KKhJGyLeOGZEIY\n9/0Af3lPQTlBmUs0FbDr3B+uxZNNUZTaiFr4QJqkYLQH25ZeuNSdcjwTuwXT\nda3XtftC7IXDNFzrBHsWF/0HaHflgfJRdJkmKVtlbzWkyW0POytxk0g7ySjI\nH+MNeYhBPzXiNNkEISAI89bARNCiCIgKStOlX5K21116+Je47diL9RW32N7G\nYWGA5BNMRy9HdKFwKMlJIg9V4Nw83s37MZD3rgnXkUs/jDhcLXNWqq1gVW/U\nC29532fAnBoQgPznCJvQmdARhEMWJ4yW//qM2927Dp/Y8duOXXoTs1IG27jS\n7r0ZezJHK+6vA24xiUCMsJi5GAqjs5GLWQ+Pt1jB1IccNXXkbd0CqkGMGFdn\nooLR\r\n=SM15\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQyBfvkwbdFgxpqjunl9t+2etyA1jyU/ZBKC5s6yVNOAIgQlj7oDjYUtmGurO0rzKtdJFLjD6SMdIUIJcCL82bk/A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-lock.20180731103853_1533033728018_0.8818900761301507"},"_hasShrinkwrap":false},"0.3.2-20180731124532":{"name":"@atomist/sdm","version":"0.3.2-20180731124532","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e63f3573322515257ddc2023c24602353c50b96b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180731124532","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4z0Uss9t7/aQ+k/3TFMJ/G4FPy/KSxg55U+Wcx5HFajUtd/4yYQZcJRSX3Hi1CeZsGv3s3wy9X5Q9mj2LZGHQA==","shasum":"5a49f9249892b815bfd59ec987f6ed31bc9bf953","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180731124532.tgz","fileCount":820,"unpackedSize":1798923,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYFq4CRA9TVsSAnZWagAA/x0QAKRo5lg7eD0BXLeQ30WN\n4fP22AZUIzdiiY7L/qklt/t/KzNVQ0K6CwSE7T0CyqnkCTlvJ9P7faEK5BX1\nXcdUyt3f1qM7pJVqWxp6D6lnfJQtZmFX5c0thcnA5nBWF/pxCIm2EjhjEdxr\n8naL99yPxWDsKkR7aEJuhnsGP0bZ3cZAtaq9ZyVlYkHmnQ8YMDh6uEXXKusf\nmxVrvJF/rtNIJe769l04hIA3lu2K62brK1eg6B6WuqIBMteeQO3tew9Tej4z\na1iLRodrJVdhTIFRhNLTWgXFMo/xPB1+YtEIkVi/VPhkV5N53FrM8rn8ZSe0\nhf1A5QE61k6MPOqIaySZkZS3UYK2Z7siV6stAGafOGwdOZAMItHVabZ6wgsN\nF9foEH0f1mUlLBhjQR4LAQpCU/AgXjCMRY7I3WngHL1ch+VATpQQ/3UP1aGR\n6wGZCcoJyaNUoGYFtJAudxLjlrX0eq2aXgWiI9u4u1a9cASrY1I0QX0z3Ai+\nwLTh8lNirbTqR+TpNGbMyytOJWWl/bHC7LM3BZnL41PPhIlTZZT98HPnawwT\nKzCp3oEaOeFNpEMNU85EpuFjoO07ga2U/Y7fnhhVtSyDdydZlij5CGwpqGvs\nCVXxZCx0CIIWsUcLSB2RbxtIsmt59B8Y0r1PCxkWPnG9+g/OMBssVNGRxIZo\ntYBd\r\n=1oAs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEVkMpBcmPezT5wqbLjEoj0oItx/onUsHG9ORQJ1eIfzAiEAlphy2jf8HhzMVUO5zAqr6EqjE0eSCYOJf4E7briIUgI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180731124532_1533041335843_0.8892529720555622"},"_hasShrinkwrap":false},"0.3.2-goal-lock.20180731224349":{"name":"@atomist/sdm","version":"0.3.2-goal-lock.20180731224349","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"301634c47c85c7a746e9806ce75331d2c8aae918","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-lock.20180731224349","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8WJQIob9Pn0qx7miKFKwqy74YZvEx/ocpHnEbZHt11Ln+t3Vxya9vwCWGF0mADiKxMkfQq43AfRjwSFDUnvJKA==","shasum":"bd2366e132abb413b222a8948736877540bb9a6b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-lock.20180731224349.tgz","fileCount":820,"unpackedSize":1801799,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYOlSCRA9TVsSAnZWagAAjjMP/3rsF5UlOvhPWoN5SDoN\noJjVhuJ6sPD8Ev5rPOjA2us4fVkXF/8gWGiCLhghJAGX/0rij5Iky9dab1ll\n7d5XgpaX++yGJGtDXXweAtXc2OLAkFStPjahdQTVaDmag1+A+o+PnS7ACUTF\nPSsiTp0NCdm4p+KFgKPG2eKJtzE9VRcomgafHYM+DDa7jKToinR8a6LqR4tH\n9YLbceOaDezIlwAAoLL3Kq2PGP+GcHG45tSc6sFzHyXeuaOjOP6/3tq67dMh\nAELF0iIf4omubHgsGQq3URqRJ6nHZlG9C2qYBJBfrkn8yGoneU7GkW5lQpto\naztecBmtigrM7LsI5N1i/fEdA4+bcdqs84b6ksgc9LRXN2GHGsJWhpUVkEt7\nyZWUxJkQfg8iCfC9nSKHKEdGv3Q4+jF2Twn8rAK5VIBYx68gL+KLpoRcHGa2\nlkHM/TmTrbmRARUBc5dRrhUYdDjhsJ7ex59WLDq0ovUhUnXRQkukN0v3dxa3\nw4gj25JU59qL7zmj4J/IESzSi+9ulnN6GS9OX914BXvjzuqNqNlTYasmqFtC\nbt3bVyVKGD/2HEjhRlb0XwFN+au/PRMgVgsx5WuxI3FIDOAYJGmUamEE6R1l\nSM1x8YvSlQh3TwDaieLEAxk9zFrsoKwBl9jLGCTkCNPXykZNyPeALg6PBBWp\nZ4fG\r\n=VfBg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDytn284/9qnhqQzr9ws6ODGBoLWWIn4qwvTkfqQhVDKwIhAL2B0rTzyh2h0QS0gpoqc7BcrJ6R5gHjTtVu29Fj8Tw9"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-lock.20180731224349_1533077842166_0.9773931441712886"},"_hasShrinkwrap":false},"0.3.2-goal-lock.20180801010341":{"name":"@atomist/sdm","version":"0.3.2-goal-lock.20180801010341","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"61efc6145e361a22de9eb4356570f117fdd9905e","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-goal-lock.20180801010341","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Zk0/plW9XRgRGPgtsU7PZNhdks/N4IUQazOrFiDmMBRMm11OpaZx29Ge44oAtOegZnBXaht1J4LNQpYj27zyOQ==","shasum":"32c91a4471d3239e93c50e3b1306f09219e6cc30","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-goal-lock.20180801010341.tgz","fileCount":820,"unpackedSize":1801799,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYQfDCRA9TVsSAnZWagAAgBUP/iQJe9NTu/feRhH4Vbe9\naue6Vw+YOWb/2p7EXk6IE8PhsTGaHbDnMSWrZi+EZAq7IHqES0T5gk0FsX0a\nsfOxopHFVnEFK5RevAjC1JMJufboKPVKUlpU/1cYHvHwN8vUhX33hQcWdgR8\n48DQxmTLrQ4mpTy1pRureUPKAmIdu3OuOapCY3+kXh8NmGm5Qdn/U6UYZl32\nwumWGeSd3gfpaeY+JNbGgGmrP96HNdxeUjKhrUU7rck0HwaZhrMmaF24+EeB\ninN6e+vnt0rnLhsXwhjkLj6LWpu5fRGP0pVXTGYi2HrQonmhSh8tawMV6GxL\nTA121HezsWB0veEhEbUaANtkemH/TqTwY+fDuK7TPLSTyqR4w8HaVZoIDsCP\nYS5NENboynorV59JuZDfwEx3tC44JX66EZMX7klNrjgxPqd2VpfPPX1H1D5q\nTOgvR6TOiiJnqioy+fQ12Wyo8IJK9OsP6oc1Pn0nGsHjtxsHNMWzqSnp6mDU\nY5P60cJtkCe54/eIUC3f7BzlXnVx+5lYPeLQsG72oSNP1hFqY7YiMH+7FUIX\nOQ7hYhdxivvNno/lLQ7b74xQOaWtaS0nnpIohsn1bW54xvQX1bdJ8Uc4DdFv\nJC44yeAmD9xJWcj/QDOXfh/1+zYkbEvzWukq0f/39wtQGiCEcfmNMYQar/Ho\nkz1O\r\n=DK79\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFpCYrwV0CbbmtMpYMqMx//Is8HT4k15ZZOiif1vkhbeAiBBBCgAjyic2qtn0k/vHx3o5pluB9M7fKQRjFRJpANbXQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-goal-lock.20180801010341_1533085634852_0.5452297013724918"},"_hasShrinkwrap":false},"0.3.2-20180801010455":{"name":"@atomist/sdm","version":"0.3.2-20180801010455","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e75a95894d02df71b29ead0d544fd2020e73b0f5","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180801010455","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BAmjiTk42SqTDjq3v9kFjvidfM/lwz7xotoFq8DJuVyVMaQJPJfP9KOKI0OmdYeWUjYP9NArcA8D6yedboa+KQ==","shasum":"091d376b82396025b4b37e10c9594657dbc5526f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180801010455.tgz","fileCount":820,"unpackedSize":1801789,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYQgMCRA9TVsSAnZWagAAG+gP+wfcWpuYy3Jt4bqzX+2Z\nXBYzCLnpL6U4UtBeHUZEb/3Jgmu/ljGt3dldiyMS63pIwgdc47SimYqMYap7\nfn+tApiyWoTuDDuBlk3NETXFVpXwF6XyKUSzX5ElcwEIQ/nmH7tQ8/wKUiI9\nernUWvLpWRWhHoGGuOioQFMCVRyoIRz6g/XfGEZwtJxZDqGVMIKosrMpt6Qm\nbmTX1lEeVUI7SfcwyA8wanrbLdLQrRlwkSWWH5p3ozchxC+U2cTLV7tFkfBc\nXfKCXJmfWIzvt8+jEs4qhOvjWtOE+tr6dmjzLj2JCs256Be2iUF2p3JHed54\n03x+p+XSeokeouuyoD8Lka78WvpEA1zfXN7kFd11j9Q1ldgQrIs/8Ag98WA+\nwYdeOJ1UiU9B3mLVnAuzcl1cY1834K+Z30F7JeM8aSsBpRRpKRxmVf0OFFdw\nDCDHQoAxamTLfJb/pDBQJEfb8vTSXlwZyrNFxq+aXvmavZJso42XvEGhqDaX\n/MinkP6V5MUb+dteND8Di0BMgr+fp7JJiDlP3oYESaJybz3qpcBHa0k16Kkb\nSxiuRwpMR6z523wzzmIIS3kSUcGTav6mcg3Dgpl94S3MnV7fGM4M5m76JIkt\nIypDw6qau3o5mWSuRe5QrBCHcKi4LJF7p/tep2dinJh0YvRJfEjVZ1bYjvX/\nmSCY\r\n=5KRs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEUH8cjCofups5sPHLzl1AFHoLUlixauk7bDsa7/bQ69AiEA4npMkJg2XlKw+0fCKKVSeOs0rdHGePlxlDdqVOpVzaI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180801010455_1533085708341_0.31556382653383985"},"_hasShrinkwrap":false},"0.3.2-20180801093406":{"name":"@atomist/sdm","version":"0.3.2-20180801093406","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c6cf760e749e22330c5bde3b4f0829262355a344","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180801093406","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-j13d9RGJRhaz5Qor4QpddO92DZLQLHoAX63ZfBcXqiKLdlMnG3ALlkDVbWg0iOm6YjvqiAirLuCkD3QzVFr16g==","shasum":"c33ec1ba7782a085e9992ae2c260045617d5c2c3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180801093406.tgz","fileCount":820,"unpackedSize":1802078,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYX9UCRA9TVsSAnZWagAAlgYP/iaAfKBILbRLJtHmkI+w\nuOCgbwLNcDKfAorWBvIdcF1sVba6dZ/2HMdNbfHmkD0OE0ORUqnISFDhhigi\nSZx2BNvgQKui9iH0akjP9ETkdNcgTns0/LwMEIWrsrHsfnlj1yBv2amUECWb\nfNn5zCJ6GVnd6TOnaoQ4np+IXjOr3gfttCW3iVMp3kBDwg69Usrt4MycBeyI\nvqtLycfXJo75+vBLTV+Ck07ieEoAW4r/yL9QLeQHgO0EMdWqlTyX2ajLzIAL\n1TbqPySpurIGWMgY+k6CjUmbz7ox3903fc29dg88ljDgsZZ0Hf+U6y16+1Iy\n50pfbzsM/AMq9f6PHQs5WF1WqUB1mHPeEWlqDZgHNjwwHC9vTqrQgh/XPKLM\nlh4aayrEu51bG6XvKcVfaehDfwJOkuSkuvHGd6BuRa2fSkCDDTEAQe7+cG6m\nSZCzmvk4rreRQtNNHmHbf3AnzWoKo5bk95WQUSEUMlbne5lDMUXWBB8UgP35\n1BZB6FBYP9fKTmrjiYey+WViznn4h0XM6ypO/2TB2fTMiBrArEN3TiwLxav8\nKrG3+7NFTa/n6AhFuB+MsWaMmoL6bT52v+cNAt90WEUBTyel146ChmGRaCQG\nPJspGRY1wxqi1vKt3ev7LDjaaNF9dQH0PARC/kqGrbOE68znxeD+6cULzGDt\nSUeZ\r\n=sSxh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCrpYd9OxnNsWZzBlRrCCv6IwLCEgnVhpcOaZG4825E4gIhALmRb0pTTr1Di0zcxb25CS9945AdE8GBqV9PusUKgr8O"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180801093406_1533116243724_0.2908933902736244"},"_hasShrinkwrap":false},"0.3.2-lock2.20180801100043":{"name":"@atomist/sdm","version":"0.3.2-lock2.20180801100043","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.18.1-20180728225753","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1ef13b20edaf18ba82b3843207dc8f5f95b99649","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-lock2.20180801100043","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7BRqXbwhik5mMXlMYB/kZI1Cz2NF2+ykwFjPnfEtNYziNk9JbpQOtBpKQbzGMgbr0LtztRs0Xqku3XLbIpZbvg==","shasum":"b38f1363503d8b649694caf82ddfdc33224f2959","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-lock2.20180801100043.tgz","fileCount":820,"unpackedSize":1801474,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYYWcCRA9TVsSAnZWagAA42gP/RmFSB54RWLoI+brN0dV\niw08lEKUXMaTLcIc9hrKaZ/z2+nV4mXv1h/p79VX95UGVR7xZN5QoLVhUZg2\n1rqFgXEobgktbCyhqlB9a6OqI6RzamW2k1lDrz5X0ytgLn9SH1bKL26NltGt\nkP7YX0kgb1L/jCSNWRwhMkojOcZO8vREkp4o3hwyO0zi1enyITyIYJy+dNve\nK5zD7W6EvkBxftgsPxmPPJRJkiMNFHLnS/FtheLcXqpR3uTElKVa2LXpLrVx\nd98qDR7aC4t7qyRKdnCivD2MZFRp4B0XV8JXfaczS5FK2HC/g35ls7csd57b\n3TRGKOKEnSZtlCWAIS8UUuDTLQXnIqGvK2m5ZdlUt+tArBcI2PfA/ODDiQ3c\n62spxmZToQUOImqupuc3uq76jAohp+CzUYMV7bgBBnFk6r+qnyMQU9tUQ62X\ndP+prjcvgQPkGP/kkPK83qu9X4ejt5bhm4WuGbAkRi7QFupMOjVJUiKIrDgn\nVPIJxL1T3KSMDx4xnJDjP9InQFOTpi/NMMagE6jr5ItWHpq5Qk+9X8fEoj0B\ne34yHJjSw+jfGWuNT0l1sRwmpA1l8kgCCB4pzh/W82JBAIttBs8WtnHCCLyp\nDaSeoz456cVwP2aV9ACG+ZXQzo8K8OzjEmEWmMqEWBXpdJs8camhvxC6kiYH\n7IdW\r\n=4Kkl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqQifqjIfQ50FC9V3mMGC7Dm39xTDRel5h4YmcU8b8DwIhAOK3v1y8HkVgjkEPusa75O7jgewKcKQCbXcQz/ctEhnR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-lock2.20180801100043_1533117851929_0.2572813428805061"},"_hasShrinkwrap":false},"0.3.2-20180801104145":{"name":"@atomist/sdm","version":"0.3.2-20180801104145","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a5923717305a969daaaa66f8b73a0f02692ca89c","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180801104145","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pVJcmowA2BRzeVeWbLokIgKBm5Asc6B8jcuNhNXpXP1SvdzVNciiZCUj4l5JblSzsI3fTAJmtIB/7IfUuDWSuw==","shasum":"143f7d23e04aedc490400f3e774c8fef47dd20f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180801104145.tgz","fileCount":820,"unpackedSize":1801757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYY8xCRA9TVsSAnZWagAAXHkP/jHUaYfAGzBAxsn7WEcx\nDhaKck4wlVT0BJqLvVFlSqNBd62ucTUVivQ/26X+E1qyEO0gS3BfZ7WQsV/z\nYFh0UE48u9u/H1FhIAxVWtfbcM1E22M6Yld74UV+UEGxyGbtpbVgmTJ3D34T\nm+TN5Nh6G+HoIDejUibJhkeaxqsfWoJxdqJeQUKAYJ+8o7oeCwg3qK2V0k0K\nzX0Drm0Y/B9zQkMVo/1gcUR4CUURJWdNINWuMWKwzioczcHnmNgKl1XC46Gs\nPa8YpHEJ3SFanHskL97wvllD/hEQTAMOKq2ja5tYBIZ7HGYmkpJMo+4HtE9N\nvvPyMrbdWFMkxbJfw8GgI6QIFA7bVnNk/o+N0VU7cumqAFCN4s8LmAbe1Y7z\nJmxIsoylmg44A3hQRGNqFAuZD1orMOHoiobLIbFzV5MVWuxHw27Nwqk3gfla\nuQhPrGStc3NVMuFtHE2kUqDJO9xptZvF0e/hFaVRV65tq5L2nbFcCk0TaxiQ\npEdH3VKabukHKJZRZGUc6VmpdUwj7+XotKFXKWwt1OKaaH6VSkJb2KcTHFfO\nHnJpFxN3ZibzLpCK5DsrBUGvygHtjEUk/Gfh+QNFUmHWL1moTAhWgGLD9ZKM\n6kB3ZJ+m6uofa01K118enhdqUKT1uuUpwPoat69WC8z6w1YB/KssTN+Zibpl\nNmAb\r\n=Ga+g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHe2PH7h8oT/bvO9gL0WHpNnq9xjaL18xt/w/G6dLiGjAiEArHi1eZ06VjLERFeZmygNeBvBvJtGuNu1Yj5mayB86PE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180801104145_1533120304460_0.175639075810206"},"_hasShrinkwrap":false},"0.3.2-20180801120305":{"name":"@atomist/sdm","version":"0.3.2-20180801120305","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"12bbeb3b439159bbfc2c26368e79f75abd9a72a4","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180801120305","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hVrPFOuqTd3QYI8W3O18/PQukyGoRuK8yyoOjW3OzfzlUAvBeiBmOuq5ppRDVAnB+32JseAKoozJIXwjaMCEOw==","shasum":"a09b3c317c2ecc027102336e0ba87f628ce89aff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180801120305.tgz","fileCount":820,"unpackedSize":1801757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYaJBCRA9TVsSAnZWagAAd6IP/0iYAqch1qYtGPxUzn4t\noiGzrUnRumOojD4GZ1EUc3vDYnCSIqdUjiCIW5UZEiuBg4Fn8xS2yRMNE0Pa\nN2Ryo6YU5TkmQNjYS98sLVgKFnlhpS1DkfE/WCjs72g3tf7kFYBKR9BTOp4B\nKX7AGcZB9NZX0YLK1FlbqbULrHxME+TxeNDAIO86gSwBaIaPY+slv3mB24Oh\nQIU5pBcB3rklgLQos9b//vO1SKOfftU+/1S9v2lVRHZPY0j6mmw3vmST60jE\nnPzo31zfv49rJUddp3M8xMRqWAKV+edbXSMfLzIfD6OLHSeaGaFZWtvycEzK\nUtup4D4Cu9QD3RzCTIGDjNuNJwJBXNDZ89aEaw8X9tblZxuBtZinhxbh0L7Q\nav2pDd4dcn5Dhb4v8vDT1AbPZy7MtZgNaBX60lHwptQrzewkXsryhoEwFktq\njhAafErr3l40QLSA4KtdDbZEjUM1Wu6vt1k1tKHrX1uU3cokuGje0FqoUq9W\nTu+S8lkZCsYxdNZve6ceokU1KmurhTOlOPFJjhUIjsxu3TffGq9jwKRiCJCJ\ngP2W8C3CWllMU3K/PhZupHzgXmNzHdyqpbqa8GSwQ5U8Mga0nWDgzLt6Us05\nJAP6Tv9kwh5mNWjq51kHxAzh/pIcKgiIC4ixF8TDMye2JWMMxSzfIcgWmlW2\nzICe\r\n=9NKH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQ8VHCNqu7PFFngUxvsaSjdvs5dONBbTeec3awnX4TpAIgDrGlMMNezw2RvuhUAqt9+rVoAhlXNx6N5U1SXwCEuvc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180801120305_1533125184469_0.07811110239717345"},"_hasShrinkwrap":false},"0.3.2-20180801121109":{"name":"@atomist/sdm","version":"0.3.2-20180801121109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e288c7a31e0f95dac085b96e30bbffc784f174e1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.3.2-20180801121109","_npmVersion":"6.2.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0nFnA/fwV0U18VC2TIDgL1M3AUXXk9lDadTf16sxxirCG3m0oIUiIZmVZ6HQSCHBTQeeGeEYfgZmVAbaq/2JpQ==","shasum":"e2f712322f8c9dcd58139057a4962facb76aa56e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.3.2-20180801121109.tgz","fileCount":820,"unpackedSize":1801757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbYaQtCRA9TVsSAnZWagAAz/wQAIB8Le9fODvHQhhdnczF\n0ym4C5cnqkJzG2pPjAkbFV0HvFdgEbcR/c9flpWfO4JXmDjWk3C1Y6FY1Tiq\n9ykHnTtxE+yNAfK8Bn7HbBBjJdmlczbH+W/66y5+i3Hz/9SRLN7EiDfyH+VC\nqUF0sUelrfmgsCmZg50nGtLNtDwanfngi3hnLdbVa56FTcuUw+bhZcupqT1d\ncYqP4xD6SVxjTEBcjPkoRXx6uuKQ0uBocMHg07HLsnicw+XqY4Gowl2CBLdP\n4+Su2qY4h7vcOkFhqx//yfIrbnX1mc9BcZOrhTKFugOLD9qnQCU8Tg2f46yN\nG3kRC8fYnZylDp1UyXi2kNEEaamOW7P5UrMngeaUYlOT/NAbgqCFPEB9PW8J\nfnPKLNix1AuFaoZXLhgPAtvk0E24IHD11xEqT1hRQo6DFnkjkjhexDCkXVmr\nPwsZ7h6ldPW2UNv869XxhXeqLODc76zCZAlzdFEhwi+9Bj6LMNP6jZ/16aFD\nStRGGRSOIHTqFb1vG6HKJM2ItbIymAH1UHuTOvWD/ni2yX4B94RjgEsaTo2r\nN+mQMgVABxfblu4SI3qxt92GcoLGoRsKp1S/QC8Z+M9OtPH5y9YV4864qOif\nVovBxqk4ebD08HZQd35Z9c4IKHa9A62U2E2k5mgwwNHDi0NH/O7gPQvNB9R9\nTl6C\r\n=Z5ty\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDtda1Vnnkk+dMJM0Dyukz5iE7MvF6QhzcOVyYKTxvD/gIgEZlWd10wn1FaUeAVrwfE4EoeJcyJDuQ4Pt7D9tIxJsk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.3.2-20180801121109_1533125676086_0.8782704128892129"},"_hasShrinkwrap":false},"0.4.0-20180803134505":{"name":"@atomist/sdm","version":"0.4.0-20180803134505","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"49985720cc3a091dd2fb61f57b0680dfa699d99d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180803134505","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZcxhZvzvfQ/eLTR9uiPCftRP8ELJLT6mOc4sOcs5pxcrEM/5RxJRqABQ7tiCxFF92wi3YofB4wr27QSarDfH8Q==","shasum":"4f53890a214b3425665698f0bc4bb1b9d5c3b06f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180803134505.tgz","fileCount":820,"unpackedSize":1801757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZF1GCRA9TVsSAnZWagAAEk4P/14BJo7vDHnM161Ocs+x\nkF2kPHZEUUm5Mt+N78Ib8iq9OrGZE388JiN7Vh6bnBkMobm7QEhg8Cz7nacY\nRwNX+kSljwUw5HBAkoJMnT1yn6aT2zv7UlBruBj8Due7otCFE6ELxp9e120I\n4AKnFv+N5jUFdgOveO+e1MK0wKvN6n5diVtqv8iqJD4ci13nDGFN+wJyAgAT\n3Z7EWeH0F17twhuCReFcR5Vp21kLKVI0tPGQnk49ZhsWFXyOGDxsQMyT2xPN\nxf6lRKlxLM5IKjmHJr1+tDmM9BquZ5kiJN0UIx46wYQ/wQgXXrVKnOxRrPUl\n+Id1OUbFjmltiuTGsJc0Ktghjd4GSWfp0fho9oGgorCjjuklhg8CLbsHuQZL\nZLyBtw9vd8NMyOizwZkzWu6jMyH2wzr/IDvKQn3PB1RZzasrmFgt8A+FET/X\nzbUgu5uFFsNK1VIKHGsVbqgtwAj9DbhvXeH9VMP0QflRoPor7ZGhHnI0wV5i\nMe4gMP5rthxebAn1H85vGM4f2v8sFaFNX3P/eQbSJ3rLWOFYcbvqrbCc9oAB\nqOyUspTfNZ4nhqSJ8F0sRC/ePsRikdwgcTwYw+U6Q+Z9ltBcm/xePZqE0myi\nfGEmXPdgyYcyfMBYnH+Sc3v3/O3qEXmCpJUH9+7+rXhN0AbZO3tUJpik2tyj\nkasO\r\n=vxLD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD9mRuPb3F2JU6XwxDnVsC8VYrwVzuUsch0bsonYNiVKwIhAJKDERHlHsl3KpgsgiFsZzIZDpKHcW9nXElVECTB2X0R"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180803134505_1533304134315_0.8861615859633047"},"_hasShrinkwrap":false},"0.4.0-nortissej.default-transform-reaction.20180803220626":{"name":"@atomist/sdm","version":"0.4.0-nortissej.default-transform-reaction.20180803220626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"51e3a9c0259d99ec39a419abd7731b6aa1c5b51d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-nortissej.default-transform-reaction.20180803220626","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ICmL4soPLlol0e8gj+awUkXg5qsx2FZbeBZs2WCFxdcNifx36Jkaj5A6IYrkIXHxAxMu07s+nvyVxBxMy8bZSQ==","shasum":"ff398e1e94f2a2b54f49c8e43147884d96bda4d6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-nortissej.default-transform-reaction.20180803220626.tgz","fileCount":820,"unpackedSize":1804232,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZNKPCRA9TVsSAnZWagAAVe4P/jySusNSO9wmPFlyz4w6\nKTSCfnInRZXQnGRLqLoku1qz4iXPgzIRCnlqPwt49pF4X1YDD+RQzlawkFgq\njcsu3h9u2ofKr9QzG5vZQuMU7fAoe9qYoN6QiPYH5Hc+mkNCHoYDu9p51N5G\nk8h/irgIeD7E5Y86YfJ/tAMPktMDHGQK5ddxrAkSOWwtejibaBQaqMMEC28c\nFRe/Sz7+QlnwJcjHYposxO7So46jvRqURi3fbDYERYvydu+bUYQwYV+0BTHv\nth/ufsyRqC1AYA0LvQpG75sowjPYhNizh3SHfNoyEZHdzX4rYD45JieCFfuY\nfImUPbDmgHGMH7T4M9kE4hooAkuSAiA/YCFiPeKhrVaKjovy5y9VIpYGNwGS\nwVMVBpNl5IZ7ZC2bfm2MDBhQv3nQ5Sv5X3+ID+8iZMOgfgEn16dgIKcfpqkz\nad6JsPw0+VBHR1hIUhkV6YMrQR72wT2OgLoKCNFd3M7Wj7ABWS0Argag1Ny2\nKzQFq7dF1X+T5ZAn+DxgNDbzXQ2eMio4j9r8Q56LGEOnN9paKu5xtGKmQw5N\nJUD9wW3vUVZgR8hJe/n9/ayWvXNzH8gf4vQDdbxojyTirwt+ToWzgOxL0a8G\nvxdZO2G5B3828RrkQ4nW2HkL/GUolAUrayMy1FaoQlb6phZfeTrY58UBtfwI\nYTyK\r\n=IKm2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPf1bS8XISPSPb5kfJHy3MO87GJ9MY6Rj4q39kPBzmQAIhAJP/qLWlrHZZIWi0KEPA8FnaSbDZBQs4VI0wBUMKxUDa"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-nortissej.default-transform-reaction.20180803220626_1533334158313_0.614193134290308"},"_hasShrinkwrap":false},"0.4.0-nortissej.fix-transform-all.20180803231953":{"name":"@atomist/sdm","version":"0.4.0-nortissej.fix-transform-all.20180803231953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"71a00e2f437d795f51d25217131ed87f132ed870","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-nortissej.fix-transform-all.20180803231953","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NLGBbttcZpUkPLOpNG3eINFm/GPryfRP9oZwhZHR5KjBRtO01FSJf7mq0FPZKOcXskobCr7uwT0he2B8uavUIw==","shasum":"c7b9d020452b0210cc83a7775b147ce13567b613","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-nortissej.fix-transform-all.20180803231953.tgz","fileCount":820,"unpackedSize":1802520,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZOPjCRA9TVsSAnZWagAAfv0P/iynyqC1Mc3IFRcertAj\nzvmScOKkT6H6dk48X1kzhLAhfjfhd6AqTRjgrUXX7F0BkgFyI/ZVs22Ghreh\netamOphcdlAorweygSfxwOPzne18Z+EAF8jjAEtzSA/A6dvMMgQ9WODGxOaf\nKydMd9vy6ikQJjg+LMr3WoD1W6god2QKNT1zhL7TuWjb/JhqSLwV6+C9RCBe\nhEjP4OFXSX8selxmaf8A3LDiUPfHRvGCTSCp53Mv7fQT+wdMq9TvKnmeomre\n7UXd3t2WBhruKNJO0tZpDDblPLnKfal8+Md9m1Csd1bPGrMFLkUs1b3JsGkD\nZQxjPBP5KdVzXTdeJLOlFlW3e6dCyxky12HpHJ8B+Q3B9160vbq4+ZGi4I1F\ngBD89CF7hrj+6WEiJjGcta4DyLsj9PCG5PahlKxCJrZOlXvXDLVNZk9gBWvQ\nfyLh/MyAelAinOj7DeZzgRn2L6iGqFSP864IVKvzHAZX/ZHfJzYmZxAPbdRX\nSidQw56bfOqFXgbMzhFjglwbZmiavDblxkFy2Li9DAcHZYeUSR92BlvWTFiP\nu+YfohzAUeIZ99sfEbsWKCNGoo/tm9k6AehKBl4ENqWn0f4zW1pfC9GA6o8K\neo016TOfBNCdS4VGNajfxeCH+xihLpnfyIl4/YNj+Ic/4Rj4VPiKgICiXafs\n0ZXm\r\n=YBvt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID0j6ThQ4xskOMMIiE1uFQQRZG593Wi/LfZyMl+nWI/uAiEA21vAQLUl2PC2eDqJCNQbroRKZWe0h2mlNSVijtXA3/A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-nortissej.fix-transform-all.20180803231953_1533338595343_0.6784131264487419"},"_hasShrinkwrap":false},"0.4.0-20180803232455":{"name":"@atomist/sdm","version":"0.4.0-20180803232455","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"091e631796f588fc23771d9097d5ff5baedd1fc5","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180803232455","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-D6wwyh9WV7I0ss2ceRT5wqf5iotZbP/foZ3CYVvue8oxuzXKCkzHMU01vBYb9Dz5XHEF1LT8YnVz3RLU03NlKQ==","shasum":"9a2ee8d1857efe7d021fe1a93cf17efa982f4596","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180803232455.tgz","fileCount":820,"unpackedSize":1802492,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZOUQCRA9TVsSAnZWagAAe/4QAJ/0oj5Ua6UZpXJIHhps\nPyFU/O3lo862Kh4FCvAFzp/ar8Jg2/nWO738C3TN+PMi93ysbVwyRkm+ksEX\nEikFE2ic1MjAEpNup31qAFsiJ/zTM+PRY3NrxCbS+79EzVFCG2/QrdQFHXWY\nQ89kpg5O8g8GQ46/0BEbKwTJF9FshaDy501zcw44y82ZOqyI11jANpgrBQpU\nLNIGuFEfUM2i/WUGfuDW6D3bLrdkvPmnbtFVtq9xlm045EfNhCW5S19jIqkY\nr1SDp9C2YNx3OWXetRDxbgoFbZl1aFTg5A1G3CMzb2KI4om0ZTgflayF6IsY\nlgnjPC8/0a+c6cYaFSKi5L8F4Xt67R4GgEWq9sx0g2bFkBgmzvW+j1+50svN\n8zzIgNZ703nsa7lhUA9IOOPJ81+IUKoJQzrp2ylhqInTJkMfyS6m+4u1jYxi\niDgBmpM7dBQDdQ8IRLqTKYi7w8vLtjeg2BuPzn35hhzq0DbHXO7ort3RlHFz\nO3F9q2Z6qY4u25I/SwKVTcr9R6B31ROFnzNeSr9Qu1i3WYO3ZH3DSWlHJRTR\n19MufH8Z3waz6ViGBz4i1ZiTnCiojQvP0K7s1Zo2Iw8A7YKn2Z8vFCYyxh4s\naroDxGTLBwJeVu2zkVxSigQBDXmDFK3elzv3WHx2oONgwA5JrT8ekIZN+m/s\nBdEi\r\n=QSn8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPCDGQKcpmU3p93Myo8qUlF0oJbNuST7aBeudQdgvtkQIga6eV7M3FfbyFQaC5UiqrU+/gi9EWhWlIWtinNAlbJH4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180803232455_1533338895984_0.03066227534076371"},"_hasShrinkwrap":false},"0.4.0-nortissej.default-transform-reaction.20180803233032":{"name":"@atomist/sdm","version":"0.4.0-nortissej.default-transform-reaction.20180803233032","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9c834550f4f6830893252de0b3eb46b0e4d32d36","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-nortissej.default-transform-reaction.20180803233032","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ngOV2/689S5kRoshhpDSbg77pZEdfZxmlqjRN8AZ0I3lXdGSOjfPomoxntLS2C4d5FQo4C4kWWMesHQrEuq+Gg==","shasum":"b33f72024be76d9860c108fc16e0312d97af5b20","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-nortissej.default-transform-reaction.20180803233032.tgz","fileCount":820,"unpackedSize":1804336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZOZlCRA9TVsSAnZWagAAe0QQAJsXhOuKwMAfI8kkLuMN\n58+blF3Xtu9IKiQMhbzaXGibMFn0ILNdnChNpdiZJqgJrxoY20oZHPc3CV9T\nvcOk34eZg+D5/qXmmO0SAPDbv40+f3RPCjTrKl9cdEwm8jlE+LanzBDTR/sv\no6bTkF9DOv9jLmI0rXtYhtYfvo4lrc5Gt6SH4WS+NydWohA+evbO4HXt1/Oa\nE29M/bdteetJW5Evjq5+E2xzYmdEWi4hRXX/XWqXXYoHy10CfmeAqbIv9qUG\nX65/rlN7jRA/hEvyUSCMUQImqoUvh+G0r5pWJNSBrWPOHFbsdcSQ/dzTljJm\nW7XqYwDSbKYJ4BiV50VzQksuZ2o8Q4ktHPue9Mk4IYBruT3wUh9XkQlOBUNn\n9JosANYBSr7eVDVREJarVXlBhm7ee86fF2BSl4HuDpvF7d4b4CU2i7WC7PXT\nHqwN9sLs4bic85blKSawEYECm9WoooO32OydMDf2b9lXlL9zFahDk+MHIX6E\nSsctGH6vyVtUyah4AegofWU5qBcP5GWcPCpgr8jilMSPVgNe1RS0pfr7shta\nin6wOdy0dLCVzlVhxpOlz/F5WTswV/sRgjaYtIYqaVrL5G+ozQSLqldko4I8\noeCOYVCfwTVDRqaFA2GFwE5CqV9NVyHBGU3se+g5fDsvjP3gt9wbKk9lzFrX\nNCNb\r\n=p5uN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH/tfTemfRW5Z+quLZWFXowbP+BP6DNbNmzYrQJKZ1qOAiAlBKXcqB+rA9YNClkoKWFkU4PYTaw04E7lGvGz/7jD2A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-nortissej.default-transform-reaction.20180803233032_1533339237187_0.46098127781121545"},"_hasShrinkwrap":false},"0.4.0-nortissej.default-transform-reaction.20180803233624":{"name":"@atomist/sdm","version":"0.4.0-nortissej.default-transform-reaction.20180803233624","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.19.1-20180801085032","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"efb4387cc72a39a21fd8f4d95d541030dfd070c5","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-nortissej.default-transform-reaction.20180803233624","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7DYa/PZqtBJ1cYNJfdeuiqUYN/10GCmvVHPcbET2Mt2IQ4kwWq/Xy4o08XxvgsQfz7oM2f1NKbqmvWkbE4gNIg==","shasum":"c8bbc8a04473ba53aabe73d5fda385aa40232ef3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-nortissej.default-transform-reaction.20180803233624.tgz","fileCount":820,"unpackedSize":1804416,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbZOfJCRA9TVsSAnZWagAAR64P/i6U8cxzxjLGVAGe22PY\nvEDRcgW66R2gkfTAzDnTMZokwR2b8M9Y3iVH/CL3ZmZy6XRZ764O4FTtJXa8\nvBJYiVPreDy3p7WVaLOVRQMAZf8bKmvauy+lpMLMmUvwpAh3KTWTmUUlupJg\n/V/bTNy5mzjZvTtxuOHtgMN7w8ywKjUPZakCwCl5pf9pfFDuaDivRkjt4M/4\nRH+52B99FIMHnz0+/19pQ8Za7lhECkX4jMYqsWEac4z7PASx5CmXAu6HGhQP\nlyN0kJw6SSSwf/hz08tHwhfI+/FleUn6GEkG6zrc1sQRyZELQXdbOT3YHxXe\nUXFpct7/h2iBNFt9H85Rs8N7U+BFxJHgeKEKEkaLuX31u8EJ7Z5K9cOu9AOJ\n8TrN2zk/RkwOhumxOiDyM70z9UOrB+iYVDh4zazFYXSHt64EhoM0WarwNfSy\nVqdFLK66AibFUQ0WR9gMg8SJUQkWLtWYQP6sGudSItGfydyznUY0iQFirtYX\nqpvYZ6jQ3m0d2VBkDDxrcyyIHS5BtxX0uuGjTMhGg4xc6mQT9opw9ZiQipom\npv+Q4+URx54J4tSpfPtGHh0iOhOJYRAOMXt8s/4vmp3gvxGpIvwqnkdjCr6m\nA1hGCSuePL6GuF+TzrUOzInLEQGKdCw6SOfcShssz2qjpSfEBAt1F3MUp8GL\nqGKE\r\n=M/n3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH4JvbsArjnszb1J4Kq2LvE/a6G1CKgQ/jIs6BDMcO5ZAiEAjAc6bdSroM22831bhRP428qG2fVe5ssLLpgeNy01UOk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-nortissej.default-transform-reaction.20180803233624_1533339592445_0.5312141381414532"},"_hasShrinkwrap":false},"0.4.0-20180807022357":{"name":"@atomist/sdm","version":"0.4.0-20180807022357","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5-nortissej.report-editing-errors.20180803213716","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9b00034b35014381be914ec3a6ff501d9ea30ee4","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180807022357","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BaQk0Xh66X2fryJMACWUR8rK1AhUsHq/7NXbLr2Na3/7b8kNRzr4tkl191Pd921mpYMldzogiYVcUvSxTodNXw==","shasum":"0137bfb895c0f411ae3ed4afb9e6a819969eb48c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180807022357.tgz","fileCount":820,"unpackedSize":1802611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaQOFCRA9TVsSAnZWagAAWY0P/3h02DhS8Xg+GKfdfXQ3\nINA9hfKI8jEiNx3vIEcc9sfjHLd43AQ7BI69JZ7etRj9Zs1MQyjlaqQ+aAaQ\nyMT+8hcyeIcl9I2zEkUJ7bwGCB18xv1dyv/EzmEkL8ylt882GYvqrdNvnvFR\nurnVuUwE4exou/jAbdljz76DZiPurqI1rVHYXx8o+tshIwVNQzwNmGYkY2pz\n2hy8o9dU4UfNVPhJVj02MiLhp9Pgpaxf1qFjYi7lBvnZa5cYikWeuaUgawnc\nF+OGmSA4mZ9FeSILjasuM+1qtmnxg8OZvOlPXQWQmr85zruv9jFSY61M7QiQ\nPMe+eAD0A8txoOVXaKJbuRLnZ5defCvnp1C38g1jrGZg6d9G/wnO/xdZyqXF\nyoKYaxMvyfZsAZXnGE//S5nRHHv3T690iXE69rH30RNgFH4mVIRrHh80Tg9/\nWpUCj/dE5fhx0bB9dAAE6O3M5+11BjL87JY89fj+/Vo2WAIBHP3MA3RDjP2y\nKnMtiFBztqRIT5KRZ3P0oTWHtpj2dUVcebneB2PmOzxcUVe9D1ksYwoFmhhb\nkeF2OngiFM7V33P04CfOG3kws1sDpMYhZIvAhjrnntKbq7y0h9s3jx6W8VtG\nRabQ5MjCPcOBssqP/iLgliF75ji7oXasINCorRS1Ai9l2AGTQ8f8AV7aNoDz\nsPK8\r\n=yLFt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPGOdRTl8DBFfqa04esd1SeuXqW7wuIBfey8+uXDjvQgIgb0Az1QBlJws/kSJFXNwjYuDt1yM2RO7SaI84HlJ1r9k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180807022357_1533608836383_0.9808973924559763"},"_hasShrinkwrap":false},"0.4.0-20180807022858":{"name":"@atomist/sdm","version":"0.4.0-20180807022858","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5-20180807011450","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"75971f2bfac2723009863a2fa0c7fb49056c5f74","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180807022858","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Pb3T6XX6wV9zCWx24PThMWyDUz8BcW49UrDAT1UDW5NzPgpeMDBrp5j3y4OPYBSotPJcBfsQfuKqEXJn9q4Tnw==","shasum":"02a89ce542f669435c818b575795b3b268e31c45","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180807022858.tgz","fileCount":820,"unpackedSize":1802579,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaQS6CRA9TVsSAnZWagAAHMMQAIc6n6JpJhrgWmzWAojl\n/O8z9LZXLg0+656K+C3rHdqYY1ijPgtIqoGKPa37cGSVGieVpHtIIVNEkNtU\nM5xZwyVjijUNpPbsHsGRqjatgrHccRe0JKn2JGTrrKSN01YCwUyYoC6dBgFG\nFLjEr9IpB32GO7ng1hstRTJpqho/wj+Xk3arcwa79soFIteQ0WBxPTJ7XWGe\n+NDOEM3DiG+FXVtMVn3+h2iL7BUjxtOExCt9kMCykOP6vqGfhR6OLpViCUJu\n0m3zh/RaC8zhxgFX/wx699P2EUXlDfP+LxvYGQUmEDbAgA+KfjL36oVJlgu6\nQXDvNGvPc8nRl36NhHnb3w+RE+RmxGGibEMpamtm0KuSyBpTY6/5qniJ2CA3\nC97gPa5POfUmEJ3vezPF79sMMHvE9VFIpAFdpKsWub6zuRLyaYNp9cku862L\nkca0/l1LpK039WWoASkxhhFNUwf3f2l6IMmErHDRKtc+lr4mT0gwZFQPL6JS\nvt8yuTDJ/B/eIPwcj3Irbgm4XCk9MD+c1Fn8igysKf+FjG/PnU/MYl9HIpey\nw19CGyazVd4ezI8z/P9fGNXGqfjZyZCUnqC9JiccocfTqYHJe4pIfL4KefuJ\n3fqBHZtrDo0XnC6LNbAdvbPwoePW0oe4rtNE07EZtibPwb00UBFdMzACqp8w\nAcM3\r\n=wS/K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMSsmgYaJhY6RbOoi39AfnrQW6EhHdAGntonHJ3psYewIhALwbOoZenKs1nnen98XdU5o4jYfkVmL48PK4Dap4Z4Xn"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180807022858_1533609146109_0.516724298655145"},"_hasShrinkwrap":false},"0.4.0-20180807090053":{"name":"@atomist/sdm","version":"0.4.0-20180807090053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5-20180807011450","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0a6f3b7da8da941f2b49639782734f42f4ad3f9f","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180807090053","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-D9n1nWjxOzYp+32zsfjjOIvqLoF7Ome+gLvg9cX+jtz/6cDDE3I+0y+XIiwu0ngTbL6rho3vwsASq1GmLM9R0g==","shasum":"a1f6a753e15c024d59529158d13db67025ad1ca3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180807090053.tgz","fileCount":820,"unpackedSize":1803921,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaWCMCRA9TVsSAnZWagAAi4wP/05sCy67gQztmKjejYQX\nvD0WtKOJEbgnu7trUz9MOuPYGCidLUe0Mp6UvlxwrCzQZPEWvKALrsRGQSaD\n4MhPCTc1AbB9reOEJe0vc0S40S4PFNkyY2ETFH1TwXdOkIot4Bz6LSwh1+7S\nQLH8APhGwC43jI7gfmNnwTE2YyrdjIS7XXhRiNeoED3soMS0zuxHaryaSjzj\nUH0PQn5c+aPd6yXgBXJdYdX9Nu++BhNHzJG2LkdUZmHrtW+6eWAETKoW8D3A\nNGCKlRqfRM1sIOx9D6tfSxpBu8+6jX1kyNpFmncsbncoCyVLvRo6B1t9huCK\n/ocUROslg8YyVGMpfe3R/CPelhBN5IXKode1NLV2IjJtVrife/dxcaMyuLQg\nAwkp0O2DuSySUwF5GytlhiJJL4vGtMzb7s1U6Chh+G+Ltr8W3qMS/S3KOl0r\ngd9o24bO+kNRzI9hP+cNc3qPMAzZMulh+Z8d2efuLf/yNUF0zNGRElF9WkLM\n9J8TEoC/Qd1p2OfeH/4aj/W/C18rotdV9ZD94Y1xNvWDdpf2ZyUEjXocOru3\n5hE384XE153VfMir+Pry8fxGW20HCka6Z6/aTU7OJICCCciLXIdXOIBER4wE\nkDf5bW53A4dqDRBnUZs6fzs2rWtulWGLKovKDzuuhLnQ0Ua582dKB/VV5KRt\nxfSx\r\n=ovky\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCxZ8k/DHrNHVW0aTAc3UR4jJ1ZwmZ/ZCPhLuWBnzPcwIgNK3iDYHFM9pgJyup4/2dhrppNsNelYUVWcu98Y9QEac="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180807090053_1533632652166_0.10071152300089059"},"_hasShrinkwrap":false},"0.4.0-20180807113905":{"name":"@atomist/sdm","version":"0.4.0-20180807113905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"977af5fe80455527c1e635e0a3272fbe1b0e91c2","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.0-20180807113905","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zDZ++7SvTdafYNXW9LG+2wfBSVCDFQ5HHim6czoru5kOxLMncO2+oEXSCgSkHd1KpiTABR8UA7fEgR5s/jmLIA==","shasum":"d823d3cf02b9871bb655e4f0093a4de147c34786","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0-20180807113905.tgz","fileCount":820,"unpackedSize":1803906,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaYX2CRA9TVsSAnZWagAAQfMQAIrucMC6KGSZ7SK69alv\n3OTr4Ph8HTUQp+PbxG1giguJb3rjxl0hnlDOnAObPzg4iYT63U09yvfWwLgy\nMfc6fjEcbhIMzQQ5OjCy38BGP9ya9px0eV8agfhqB/nw3+B/59oEvJFKFgrs\nOAlcJta5yV9QnUWVJ9cBIyZvLBID/IRWeSO11I3aCPHr+RAn/vCGO1B78GQK\n3ig4qM10QfRKrYNC+zayCOHA9ATOEIUgaPtaKc4/hRDYOwwR0LwfjuKQZl1g\nW3eozxViGJ3tSkXKja0M1X0/ODayO802BsV9fVxDQnpdKG0WBPNSzOGzH+aP\nY/DUQFlg1aF68it8y26nR4eKn4ck0Xha8XunK2fMnmbac4WSs87kAROHECuc\nHvU3715p8AsMYcQxQEl/qhJBX+cxTnchxXrcojqnsEsWyS3QO5+RML/8bMC9\nWNNU9t2bcIWfR2TctVJ0pSpVBQIXTuZ1lyfwfOCRxUjrFHiIdZY/bspqC8go\n32TTkTix8DVUOKBSvg4r2e/qcvrr7oVWXg0Z6aWdOnOMhu0KmDjqGQsHHlSj\nzteqrj9r0gIftAU7DAxp3yaGQGmxTVRJO0niOkJ8M5v5PFbDHPNxzMv6BVs0\nSKgSjxqZOb2Q8ObSzG+ceCPFk9jS9z+3FXZ/JA9OdnH6TRE6I8AIzz/kWuMM\n+GzP\r\n=1Kfm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHR7chEhJUkdITTwixFuvWPU3FgvnAgWEXw1Wg0nrD+3AiEAolDx/ieJ0kOyIpPhh5CAMczH+uTiP2PouZ4QEoXHE70="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0-20180807113905_1533642229706_0.9391700355810342"},"_hasShrinkwrap":false},"0.4.0":{"name":"@atomist/sdm","version":"0.4.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"977af5fe80455527c1e635e0a3272fbe1b0e91c2","_id":"@atomist/sdm@0.4.0","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JyDgGTQxm9jcleD0YBP8oa1x+OnUtT5AX+mdRCwmmzx1y67vRpgiSNKLCegxQ8LY0u7JM8G0k/NWtJiAl96AHA==","shasum":"4543fbfe3711e174b9be7ed9331bcbf8464aa131","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.0.tgz","fileCount":820,"unpackedSize":1803891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaYjQCRA9TVsSAnZWagAAIscP/jCb2nslbGiCcBF/XVCZ\nXSFjVoQf0GLiJPAir0n4s+eU6YS8vIffHK31qvWl4D8i5iwh+dEsMFxnI9+O\ne8bYNvy4LVfIKj3hooW/3OtTbJ7AORt1rNC0nhdZnyneY+hHmTi3MDkpIMQ+\nr0J/rSKdp584Q1U2NWSzhxAuRCS4Lxds6s7GMyS/Ryd9Bmc/097ulWWd4Wap\nTo0p3iifKc+gZ/O3VPH5F4e+cWswS7QgDGPPS6Nij8vps+7j3Ktb7OGjf8u6\n7Z/IhPerviXEjeXJbZuF2wtvtZV2cDW0/WU5nax0Ho566plIvnT8+7kiuseW\npNVtVQ0Mf02I1zRmiGt7Ww2tUgOyTnHDZtL24J/SoNibw5iTNvy9wWs/g/Tc\nwVplYylgbJX1kJUXqdFRU6Grnk79xZ2GDtkQU1E6dykoLaqUWnhcWR+NNp2G\nJeEUgBWoWCDixj13vSk6dZdZzNv/goGTXoG8YTxQ7+e338Vzk5ebHK9mggJp\nWhxQF4z/4oIr6yYWFyoZFsR5X4YDiewcSLMByahv2ueQPasLqBaoOe3HmONq\nGpxNzCTWx+Mi/ZesebDAAJX0YCw6helVyhCbjNXMfbaDn4zEFw6bsp0QW8w1\nQfZlJay4lO8q6RT+qMDMggTCkunpLnMUsHUwop16wl8GT9qMuD89zCRM+ZCD\ndgfG\r\n=cZbl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzcrVyEnU8+ixaQAw8jPTDB8Oh6pZAHbvC0j5uK4fUigIgIUrpX+qDY3HbHU6dYTvQLr4N0yuM95FazcL1nS8nB6w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.0_1533642959355_0.22666341593141515"},"_hasShrinkwrap":false},"0.4.1-20180807115715":{"name":"@atomist/sdm","version":"0.4.1-20180807115715","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cbc8333ab48b1c12b800f8271efc177d4792a2ad","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.1-20180807115715","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F1wiVsQZ2/WcU+VsMEDoEWWqbzLuXO9ihOiI0T7sKYc5L8bdeEfaOHgOAiwb4qvi5Hj6zMdagO3TKIMLFgTTDQ==","shasum":"640d43671ff28bbaf588795407a6db2f1e613345","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.1-20180807115715.tgz","fileCount":820,"unpackedSize":1803985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbaYxvCRA9TVsSAnZWagAAhHYP/0qjXvo7MVL03wEKyldL\nfWwUeg1HgW5wRHrOWOg4A9F6+YU1P4AqxtQf+BnZccoyxcxthd+YNGFWqqAl\n0Rlt39ioG+6C4PYeXfCb8j8P3/BNrV+l7eFCDBBw9lUQgTL6vJ7TgCosRUg3\nMWxfQhZwJEAd+Mk7XNT8csabmWnDEP/ZSi4CIV5l7AF0tQE47lA9uM9Ux+Nn\n+O88LBpvjxDjEh/S+AN0FcPHWHJ9M41Hstx03+iFk16uvKN02MzEnDnenbuQ\nWhsAhr4sLHh6sOevOHG0Eb85RAVMMDPFnec2tAlq0UBt+Ai5XoYlxg5ASPY7\n+BXj2tBV/QSaxeOc2ALW9EzyXzLQW1mXf4c2I1slqaSeMGbmy7yTYtLElEYL\ncIGx8mYnhZYFSF984HQaAxLROxadCGS3DYkQg4rm5hGNrBIZbYx29brmVWvB\nkEWKWYlzg0s7O2ak532bcff561AYF00w5klOGGXC1aO6uw+W/hy95d3oQvmR\nxCpE9mbMU8IVeg8jtWnZYsWokfrfndHJ9aM/RMxvbquTFCtAlARRDpsASyBM\nTHaWEzOf2MkdGPxris1Hcacl5rFf65msUM9Wza/3qgx83EoJMGKppbz5iUBF\nckYBO06opr5TWkZzazbP8A8js05wyS8uW24fPTHjoX4BATVUfm+NSQIAhPXs\nB0wc\r\n=sWKe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID317/Ms4dp3d1Xxm4Kk6inmeYbSUh47gJrOBmVmk6dMAiAkwaSFZVhS81Ahjb9cy6LS1F1QqTZ8SJQBW3QMujVAZQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.1-20180807115715_1533643886617_0.5072512308109933"},"_hasShrinkwrap":false},"0.4.1-20180809172213":{"name":"@atomist/sdm","version":"0.4.1-20180809172213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"20ce7e8f46fd7c67dce8090e7c7baa9098db5df2","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.1-20180809172213","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8NktMs4pPMK+Y0fhFTiflBR7/KKrOm8PNkIYzwPqUI4v646pvXeFL43ljDWLEFzWaHdgZGsRdPnMZW6eIMF/2A==","shasum":"9cec989dde36281be6ef130576bc9f99a850cce9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.1-20180809172213.tgz","fileCount":821,"unpackedSize":1806469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbbHkZCRA9TVsSAnZWagAA3GMP/10p8GYMNZdxJUOED5FF\nG5Y5HuMPAv7SEDKpn8ozmnFQWoBvBDI+u5jTEFw7nuRIJ7Rmbv/UR8m+Dwrg\nawDEV1+IeE+J0EYJTqnkCRQkBKaonD3SUI6lUpLy1yJZXW14LhTaAhLCxc/I\nOigyTq2Ip9o7tbijSVnF6XmSn0N9ZcswU+0z9jRgBQEkyf2lZpvHDg+tnj6M\nMS34cqO258jHCb/dIZh7dPgTtbsYm/1QhShCMvnL2TpomfnV/9nmhTQmrXPK\n56YS5RLcchpOx1LCP2hW3hgI3Wsmmq45750EZhX83HpfAhsh1ur8h2n3obl0\npKx9QlyMBFtDoyF/f378vRxdg1dN0k5X2nVDzpzIpkZwDWRtfNLWZxdL7vfZ\nyYKpGDOkhUM4PZ77NXpANJ5XE3HpkfgqcmB4B1J36Ovp3b3SB1Xg0lN/BLns\nrziUzYyATtht8FV83ZM9d7c214Cq6hT5Zhcje9OJshwMV7eV3ZRWDHnS0kFq\nMcBConGUNKrZut8+80vCLdWw/0JdDyHXc2XTYY2OpjTPcgTuSPIRIPYtewu5\nhDpQCEmJ3RyvoDxnile6DdHq0q/TV7t903guwq8aDwyhb3a4bOiAzmpK4G80\nCnyTvaV4c73lk9ZhrNJt1cmqvmARUE9qHEtE4zNrRL0szdhvliG1VC6f+0gd\nsKs/\r\n=MnGA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHEI1LZYo4wxirwb6afIEF7ly52D2Z24nkJmH5rGpw1/AiEAkOJp0LbGSIoDH6kaJOmIngmh7np6RDZSu5aamdD60wc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.1-20180809172213_1533835544663_0.07591245281000503"},"_hasShrinkwrap":false},"0.4.1":{"name":"@atomist/sdm","version":"0.4.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"20ce7e8f46fd7c67dce8090e7c7baa9098db5df2","_id":"@atomist/sdm@0.4.1","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LJ3VI0U5yPjMkoIQuU7qBHwc8d267mLf12EiYSSfBv2U7lbdVGZYgYcIURY8627YgyjGBRijQewbvNdmPcqvPQ==","shasum":"cef9a859b8e18926aaaefbd2facd409f2c65292f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.1.tgz","fileCount":821,"unpackedSize":1806454,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbbHmiCRA9TVsSAnZWagAA39QP/35MmRYlJJSQvN9WnPE0\nGe9OkY6SkFmVHBEnqBMT8Zo2Fuj0N9EkN9klIpcjgDq68BUh2qOETKUH9EfY\nl9T8cn0L4bIAj7nV8sztJqD0F4seOwWbZStkJu/Dj6fq0aQeH3ZYJwY0rrH1\noCMBx9vQ+BmVUcOZOzlA4/siCEzJq2q/2fG9jCzGTGuwGfkm45AIQRWjqaW8\nQJqPo/p2yJbJWt3CxZKrvfVgA2EVSgDeBfNwYCWBd5lrdCDGqvwDPVsd9vms\nFGH0D6uGBhmM4clwFgpSJz+fggA7REhC6YWmKKiYtKRCw88GRnrVQa6074cZ\nqGvZ0DCvuhh+5sFptktu2sYq43rKkIL1TxggWmDvpm//h2QV7YCKBKnWPXDY\nsypZNKF9QF9pCKR3xNRoJVp8k32o6mfU+ogHr0uulj3YL+CSHXS04a5HrYqW\njkPpe4JXAlTSY34DSGa5frFswsTLQlwGVAtNNjljUUYKoZTn7nx+rrtmNngg\ncWuM4hlE16CX+0BiMlp/MeWYhssllPaYLg75bDAqypQikrP+j+SyfwYDGI1m\nPC1FRegnEa1m/YQt+NqPxq269xFDM+yO89hoNiL7gwFYEWUmJxHmSnE7YZOB\nvtS6RZTkIz1IBMLqOdydwS1OwodQc+4vjnhXaApq+u+uwcy+ZpPYC3F9SmnG\nDOek\r\n=IRo3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAxWpqjbypJURxM/9Y7JzUrjaFBreX5thwoVObHFAqp6AiAlU0kPtVB4V6GaW+ubFVCAPyD7hS0BkDJGz479l+P8BA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.1_1533835681727_0.17472467031911365"},"_hasShrinkwrap":false},"0.4.2-20180809172928":{"name":"@atomist/sdm","version":"0.4.2-20180809172928","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"730eddc90cd3a8ab802172aed0c9857a61deea5a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180809172928","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-03Rmhn79uSF1KJAmMr6vyEg0/DWA0yhA1ZzVqkvgj162ruS2Q5Z2tjIQW7mrXaHJALGF27deIfV2wGg1ibCiFg==","shasum":"359c3812c8730bdada1ec35c2326e56630193c85","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180809172928.tgz","fileCount":821,"unpackedSize":1806548,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbbHraCRA9TVsSAnZWagAA7ccQAIt9F27WMX8EDyKJy2UD\nEajvDVXBw345AiWtNY8zxJWhtxz/QOrmU2L80THlLn83cz63PNU0f3Au+Cfq\nOBUUZyZC6upRTONQQ90guxDhD4fz1xU/GA9y9Br329h7il1ieA+VCuBNCnCQ\nGdS/6PP4FyZwMY3PXYYqH9RUqUPoumtVS8OU7pgeZ+FqjpKnnDElV35cYX4m\nPEqGySiUWDRSW5SzD9jNYDR9ylmQE5cib1ioby8eO7FImzLf7s6UBYA5/Ml3\nfXlJLGVEYO0RCEG37rcGjddLxL9m0+5CYqle913tfJGU1KDw1oqE92iXz/dc\ngq0Y9eut3I1PV5CN8Zr3URGGzMnfFCtt4spzpFSxpKc7Y3JiliqcSkhYYRKd\noWL2dr2vNIM0Rc67YBBTHQjEwAwvUw6SSwSfyikm/7iXPWYjGVxlbH0wNAmH\nmzcxadgf7Ss9wUs1BFCvuAt2j7C3lPvJ+2wF5HV9V7CL0d6OUt0cHahfhhoK\n3Wg7iOHPv97Jy7gCgbz6HSKDMW96RDSoauWeFTe1Vbd5zANJtw9xFvwJd0G4\nA9h7vlYBTKWKga9Ft0qW58KuF3njjkK3zRC1FkJXJnaGfwyExIga4Xe7ZCJH\naYhMXvivDeheri97BjSM+UnCLtAp332wbWLPedTf8anNvJ6aFg+Z9470fSRd\nRtsH\r\n=mMcD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDmVg+sv1sBjkkzvWbCmtWkeQ2LQsgNAXW0jam9Ur6HHAiEA5JegKO/n0Ofhm69XCHRr+rGTs5UXNgumtGVLLzH5ib4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180809172928_1533835994288_0.27880862556439023"},"_hasShrinkwrap":false},"0.4.2-20180809210204":{"name":"@atomist/sdm","version":"0.4.2-20180809210204","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a567fdafc2d4f1e02615371369021d83b4aa18b1","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180809210204","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qx6aWJv3PvTRbTwICM0gA7+WtfoogIp0U1Bqb241e8jSAIzrqv5QDyf6CPwyH1U/hfxkU4qoFyxaR7xgdcFRDQ==","shasum":"3fb89e90adc5d16f9b544bf87c2f52aa41bdf07c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180809210204.tgz","fileCount":821,"unpackedSize":1806553,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbbKyqCRA9TVsSAnZWagAA7xwP/j053oeXvzyPWPppXMLX\nAppp7py154h+7x8blZVXQWRLvygH97vTN+bevecP3BKYd8CnAlJnFlQ+GTVs\nb3IgwnZce1MYf5P6kvhDCEbCQ1doxSioihYPHfYf+Z8VCnZDRTyaaaateF1I\nr0/c3B5J+r/RVlQTX9RzMYhg7lCpCg9KBdcpgTeONYzDG3afF5wx4ciAOdWJ\n0l1IzLy2L1totVbjAb5r/2MHg2Zat9r2+rUwoUdemMlOS/YoW+3ZZzjmn84p\nbkasEPSrjLvUb9caMvAAfZ98cFQXM+dMO22PrCq81mEyKYsZBPmxDPniXK6f\nr8o3A9MZlf9LncQTYK95nK77/GXS27Hc8siAOUXrWc1G190hYt/EXXyyZVmm\nos2qPLi3BWxO501ZxxrZqlh8LV7iHgNxSoh2EtAX+FbqK/HSO1tZ1mWdye6u\nVkBkBiXNTbbAYQ52+ebKn9P2Ldzokx9mOpdoytlFzIYa7JrPc8hoayl1NHZQ\n2bTGHNNngy13CEAluOoXKwOt6CP8AuVM9xUUBQRop+6/3cDw/zAqqDzdJagJ\ncc3iDci/LyYleVFfVcYMHrePktl5b+k46lqUKL8lqmJHc5suPwWPGh2XoNYw\nTPHXiBpgDl3mtWSTTGiQPxYAcHJHSHmkQ+r2pcJvWkvSvxZu9MEaDi1RcEmG\n0h1K\r\n=IkQs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUnyerBPfy1mmSR50KCtEfmzy9UwMV7H19W08FtBhVDQIhAK4o0ceHnFXhnufAmY6WPKmJO/NQf0qhZF0+J9cpjvGt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180809210204_1533848745779_0.16133691730695765"},"_hasShrinkwrap":false},"0.4.2-forward.20180811233712":{"name":"@atomist/sdm","version":"0.4.2-forward.20180811233712","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e948eef656bf12528045e7f3ce2c12636751a01e","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-forward.20180811233712","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-k1epg3gIXDUsc1coUbq+vAqztmbzmh6UEy+9Gdsc91SdIZqK39LuJCXlAirzguXm/fbOSG6CgXqVFizbp7at1w==","shasum":"9743d579911cab65b2f46230e555a3b724ed4f37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-forward.20180811233712.tgz","fileCount":837,"unpackedSize":1816571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbb3PNCRA9TVsSAnZWagAAHYMP/0487m9KTI68TQIDw//2\nrIxPFGzleUlDkqSMi5ibuKJdbf6Gwgn2OG5CmqDBo+H9I90n1QuRGdCB8l5Z\nrMkNV7xsyF9daUdD1XAwEyHf9Tx/z7klJfQek9ePxnpNJy/HjbITFmI8g26D\nTq5IE4eDxCZUXn+cv21W7WB01YL6Au4ffMOD7nrXsc1vjIN0T0n9zQxOwtb0\nYNTsf1puMSOEIL6IqKswlCQEujjyu+HLZswCv0r0ty2+KC4T+RHOlxljYVNF\nOpv6HOwqh5dfs1XHKGh5x6bwxhOSBax894n1iIRQBu9WrPIvUb2X92aCmJHF\nWCrplLXV+xG8EkRypc+FNYaMou6uCOFZ0QnNHqDFrtc1U6VptN1CN55/ZBXb\nw0KNnM61oewxH21oWHhkaLTEGCryTSjtQOxRlks28LlOWbx09aEKbVXTBNeh\nkiK4IsU6iqeaBWwnK0SQZ5RdFH+gOwgD4CM6nqkGWe1l2i4mvd+f9jxLugNE\n0DxDDj6asM/cZqp5pLXmgQv+s+GRN+jTZkrRobmYHkd3BEWGgGqHgHloBL2G\nzkQCdW2Z8aFsl3jbAc/OEsrdFSf1CrUgIX68+IHJRbE7rMo6CRBLDxDbmUns\n05SWRFfyWP8GNkwpxB9eav6OnoVl/o9zequS9ZQ/YRWwGLg/YYWcr4vq+UAV\nvXOZ\r\n=zDCh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDBqO5hlneyPLEoaBPANw65HdJQp8nN1/8t/A32VASnKAiAGfZzBAkLdh0SaHJPA0atIZkSzrlOQy4F2d3oBT6ChLA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-forward.20180811233712_1534030797246_0.8465670181576335"},"_hasShrinkwrap":false},"0.4.2-forward.20180812174441":{"name":"@atomist/sdm","version":"0.4.2-forward.20180812174441","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3de8a790cc74d5907436b631830c5eea8bfd6793","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-forward.20180812174441","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/CIrsWu5xD1t3icgqtfVSaTJ/VPlgIqKA3LRn832uiMjcKBUuxyr/yFAQEp2AlnrB9cswejt93g018X4gVRqMQ==","shasum":"1220f995b49de04f3bf9957d02485f155ae2fc2e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-forward.20180812174441.tgz","fileCount":837,"unpackedSize":1817615,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcHLMCRA9TVsSAnZWagAAnXIP/3vbcIaB9muLjmD42W8S\nFAI0ljpUy7tLFyGLdjlnG7JOsUu8h2BTa1bR8WMv/ghdULFptJORDiqioQLu\n00prCtHDE1Qp7GoL+lek8Mz1FilV1k85AhUrm3GBa27w0A/cx5vIeLU0fCyj\ndNsFZg2SGbAoNM1Hh9WA8i/yOn8KzgFd+SfJEf3U/8FAXTZcJsZdEaQVj+nt\n9X8YxeVBuR2AQTgRfJ07q/AHe8eSLet0q0YT3QdEdxvEsigfgN1fkVaTAgTr\ndjoxRJ+zV19wSnDXng+rogS3vWzrHtiFgAK6ljIJRlJ3/nLrtpIqoN1DSrCu\nBmrYBvwzJHTYfWR55MRYNtYDUFR2xuBfatdFfQmZA2H3Kqs45jZ3l8wB7Kiu\n+zF+9Pa+E6ZrK6G+Qn7LNLMu1IQFf/QkLbXUicVqEugd7oqQzEYpR0zCs1OU\n7KlUxpMH900vewwhrP/gVwVXHtWtPkrsQp/tDDS5PuJ45QX5nBohdD2t4yP3\nfuZbNI7UCBbX/Rd/C1Hdahh3fAD3VvCr4bBiw4Z0yCEF0caH0RCwuHkVSO1I\nkTa3ldxST4/tTlKSZ7490N0ceFdaBTuKgtc+sr+lixpiopmRD/atL59XjCdY\nQQTv8S911Bj+H5EEr3WaJ5C2sWJXxwa/BSiDJd73Bzje1bd8rkfyQRtGx1K2\n8SRa\r\n=ThkB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCn1oRqAWF+6gSqGD+jwGDALEfvKDamWs8hmveDmulymgIgQnzHMx9ksS8tcMYE1q5/DlbEw337w5vix8LpY7JVCL8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-forward.20180812174441_1534096076068_0.07079263378558709"},"_hasShrinkwrap":false},"0.4.2-forward.20180812180244":{"name":"@atomist/sdm","version":"0.4.2-forward.20180812180244","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b4c913b2381822c993f372a089810ff480abfac2","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-forward.20180812180244","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gcayoAUC9UwFFRmJwzFQJs+wprC3MGm3NebJjA+gIsFH+PBcOj+p2C/jf6YD7lMfqGmUX614sk+zzco3GQNOaA==","shasum":"b776349a45e03ab64b474a1af99f4e1a893e84da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-forward.20180812180244.tgz","fileCount":837,"unpackedSize":1818025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcHcNCRA9TVsSAnZWagAA07sQAJcKOmrOjwsXyDvGF1V+\nRIz/nACp36EnnKVveZbXk5g7NKSsdo2H2LmkuZ4zJZ13ZIKoaxPQPIS4h84q\nB6VqJJ4DS9Dxvj64WqA2D/3tKX3IRLmeUJqGioRt+8Q5GOZu37p1+gj80lBG\nu+1Z8FdqOGj1t4jjqyAmj3o+/0HRExs6IgXXaQSIpy85vq7+QgGqMd1/xfup\neutnMi5RZVZDssdfMC7msrC8G9GUsKr1Ae2oR1TVJiYtq37jcdMrxq2WLHrg\nVyy+imYneHU5gtFFbHWCNaPaF3R9qqlfkNEFByjhNRwex97n6VVfpaVKERzH\nrAnJBoQDy1L9PBqP47PYj4g5tg5NNPVxfetL1SATbAAgr1aZ3uqaDUSnVTD/\nrBLp84H31gSFahg0+LaQFqKxKusiw0VUCP/wbowOG89rhb1X3u63jCuEezX3\neWW8yP+caJgfrAVLckmK52ak2H7rbOzV0cO+UPUpXNI6/zTn4zHwbwUx3b7f\nW7h3Ye92sSDWaSSKTq6VJzl8vd3oyUzsP0sINkPEpBrTHvGU9hg1nXNV8M7e\nGvGio/7+EoYWT6NpDhbXGYTgPMGlrQC+kQG1pO54lArknYIoEOZ0oPLR8Hse\nr7+qkuZJAjD9Pt84iyJWqYLACDWHslYxa9rcHGIGiFf303UfgoAGp58vQnP2\nVME0\r\n=7f/8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaEkaMjhl/Yzn2vTxbLxHUJfX1jnGZ+Yp7Uem0dk5yiwIhAKjMfeD8X4xiE6TlGGKzLbNQFEsLY/bYa+sBv4ZKBFj7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-forward.20180812180244_1534097165246_0.792029573654049"},"_hasShrinkwrap":false},"0.4.2-forward.20180812190256":{"name":"@atomist/sdm","version":"0.4.2-forward.20180812190256","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"063d776fa8d4dc3b7131b3eda69a8ed2ddae5885","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-forward.20180812190256","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-og4noIFUZ5ox7mrMN5H8hKzsl/4TMioed7+EXU6if8U8rosmizsIkeh78XLwTHGXO9z4e7288t7hSjwlw7sRFg==","shasum":"4e6f891df7ea6c2b2e8ad476ba895f5ff73d4c3f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-forward.20180812190256.tgz","fileCount":837,"unpackedSize":1818601,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcIUnCRA9TVsSAnZWagAAQMoP/3ki1JRM5k7NTomWpjgq\nKv7e/CqKBGtwSi88hS2fUJs90dUCfPmoj609boV/BcVaz0VF7+N0ngdOZS5m\nyj+7EDpN2tT+lpBdq942DbH36PW+T3KTYyz7ybNe+ZpS6Gr6g6vaKKwsaVu+\nHRj1yN+G01PbJYiI98vqITKw2YGR+ekOgPSlJQHZapVDZrctDsKr4+XeRQLb\npLQzL2zY24bu0OdgHm0gCaRBynld6YQDJc67NAbApEWEdU00nlqxXjLM35wb\nNnuFiELeFEzi8S/tZioylYA2RaX5XLUF0ZRMsAKuuZioJL+gYtuL1mhhqb1p\nVs6A+rr/HmamzkvjAyKHiZ1qN8/06hdXQ4HIjbpWhqLklS18JzzctJKfhvPx\n8FHnEfwdLQobEKK3HFmY89ATr8uln4S8BZyGORd37IhcY3NQwWlNZjbahrIR\nK/HVBuIfecjk4ax0G9xHukFoRGH5LsfV8x+tcZ+zJN3D0GXnlusYwtH41LoL\nXOXrv2jS408XqHrfb+Z4mJsTqAGJy9+juavCgEHDlAMeZBzrnrHxwmLrFYbx\n0nPaAGlFoiR98U3Q6fzVVlRqb+xOgajKsHEbNa6BWAF75hto/gxj5c1rbUyE\n+3yGE8aXuBy+f3sPzqV7zUnyWwHmJSUBOf9flyspS3BVucwaZXIjQTtwSFAj\njqmE\r\n=dLmM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCaebzhSYq5jzgZCbma0vjDbMqmiquBMWX7G7Y8qA5lAIhAK2WUxYOi2JETkc0IkvPOIPQ4o5fHdEX8WD8V0SRxrT4"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-forward.20180812190256_1534100774252_0.9582602292069811"},"_hasShrinkwrap":false},"0.4.2-20180813031424":{"name":"@atomist/sdm","version":"0.4.2-20180813031424","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9f99d2b2b5c15db383054e8e64f455c99836736c","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180813031424","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9LtGhOUy+rRYRkjH32VywKPKw4mtlcw0qmFaHHo8mNVaYouyI6whFIqPYiKCI6ynV1HIksFdLyC4sNK2aNhm3Q==","shasum":"32c3127e92fa73d182652d87ef97f8e517b7fe60","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180813031424.tgz","fileCount":837,"unpackedSize":1818593,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcPhSCRA9TVsSAnZWagAAadQP/0zIH3LaQsJjQihcWl8r\nzWuURwwMU5KnJWoX8DGR2lvYrBnCTHeQA0Kelm1nHZFPR5m2CVlNMzlocscV\ne3aRetNCpYxxJ876pZBYif4ygBj3CZgVLMEUEhl3K9SewNahUVSdzETK9Z4Q\ndssf63DQ9R+4SR5XwNpL4BBrOXel5BUBF/gFkiHwxSGk+H1Gg26ewyfK/Ubg\n1yuzi9s6Y1z+/dqxAAFkDUP/c8EgFYvU9+8SxaohU57Oc33ia8+HiEU0L9wE\nESsWRI4qpZcgeqq1YbMVidNI1J5J2TdoqaLW6Gxe3jk9xoS933nEhwTNnQOp\nsMyqPiLSeY3GuAxCa/FBenrXddi2yFXRsoijM/QX3I+C7MpjughyHFlaVBO7\ngQVrm5z+MaP5yZ5YBigIgYZ5Qu9iqrqFd7WkbdS9SKKztq8epM52qGyQGio/\nsvJHVfh/Zs/y1LKS6j248jkNSEStQc7c/eloqHpfYYUgR4u5IvyBMVxjhfBs\n+dmDsQDjPstgRLNKcqxLRlYgANpCuKBKd+9cYVKawRTh9FGO8DV3LM3gVlaX\nxdnsEE1J7Om/cCo7YUGT9pUKnvir9UBz/4+VDZvOnue+eWrA6BKf1vRgRoH1\nIM9AUIeOdX2bqXQE0cpGcLLI2j/x6CJNR03s3QriCP3L17P9z4su7wL37CVM\nORE+\r\n=oDah\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDsK+h+aInbGfJ3liqUho3cD15wKmOdlL/W4B18fFGQtwIgMYbi9DPqwwOQv7u7bhhAPwnPqwcafBAo/JNsTpp1Mao="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180813031424_1534130258350_0.20850303410968318"},"_hasShrinkwrap":false},"0.4.2-invariant.20180814001448":{"name":"@atomist/sdm","version":"0.4.2-invariant.20180814001448","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"edcc113eb4e6d00c6c32018b491497d965a8a5c5","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-invariant.20180814001448","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OYJgs+Xbh/X8huy5tVG7dD7KSLfwd3OXABXlSTmnCzVnf90PpwSKIt+j/usAarDgtkX+C5QeopZeIHb4OAz/Pw==","shasum":"9df538b0c6c1185f2bb239227adb38564fdc9dcd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-invariant.20180814001448.tgz","fileCount":841,"unpackedSize":1823965,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbch/JCRA9TVsSAnZWagAAG5sQAJaEVrYLubjHwN4VXI1k\nPUuocq0zk5rDAFP+ykY/pqH2LHctaia++JOtzpFF/jBrKUyOcYpmm+tiFm5V\nRuX+JOWYmx0rYdI6YGhglcUT3K+6/yzMYgZw5YCrjLzsiiYDX1GsT8X4XkrY\nfK6qAmtSs9HiZoizCx21bsmJ4s9zXfDX+jM65lSqomXPtSzvA/e2dswVIVdV\nBGVYgIKegizPtB36f2FejdkcNNU+NFyWwJP9atjiN3SRfSUAz8LB8VQtxNq6\nvOc6xokEbLaIv7azEummdYqiGmADx5WErMIvj3m1LCtbrOs9ILPVmHv1FG+r\nOnA9WxVhd0mMdV0qMdmumepq0N6GXXJyS7Rap2q+PI8GLYZIbAqnWQaDGucZ\nLFkGPRjWfixvpQSpbT+N90DtJjLKhp8pgKL88a3wFK60Uz9apAIbk+OuWnid\nKgNrngfSZFDd4gBbSrZT3k0uttCjbBTm1ok8/NiOumcYkjM+SO2kQ707HSTf\nGMZYuEF5/9wBo5EMt4iio9UtXodP1fO8qRmCw8rD5y0sqdPNeeza31hw+BF7\nETCAAeWi+cNzBIE4Ugp3/tS1y7ZOClDAan/wD0mj8jCfzQjgYtTjFoH599n7\nw1eA2MaAR2ew61JV8bk/nZ2edHUSqf6tv12g87mQGupjvrQI/ImcKMLnl8Ku\nJ7G6\r\n=yrAl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCdeDUbtfcmSVRLBgpZsD7K8ULLUUP8+TsUb4iS4IdH2AIhAM9JsEd1yXIusihkzaRaeWJ1dbEcPeQ1jDPAlulIEKjQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-invariant.20180814001448_1534205897368_0.8620015171286586"},"_hasShrinkwrap":false},"0.4.2-logplus.20180814004022":{"name":"@atomist/sdm","version":"0.4.2-logplus.20180814004022","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"160385b7a423a2f131d80b62159d7ce332e00ccc","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-logplus.20180814004022","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qbHYTRyFKdottXZubW1/RmicR+ebkgr6ueDbKxVJlzEYYvzYr3jGv5JRI0kpvZmZtKeqlqhrXsioUKWq2oBSBg==","shasum":"ae3d0f5ff950b2c5675e1110bbd03ac8f099e3d0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-logplus.20180814004022.tgz","fileCount":837,"unpackedSize":1818642,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbciXHCRA9TVsSAnZWagAACQEQAJzaBvRcl05hvnTRizNJ\nOI77ho27PSx0y7bAfwmmAfc9XIko3c+OD0/m6PGnhihuvayO9uo/PY3sNsUm\nbpvKsDIf89smy7JMND6ENAxOqKyNWiIk6pE86sgympoVeSR1a5e78mCQAFNq\nCD4OWs8QD6ZaQGni6MViPuRWtrT+X4XjUSeaUBJilmpXEytaLGvCsVOB60tl\nW/H9Nx4JM5IAN7T2XDbbbZViW1IW0/d/vkgGeuJB7XRSQkthQYMv/OK6npkx\nZcuNj5tgu9va5yW75LPS8vy6s9yJCJqHUWJYiOpM4+iBE4u+wwGHLIefrPeL\neY9pHRdK/rFVZBNh5jvmAnFVzGxk6R+BOiWsU7HN+39svx1rnr3KVF5LM/Wt\nWCqvXmssLq6l37xJ4zHkYkMgGbJPi4yfw5GK2OGXAxE12EgGcqhAbc93hK54\nteufiJ1NH02UrjZwPJLuxIt2GHbORE7y2kyoZJxIpkN4YqF0Z5e3POVvAWWp\ndIfC70sBwIskxPzFVDeQa9+wswEHK5pZZGUm504W4SSxpyse3tp+bU29xyLl\nOVPwzpIkaHEu5e339pUh+qByMg4J2Nk5U7NAHHuLViVVuxMtWFUxgx4flngk\nqLSy8plFdL8w4A8hvRWEVRVnPDMHM4rlJDgI4VAtr1iTI/+ZdxygAlNnVV2t\nISOj\r\n=aAjT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGGMbf6neMHbcBZlRu526L29FiH5VXhveQAIPTnq5XrzAiAaQaBBUsgzoz18RfvCa7gxhHXyoyXWs7nwXOAlLVOOUQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-logplus.20180814004022_1534207430640_0.04422165869029282"},"_hasShrinkwrap":false},"0.4.2-20180814004316":{"name":"@atomist/sdm","version":"0.4.2-20180814004316","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"11dd54016cd446631882dc268e8649f0b9290207","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180814004316","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UBRk7PuBcpK3W6oXmt+IG0tP+WCLBBptYcCiMc+RaKVINeXdsrvHnaCxCbATAdA8KgOtHYT06eQv+sqUucWWfw==","shasum":"3fd5641fef538361b65602ded4124d74047a5ab5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180814004316.tgz","fileCount":837,"unpackedSize":1818634,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbciZyCRA9TVsSAnZWagAA2asP+gLJZ2dDqlWoKFQf8IyL\n+LXjhgUSf8mLZWunoX+XQE6EK2iynO4+0dYzQBk/lKSdi2Vmc1kirEZBno1B\nlbPvrx+Tu+0DfltrEKu9NSbREIf9Y4hgyHmDk1pbxNSopJCvIRrYILq8llIr\nV+BySpjHwTlbb6YiqeJyJwJ7RLmB/iWzD5fXRAWILvtKuJyOanJYcb6HUvFd\nV+/+7QnWG9nXxcMIftkjAvJyLZbjjgOxM8CKAWTm3O+eRag5qB2F8S4wBRxs\np6p7S3kqGaOab5BCpFaOJryMrbCMJPW2HBD+mf+DC5W22lskH8j85JJlm/TM\nxX5/vX4f+dcYR/YIdN0z8DeSdhfRMwa7SEOIU/b41ERhhOd4R1WemqShuvD7\nwzinKPGUQbJ1PkULXORbtp0mnFrVD/bhLAXLXByUFhI4isLGsqYZIF0acwo7\n7aTaIGcksLcdzGWwdGxX9OMuykYgyw1H0COWUX4Y4CPVvKyH8dyPyr8Ej5xk\njAr0OQ/1h7gQdkmLdzJ0MdGzq5bTd0fh+uVUxXVXHlHoenJHsmxUSXU/6Ulf\nMagyAcZHxvCObMx/28k0KwYlTrIdiVr06XUE9fileZFPvzL6cujIw/slDVTB\nGLTZP3zFqexRIthgMMhXp5te2GErcf4+oKzGfvuExj0NM3Gynu/P9aVcpFyE\nZ284\r\n=B/rm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEw4kwLCmBXChEsgvGcTF2czGHR2NdagmQ1RK3tNTWPBAiAqNL6xII7zDEV0L2kzfnkqCNsNgTEUC6Rni+qyfMGBLA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180814004316_1534207602338_0.051580769849297825"},"_hasShrinkwrap":false},"0.5.0-indexes.20180814083803":{"name":"@atomist/sdm","version":"0.5.0-indexes.20180814083803","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"56443ff23acb1fa5be2eea33843d1305ae413c05","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.5.0-indexes.20180814083803","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UpRY244aQutxYQv0fDIFaGIbEgX6JW3cNDnfIIt5H+ZeluWbohUBuQ1D4Mc/+cs4vbCbMgxciKtJXlE3miSpJw==","shasum":"3198fdc2c3c11573a623f6c48eca15a299ba502f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.5.0-indexes.20180814083803.tgz","fileCount":837,"unpackedSize":1820991,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbcpW/CRA9TVsSAnZWagAANXYP/Avd5skjxWwk7LKFRXfR\n+jwJDyOkueK8RoSjldI9aibMymjFyjTqkl3Lc/TodWzMiEVVqHmud1TZmCz/\ncwrYgxrZ3UfykoCZdUq+skRO/rhRtzuhbhHSRROBSm1HM+HXioP/I/oFPPJn\nCtTq3QvHtzRNwB/bmQhBdS5QrU3GpHbT4fXyqyvwpmjpYMiNhBBWLQ5P26fj\nQZT9e1b8zV9IhW7+37bYsCeLPgXd0d3CS5JUnGIpafj6WbIj1qNffOuLRpOz\nnvXsGLOLQ/eW2w5iT89dui3cfHAMX5nR2b7mNNWe+Czj6yZJPO2rDBxsrrEG\nxIkF3qQQ0K4kXEFBALeFuYdYUf6855Y94WF5Tv1mmNgf9DsZB6yUkSdIwOdZ\nI89447OVUADtss8/Bnm22eskNtEu5RYh9c1DdD99H+obDKZvvnr0TD/5Ux78\nYWdQpcxF6JsUGFIKK06aAkvdX/FSqGk3S+NzlPVIXOZwBCWRR3tpXo2bfOYb\nxjjoxnRACRWdgl798q4kknnevjtZBhSHwds61E9H88svx9DHwhVYizYKBt3+\no+Csqz+KnsHTOPRWPPYLVYi/ypin5c0l0Jpu1tO4JiQrHqPY5Iw2KlI5sn0n\nZSk8XWdbCfYS00fyulCCayme0ni5gQcmmgNO4jAAjhyYPQFsIaPZ3LTOnykR\nj/HI\r\n=CAoK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsSKqOqtlaD5+Ef95n7mu8PbO8TDOV8+uRU+jxoeffowIgO2lvN2fKaAJLhrQ914MwRgjH99vTfQx2S9UBwtXiGqc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.5.0-indexes.20180814083803_1534236094549_0.7931331922005191"},"_hasShrinkwrap":false},"0.4.2-nortissej.valid-branch-name.20180815222149":{"name":"@atomist/sdm","version":"0.4.2-nortissej.valid-branch-name.20180815222149","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0db49bbd20406019eb3ba0fce584bcfb6a60f593","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-nortissej.valid-branch-name.20180815222149","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dB4fZOhOBPztv8eHEmcnHLiMLUFtuixBf/cyp4qsIkRjMn+LI50+DsI2MYtXkp9O8PH+8VBdtgVfVFSLZR9ykQ==","shasum":"d326e7776699760e2b9a10868df7a1d21dd3dc90","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-nortissej.valid-branch-name.20180815222149.tgz","fileCount":837,"unpackedSize":1818883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdKg1CRA9TVsSAnZWagAA+xMP/iYJVrNtkqEB0CFWjKNP\nN3E+eYdbx4nRm6LsruJg+piTiJoM+CoOyKMIZXMHrA51TuzeNgrD8jCeVGjv\nBCxRmBMxPzugphsGBXUAfcpn1Wi2WRt+YtoQNfBSNEG092UV/mjgFsMFq2Lv\nWmBhbhhOqyOAX/PJK4kcXo1D+IArfMy+hc8+ZQkYk7HxNuxp7SsQPK3ITfjP\niunujlVzyYHivOW80FoV4cKWXgossGBDFbNGneRmSCFsQvzlOq8vIRwNE9T7\nEV1LcqxGz7jN6XZPD17fyaqnki7XYHA/9xpZ0DsShffB3SRI0VOE5A6ojnbO\ns6d1kTGtxH5Dkn5fz81/UlSAx9ZIWRhWeISeylcCxglnCcUv5dFY1hkZpGV6\nKf0K+KdOLw9PxilTC7fzag300vkhtTLDWjqwEFPb/1nY7eWIu2UVwTnSuUQL\ns8EuliOTe0WOJ6rvivgVs3wQ+LK9/7L8UfuHV+bpMJ8QfiJjWy6idHJBL9la\naqXuKuNUe6fI9jDNDOinfBx2MIDMHlMvzwqXY0zLxY16eG3qgwMtJSquV/Lh\n7p+tSjLx2G/2qyWaZ9eJR1Wtt5YRE/gwcZFhsit3ki6eedk3QlVXwkHtiKlT\nV/SMoPgMSTvyoAVWi6Z17R/7/zwU0mf42qSAk4pe8LQL0JYr7z52QSGu4gOD\nsVa+\r\n=GIow\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC10zTlfqoBTfLn0jS04Yjc+hm/xyPWkBrla0mNgsEfsAIgM3+8xcCEMaaPOrXJwQ9nOHrvcOKBqviEGJv9OeqKjnw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-nortissej.valid-branch-name.20180815222149_1534371892757_0.6566417362582821"},"_hasShrinkwrap":false},"0.4.2-20180815224143":{"name":"@atomist/sdm","version":"0.4.2-20180815224143","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"610a4f4842091b94ee3a03be7dce579ace906c74","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180815224143","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gKnn2NMzW2+jROrk/TEx6Ni6gp6Pz7tkj1eZD0U4wMrLmHSBP+1qWTzNwUbqCNXHeM2nYqBWlyV5bzToYwYCag==","shasum":"b292cf5caf123e9a05fc5de1301d778111034f0e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180815224143.tgz","fileCount":837,"unpackedSize":1819149,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdKzyCRA9TVsSAnZWagAAQDAP/3Ka3baBY75ORTTjb0Dv\nEGrO43aIvVP53j5zEn0/BLZS2WEtuZHMP7Suc44GEcwnxoHgx2AlbDpUm4LG\nJ6H5u4kKU62Tggyo3lPd01PfO6SUCjt+r/S3QbdE4uvP3BgSQBNXnNA7AGkM\nKf3b5XwaEhybyU8jlGgFRSU8yzRFY4p79QdjwJCdF2hNhxZslBTPyjPvMBAF\nbOTH01uAeyHo6GRA161qRUkjFR25L2KlwYDoeYVoYOs6GZwJ/TnEkmQ5UFr/\n5/IDysWdgeG2nASgFoFXrkEg4A9N+HYNwOQcog0BGG3gtVbgCgdKUrZN/zbM\n6+k+xXWCZTzD1nzOlu8Zop627JIi/iJDkCgA780+qoIHk0cZW8Mn5iNi2COd\nxhjvuH7RWSyMa3Is3koWeUBjxtnSFI980SKIdz7ueqjcqBaWUC2e+XA9U8WU\npH8oOXkEiaYNO5sn74GuzVQ+Byfv7vam7R+MkeA4wUnTm1LnMHgsqMfLrxN9\nyqehPiYZBLXLI5T4883FRZV+kde4ITE3NZmfDtPott3jP+CFmBY3Q4bnysXR\nmifuMtAWxnBenzs788t1HyD6N+5nylToSWwmeuNLeWPlZhhf93d+KG6DrmFY\nWWpA0kYfaU642sWV7WpeVhMADjbcQmaEgdODTA4yfLXd0ugo0LUV5hmxIj2p\n7Y/Y\r\n=Npnz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIzMF71Uq7jOLS3OXLgWeJTuRXb2aGCvGfJxWsv7G7HwIhAPLY7mJaUvDG/6rSb0wL1X8rMc/BwpAdq26dC4bwURVq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180815224143_1534373106287_0.4516631173550312"},"_hasShrinkwrap":false},"0.4.2-20180815230951":{"name":"@atomist/sdm","version":"0.4.2-20180815230951","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c874c51c5c70a9c3192ca93e2dfced4d9b52bd09","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180815230951","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YphH7qRpTO2YUVo/v3vrgRnVQdt5JhoWgN8ksZgokbB9sbOSOlIuDJCgXpjfEft1TE/nxa1+LF1TxKJoXyh87w==","shasum":"71ecfe22fda1cfa115f5788eed302599df48f332","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180815230951.tgz","fileCount":837,"unpackedSize":1819370,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdLNzCRA9TVsSAnZWagAADrEP/AgIUHoeqMEKVhoDZiI4\niJxVB8b/jqdUhkatThqrAAT9xYceKadxLLICV+tH4NWAvlsrpMcFINwzCSfu\n0OHDinp6Nw+lMVC/3o0aA+xy/nX8uBOFUDzpXBh+8t14yJ+UYsOYUKm0+U2p\nK203tNoEwQ0n5naEkw9f0tM4CpbvKO949nOzSNfD5cBffGz2icullPla6Rnl\nJODbGaPsYlFNaBVkOkBhKHjSXZvKrU/CGWDpAafpXZ0kjAZ8q0VVbZGXi2fS\nRXsd5+urSlxuXqK0S0ucfr9NxS9UlFaUAQMnAxcqVcFyJIO3VKxVbcoLs8Bg\nv+hKIljiESiqTo3NhZ69mhpieSvdkFk6jEwOHcnw1MZEAj2VT5dcvEgToCf3\nwrqU4EaGxBo2QG8Dd8QKF5toCEIi+958mZPMQ6FTRXllvauH3VRcSPBx2gc7\nvoWi/woUwru2FhKrl7aNvm3TxkkRGFHxIF92szfxKa8YwCrd7GnHuP4wYQ2m\nPG11FGK0ETzcSodfv23t5hFFqj2Ec2JV3NPayNPIlQlbd02BJA2Er5nvARls\ndc+hCCdgIEbyDTZOTqEf/wakhvQRxrSewlfEjFwWju8XEhfnkhxXUrXvT6Dv\ndcQcHjXAAJymvT0GCw8Cy9krbvwnMR63Hog6MdtM2OxqCy60lFHOs55+Wwc3\nmv3o\r\n=OcKe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF/kxsd3BLYR6B7GgIy54gIFy0fz8hWmUuz9/bLWmLf6AiBfkY2hwrLqihA0FggXbQoq4ESP1yjZaAhM5HfEoGnrnw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180815230951_1534374770825_0.8205233874872713"},"_hasShrinkwrap":false},"0.4.2-20180815231715":{"name":"@atomist/sdm","version":"0.4.2-20180815231715","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6bb8dc76070f63154d9038cbdbc98cd5cea4c6c9","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180815231715","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IpGZqFmh99dzVPhA7yqCWFH1UvaUrhKeFlBwWUuc6dKfputf3PpberhzO2AxcouJvJDpfY7dw+7dp0/XV97rsA==","shasum":"fd948da2204aa94a0a818a9787f3cf7514758eae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180815231715.tgz","fileCount":837,"unpackedSize":1819373,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdLVaCRA9TVsSAnZWagAAZJUP/jNlYCr5MukYl14f00Gn\noDjgfbbZ9uZUPBoMclcjJChD0ZyLLfscdZcVjQTUrG/3rs8S51MR3qh26Mfr\nC2b50O76OS8dOt8FkVE0sY3vitHbtzIw96cud8riuXpRC6fW0lgyUN1Mf2cq\nO/DGQZSoOlUzDwn3TxgVloDC5xIXWhIVwSuoK4mcPTaH/ZFu55inDy5FPE5o\nCRJHKi0vgkVlhICs6nwcRXii7ZUybDAYkMJh/hE+vrqAgCueeAkCiNAv8IfU\nB/mKRcnHrVB/wli1EeImMPwLd1HUxgz/HLE4i8dXwS15IcLOuB3FiH9gPtiW\nuu5kZojDydQzhhHwX8ZYIfCmLZT1Zkwttn8xm5s7L+SQ7G+jllC8IZ9Dth4y\n2/JWEDyMvpzBYO+b6wulc4OQsECbpLSihrEKhCNXah7nAYbOTuoHE4cAAGR0\nvmqo3H6M8SYRyJctIjw2+c1iq7Ceyr56oMI17lu9i4z+vH+Vz0WdY8ijtd2t\nD4EmmKFPbMGK/73BxUMj0mtJ/wHdGrAwUxv7Pm3u91f6bhfCZMt2QYyPU1lU\nM7CREFobQZyhlIqYh4YtytK99W2xp3zRqDCItnL+r7Heqxrw3Th7zE/yS2LG\nrFMBWCIZ5E9T9RoGysa4ob4UqYeGfzXerb7vduCL7JyQ0fEY+hO+2WOZN9om\n/+Pz\r\n=Ool5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkDDepoIYcbbnDgYBJJcqXve87z+z+HjaqbD1By0p2VwIhAP1Nd9VPqEI+L7MRjgJdCHB8B4vatpJq72fiwb1zS/t0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180815231715_1534375257285_0.15969006061389068"},"_hasShrinkwrap":false},"0.4.2-20180817205802":{"name":"@atomist/sdm","version":"0.4.2-20180817205802","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bed9af2d91f470b8c028205951ddbb1118a80916","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-20180817205802","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-k6fMHZthmP/o193Buhn505w4oxLW07bqqdDlAVHIa79i+uGpKPHjclUBLmqpQUC3a7ZjDQ8qg86pYTtTh1mZyg==","shasum":"f207b0092b524244a84c1aa4ab41b0670f682d9d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-20180817205802.tgz","fileCount":837,"unpackedSize":1820250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbdze9CRA9TVsSAnZWagAAr2UP/Riu74G9W+EIbifF5Xq/\n3WuxUEWklysLQX2GT76p0B1KDzkqBnM1tOyWWlaFOPgNKE1JpoJBQfwArvex\nYMBbhLT4jV9VC3hoiWSWFp4yUhb9z4XUlfu2xKPgeY5Xsq+um9mr85SZsME1\nw3grQRTKEPmSrXgw1O2+eMNs1UMGZsvu4mLWczxKlprRhG4LIwRVkXAK3pcC\nnISSCQ6Lj4/o4U4NvJKERsVr6xwk7dnLOji4ADBEY6/3MmCsVH7cBx10YNO3\niwO44vNvqhX4md+zXj0BLjqwGCM9nNNTeUbeLwkfmyFChfVG909W6Ow5N6L4\n0CXbhsmuzWzhYlq4hv62gK9Kf+jQEcUWvJjeg8dGo22avGjaNfqJ4QrEtqbQ\nQ3Gx+rYqsk7Kz2OcRvJr5t1glmYhPwze3IsficZCeJLRqYhjiJXrwWDaS0jF\nbaCef3MSKOJqYIgG5uZReYN+IyCYkHU3Cik29sM7ULOK/7tgD78TSjOlM80g\nRxSyKcP+JSO9aFSG0jbgAjIvsTwlHQ+wDB99xczlDW2fHDD98U4fEeus+bXD\njAZyYHR5mfV9Ok1cwTY/E+mAgwQExT9X38rGe8l+WpEF9Sm93vOKTpTjH8oI\nYSu0pw6pdIeEbEZXOWkIzoJp/0hVHNt7Zs0CAliMzhthJnYvAq3ZBuQRxqiG\nr86J\r\n=RmJl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICEYHmdf4XmTUsrQkhuA+725ZBgenPJ13jPjbzJfxV/CAiBYObcnA05AnhKiLmOJQzw4I3/OCegOoAS7ILRwtNYIcA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-20180817205802_1534539708385_0.2292503307298439"},"_hasShrinkwrap":false},"0.4.2":{"name":"@atomist/sdm","version":"0.4.2","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bed9af2d91f470b8c028205951ddbb1118a80916","_id":"@atomist/sdm@0.4.2","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hPA2PSJ6YpPCIGBpEiy1e3nzcyuMDnSU9hjSwXb3Fzwm8GDAyAc57e0d6YWX+NHdd49AqMmvY1K6PIGFUP7bMQ==","shasum":"3337318170b4258d37003cf141ea01b1d1c4822f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2.tgz","fileCount":837,"unpackedSize":1820235,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbd0LLCRA9TVsSAnZWagAALMkP/R+5mNcrl5tcqfMTPrII\nv2a5kzkAk/MFvSsj/+RVpfcRRiH1WdwhiYCLWE3pXnBMtpwunOgOWx4g2ytF\nZQZhCX9FUK1yKFo8YE5R99ohs9Tb+3TEcbudIYKjOXu50A4gOOz62m3dMKPp\ngpHDsQWciQjDVbXDczufC4qLpxbMQYrBN3c6Vbaze4KZzkTuCdA3leY7cyr3\nCPcC+sMlO1y7NJDB2/Z4sAz0Ss0p8IHMGegDbN64qE7Z1FGIR72PIjo12Nib\nOHosI95nXcbsi4/RQwkVypPC2ROYkj3kPGPVJ0LHb9g5cM+NcjIdVFANHUnn\nOToPNCS7GSFuB9W2kGBzzymFvDtzKQD/Xfrr9j15V9mRVjwH9Bx6DByb9B8h\nGd0WdHzRaIup/iFkm7YB2kxRTADjhR5kTvt4BIogGCJB3X21tjQFrkF8WLOd\npvcjR+MtIVV9GBBMkSvabtVRhzvPv8aa4WHXHA9jhh5NgOtiumStjbhfFsX2\nzomVT5Cmnp22XuT7fQDsK8IwoZEG9et2/DrcsWhS1FvEO1gyCGlCOm1ndM4d\nnX6zWQwhjRPoyUolBN2ap9+vKA14JQ9smvyMDo7mPHJyEVtEXwu/UJDP5cQd\nGNlrVNSHKbl4wu44fDxiI4yM1Tlm8UfUc9G+JJMhJWgHznBREhVPNNmaR5fg\nodAA\r\n=rMYm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDA9arW5lWjvIG2I17WhGO9WLuBAkCyqXim1HdMRO0LLwIgBheV9fD32HL1qKVs+2v/F3UzLrv0UNgsAhBQFNJPR3Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2_1534542538801_0.00624761547485031"},"_hasShrinkwrap":false},"0.4.3-20180817215044":{"name":"@atomist/sdm","version":"0.4.3-20180817215044","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"961920f48ac015842648c0ec7b2eda081502ed1f","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.3-20180817215044","_npmVersion":"6.3.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+BKk4uiRVIxSBYu4xIuWyChiao8JUD3dXCGbLYc4cwv/jh0dBhyjT6ixS6eLxZD9TiQvX7aDfwNGK0rFhRkLnA==","shasum":"e24ada0c90441db05f4938f9d7be6c5492e6de26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.3-20180817215044.tgz","fileCount":837,"unpackedSize":1820329,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbd0P3CRA9TVsSAnZWagAA3M8P/2A8QqFnHRxSzgEBVAZv\nMMbjCAX2R2Xh+cRck8dHRdyj+bgH5BCG1+7Fg39o92hxg6pfA+M6olEiE5hw\nta37fISGAPI2fStG2x/FWrGibh3IXEiUor0m0yX/aSHLxHStIgeno5saY1E8\nR6RRQ1hau8uUWuxFwaf1PRZK3dkrid9oJ9wLTuCnHxZoX4HJVqiIPx9lYsze\nkk/F6u9HwmbvOa8AivKv/E71/aI8XHbsexiDbJNHbJxZU59KkpOhMwwhLegb\nB/Mny76HFmcRo0K/PH1MIlMz/Ml2K/vmhadhS/15NiXZScXRzqrtbImo3XTm\nicQblC7SQNgS5gzZFQKU3Xr5K27FZFVxJcgXi7sAleWkMpE9deMdKYi5p/+3\ndzBv44FXsjo0PX4u5n/ru6PqWLVO6Map5CkSSnnBgyJ/mNGjHCRAC5cbJuXG\ntXvR1d5gcvMA91ncWnX8N3GPx20R+bM76ZhlPKO5a0BCdbSjYUBmL+wF27E8\n+VuEI4ugCvK+W+8zc5YwrhepnXmpanT50k8KB9LkVKvl17Fe1ldwYMn/QSu0\nXDwZMqWX6FZU/XiVVQFOhFXjEapb4B9n0ZzsmQ+YUWo7D3Nsq24GHW/mFlqb\nh5HfJcXEYUNT2/EbCHl2aYb+/v6qrniSLQqvTeDFfl5ih4JysigC4Qp9zF3B\nZJon\r\n=4hrS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBBh8bn6lrcHw23gBPVj+CIyd5lhOtTFU7KIfeBaVT5+AiEA5oM5nSrTHMv0h/DXYHopNnIdOON8CKN2ChHQX6OMN6I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.3-20180817215044_1534542838448_0.027908787284818937"},"_hasShrinkwrap":false},"0.4.2-invariant.20180818184529":{"name":"@atomist/sdm","version":"0.4.2-invariant.20180818184529","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","cf-client":"^0.13.26","copyfiles":"^1.2.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^5.10.0","random-word":"^2.0.0","request":"^2.87.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.4","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.19.5","@types/lodash":"^4.14.109","@types/mocha":"^2.2.48","@types/node":"^8.10.17","@types/power-assert":"^1.4.29","apollo-codegen":"^0.19.0","axios-mock-adapter":"^1.15.0","barrelsby":"0.0.8","espower-typescript":"^8.1.3","graphql-code-generator":"^0.8.19","istanbul":"^0.4.5","mocha":"^4.1.0","npm-run-all":"^4.1.3","nyc":"^11.8.0","power-assert":"^1.4.4","prettier":"^1.13.0","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.0","tslint":"^5.10.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e InspectionActions.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atomist git","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atomist gql-gen --no-install \"src/graphql/**/*.graphql\"","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fc329d56463e80044d561d91e4dfed73e9bfe5f2","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.2-invariant.20180818184529","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0il6je6AMwZChCyPxonvAky2otW5CWTsiWuIwPu5ze/AuP2EHXdUPNV0yzM64m66duzHkWbCnoQnRlsSDxxOcQ==","shasum":"3ce43292209284602a1a37e93000c7115a9f2fc1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.2-invariant.20180818184529.tgz","fileCount":841,"unpackedSize":1826416,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbeGn5CRA9TVsSAnZWagAALi0P/Rxbaeb854OZ51mtPuF/\nn+zB2NHfBtFa/P/KJb65Ml7QYa4Ud0asNJx/US1xDUqvJzIlFLYAX48HquLy\n/RGe62xbC8AmnNvYeH+aH3zAHHc/tWq7es47alSHE/klf1VXmCJH1hA8TIPm\nCrHZe/xcN0o0FktAS3YriHnBD8Wmq6UD9Rl+RfwchpkhQHDCivxZ/i2MhVH4\nUOse1lEjI51DfVMcJxC+VT7GIy/bRvFdoZbfGxfhD4Ivo1fa7atTxHA2vhPX\nGN6CUZwj/9OM6/btxun2zyG6pSSS5IvyYUng/D0otTculkipt1S3wt7mz4h0\nhinAg1IuqRInp6hDUU1jfJISSe3HkaVlCpG7QZH1pxsFiT8Ey+l0wg73TWH0\n1i5BqSFXzoJGbn4sw9+A23Rpa46Qbx5y6EbSrQrfHyxd1BHC3fPEqITT38W6\n3hqKNj6CS00jljoQsiUC6R3PQOfceK3rmdBexttNILHPRTQLlgLpxRGrioKJ\nCKZ7rng58j3FfCY69DhHNl9gB/3OqduijnBnmoBfTcCCuTBynPnrn8ICEkas\nBJ5C8wyZ9xk2/ir+BHa6WlUrCe+o3f5KebHBVTSRECPJ+ya+ulRy4F+i9itK\nsWDdY2F+t3bTyWgnbUXEWe2NAmMY8sUxQ5gJJLO1/oqe5Aj5LmK8pyVOkvNn\nCrmE\r\n=VmfQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmTS94UrTxHyNwdIV6f9MDKb9w+ev4DQXcrCdmZ3kwCQIgbKzwkRj5m/4vFUSANesI2EQr5Skl2+lYDrdulgMuWp8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.2-invariant.20180818184529_1534618104843_0.759233102693549"},"_hasShrinkwrap":false},"0.4.3-20180821132724":{"name":"@atomist/sdm","version":"0.4.3-20180821132724","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^0.12.1","archiver":"^2.1.1","axios":"^0.18.0","base64-js":"^1.2.3","copyfiles":"^2.0.0","dateformat":"^3.0.3","form-data":"^2.3.2","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","moment":"^2.22.2","moment-duration-format":"^2.2.2","npm":"^6.4.0","random-word":"^2.0.0","sloc":"^0.2.0","sprintf-js":"^1.1.1","tempfile":"^2.0.0","tmp-promise":"^1.0.5","xml2js":"^0.4.19"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"0.21.2-deps-update.20180821131636","@types/lodash":"^4.14.116","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"196761ccd89c8b72c659bef8d164b675b9e925bc","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.3-20180821132724","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z+q/vmc8ZdTJG8Wpdyc8Krh7WOn+8Ps0nrAMLDnJKElYr+ZV6WLU03Wgmjw38vrpRI3eCHMFGlnhtw81HJi5qQ==","shasum":"c03835d5a9e045929312fba689e66cd94935914e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.3-20180821132724.tgz","fileCount":837,"unpackedSize":1814890,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfBPTCRA9TVsSAnZWagAABqkP/20JeCrrmqMmCUc4jIJN\nTXagSPd/BfMfWb8Vw51EOSOZ4HE47TTosSPe5d8+0RVlKXAvpHb+b9NtpQGq\nAThVkTDgJc8MLhL25dVaeHq6aHKbpYqONkdfG9Sr8fwWSD/y27Szsd+E1/UT\ndI5puWzKIc6MLj16Ue+Q9/DL88fsYm6n0eaZEh1hZv6iHtnme7lDskFbSJ1D\nTYehWSFHFjLx5BSpSxgLqnMKcaAzUkrKe4sOfZnwzvxcJQOCEysIGAfNva8N\n5lNlprJUPZaTYBE6oPES9rNYTMK1ZsfxvLYQsejjx/3dHj60ZmhUPC2ktg9D\n/jEySW/V7clcleok05u78d/uVauOl8MmKNvhwC2GozFHbs/5N9/oxwiCwUhK\ntLwJBiLJ2qohmSIxgT3UMgdLyT+tYrIicPQOEYo9rmwjxMwZ0C0vhPM8TVr+\neh4OaFuFyZZRV3FLbH/EST4SbZ7EnzcxSKXU1GC+i8kC6UgTzeCa0AnBLbrv\n93DLa1egDP4VmMq/eDQ5M+unz1y1ctgv3YgMuSGXAE9AqibBd3b7EoaEbS0I\nwcN8tz/dreoFR44zA1MsyJzwJhA/0CcpInnQoPTlU6JVlHpVXqx56tU3J/M1\nz4y/O7iaGIQzk0cf/X13W2hSheUjkm3/65ACsdwuqk1hFOQ978XnXNcIg/qd\nqGiQ\r\n=n2cN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAEBodkj/X2PokatLMk9WMTTsMaTWpeFDie6X91O8GJ3AiEAwN38HixM211mv/Tnzm5KQ5/gpUknWYvG0v5F5JyLJKc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.3-20180821132724_1534858195043_0.19780719144824288"},"_hasShrinkwrap":false},"0.4.3-20180821142132":{"name":"@atomist/sdm","version":"0.4.3-20180821142132","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db6495df116a2dfadb57905ac759aa1cf604ebdf","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.3-20180821142132","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-k6NwS+0869CXQFxxm6eFMF3A9wLi/J7oQerMCuzuXKeg75Ar2opwCP3CD6JXfHTcdFUPwVlP39HPgsfHb//YCA==","shasum":"23e5d1c1b5a74f43ff781637304a3f89bcad2af6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.3-20180821142132.tgz","fileCount":837,"unpackedSize":1769300,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfCCLCRA9TVsSAnZWagAAU/4P/jWlHbi/wSDkmcdlLuDI\nvMPhqUVwDdcuJXvujja4EgP6Ft8wjWBq07kJ6431zNpuKDFbsV0ZTbgMG1qj\n/vpDWsNLUVrZHxuVrphu0lbHFue+zBzPLmq6jVUZowu4wgSf1e9VkjgjsraP\nr3Spqyh7cxg1YI4UjFp1NfG/nh9ke3Sha6SC+HOeAJNezuh3S1ZHTrCh4AVB\n501u6EU7rrejhbAHsDFvfJSTLmfQnPXwsBJ3D7jf412IYLYgtFx6TaHg1OqW\nV4kKuKyPpSeSz4JUYfKp2QsD2OtQp8mIp2ysGkjbJPbhMc0mEcdCM4UELKG2\nWjwWhveNQ5qyaeRGNua+YOXFLcG1CEZ4JlUD3cp0qroG5QUhEjR4SZVf4C4t\nrdqqU4K5/XZ5xi8tsoS1U6AKDFS/t6kOTuPCkFRm+NX5g9vPq/fciVqJt7e4\npSXUxGLyHnlzWgWBLQXU9bIk8YnE34JvN5HQnrNzceOFphqFBbYCysPwkTNs\n6d67nXUYyEkpithsAD2lqwhQwDg4VistNuxjKvaP/z2bEA1Gqj0M2LD6pgRt\nFnawULdvOtUljiVLffyMp6dFWMdXONI3dhPFeKNb/dOEOfux84xkAyvEQTkH\nEOiTorOUl+zvbUXcybJaC+XC/sPTrT0bRG1xSbliun16K2ZbVoS1slhozefk\nhSnY\r\n=C2cN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG9vmwvFpES3FfxOL2+VjHYDz85ndtn8NX5k/A8gIkTJAiEAlLndlHWQ+k/nD8tSFWH8F9R4dSBbxojxoUVzPBVH0bY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.3-20180821142132_1534861450461_0.34387359510964965"},"_hasShrinkwrap":false},"0.4.3":{"name":"@atomist/sdm","version":"0.4.3","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"db6495df116a2dfadb57905ac759aa1cf604ebdf","_id":"@atomist/sdm@0.4.3","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-esrPrGWQQlE6w0MHdIANpv2+vbHO6jvV5lmhiiI0ABokTXmYXL/0eV3lE0ktkCv/ZqSBB6Keu/PDEnoW8Lvjxg==","shasum":"029bf294458d61afa5456fa515b60d31328f85d8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.3.tgz","fileCount":837,"unpackedSize":1769285,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfCDTCRA9TVsSAnZWagAAjtoQAI26wk+UzSCT58/SRTQ4\nsBRyekc8TUONFSciYSwV4RJ2ynRylRlgTWFXVKZuyTNxxbOlRNJH+z4CjhGT\nX42vVYQnIIDWdBxR+7FfVWNVE1rR5wNHdVq4fPzbt5to4NnjhMlIU++Ye2le\nOp2GHRPszu8jigTpOIxiVHF+QFhPArMr6KyzOPk4OIwqVcweJ1qhN4mr0AC2\nxcGI4woeTOsaO3+Wl6zlN8Ls4yNpp9InXSbejiQyqGLoqWuugINwJiJwosC6\nUgShUKkmdi2/KUyVJIPj49JMk0RcC1Uzt6c2vqKQo5cXMJmFvWv+dW65W7qe\nVbkgQoe2u6W79KOddF63l0mM4HkXBnowlVBCRWDy8+6aX/9xUzAwfWtNtOEH\nnbzeMBwY2Cmi8BfsNUi2lp0zxZS0dgT/M/e3irMK1LiV80Gj4m8oALqZFkyV\n0Tx/j/X0wBMal6h09J2Q1/N9rtgZNQEMQ0yzmqh1BQJgo3FxmKv6BCr/DXeC\nr7UskQZZ83g2KnmibxUdFrVpHFzxInPMwvYpie/9WfI1usWGqqzjvLyfYpJR\nAU/GjJAS6kr7d7guY3t56481X2ITmlt2brc3aXp0Y4pBk9TboyECdLhgKWI0\nBi4wcR/SZUtf6/HVzTEZIxZX+J9TnBGhNHFujp9O6T3rQ/nhhgi/6hJsPLgS\n+FhM\r\n=Yz+M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGtCb2BBEhv4q8Zc36l5AWGvKEXA27nm3XXaC8X/2WLDAiBtSCk+nQz3XVc9bZWFG5aVf0Nm7iZYD76T9kjp5iTeBw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.3_1534861522184_0.32672314145362"},"_hasShrinkwrap":false},"0.4.4-20180821142646":{"name":"@atomist/sdm","version":"0.4.4-20180821142646","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4bdd76c82fc53c04f564e931e6608a1a36f6e037","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.4-20180821142646","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ru3GN7Qq/xThy7WC8EjCAcxpi/8/9FoLaePEFXHzvj/d+OgFBf0Zy7OHGvNP80EKsyc3+/9vllCl1qgEMbFfuA==","shasum":"e83d0132139f9f727d01fd568f8b58582a99ef9f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.4-20180821142646.tgz","fileCount":837,"unpackedSize":1769379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfCG6CRA9TVsSAnZWagAALHUP/RDiSCZtVYSBLkDRm66C\nKYpNoDb9HSokQC2YuX4s90fngXcsvIRFsdy/Gkj16H3FqEa1QyNcLQcIWXSS\n9gcfHHBVnKRYNYAaY87ldZ14ShyueE+Wn5ieVDG+WNd7KbypL+u9JZa6IXZT\nCRPULHg05kw4we8oJSqzKHw0J1VBYMuIL6yPljHsnUkp0bmYVz5uS3QeNSZV\nRO6km2zdr979oWmsFT+w0ZVK6+N3p1ALjPcPTK4nUrNOMOjsq4HBOdd+n3ef\n1vg1PMRaos2CpXWTNLiDVxIsyy+jHduaJx9LBCSBimKtklh/tv0KtFKuVJSO\nEH3sGMuJuU4MsQ3gaPMgwqX0Q/2u/ztI9MsVmg11QZSHO3mRqJM4hERtYT+1\nHpZuWh0tKJMJW/J/z6X45i8G7Zho3tcM8pQC6QH0BGv59AUUDJ80fI6LE9Xb\n/KA5DrNl2TpZyIU1XN/GrHkf5Nz5QWdgOYGhygoxJ8U97IiCSY9L8vlcV4Ch\naqu3OeoRjswsFxPSFKhGMcumPZY90h84CIC69cpYtuvx56iAG2sLy09zcgRV\nmf2JqPFOobOsLj7SNv5Y19tA4HlqebDRpoiTut8Unn+vTilSWUZh2UfMGNYG\nzdc18nDges5XGLT8/7pRUJtCznZTmCjsDM/ruY5XJ5z6EFKIbmcTXBHKX0uq\nsRy0\r\n=zhKu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFE7qchp6ePxlFu5Wr/54AfD8CDFEC3ZlmSBh1OyFrZUAiAjAVXiexai3tLENRx5Nv4LEoRv2LiSHXQ9DsumsNBswQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.4-20180821142646_1534861754181_0.0374355563693094"},"_hasShrinkwrap":false},"0.4.4-20180822160505":{"name":"@atomist/sdm","version":"0.4.4-20180822160505","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e707a0fd84044a7ef7c3be9948a9cbfbe0676c9f","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.4-20180822160505","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-me7ry2A73dIIrkTUHphmOB8i0m6tf+5JcWw/HGIg1HhEKJwMZNUW+XBJCKPzUsYR6Eg5iz7dHJSz+7W/dso/wA==","shasum":"dcb4d67538fb30c03569861d5e536e160b430fdd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.4-20180822160505.tgz","fileCount":837,"unpackedSize":1770503,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfYpJCRA9TVsSAnZWagAAbEwQAIHBPBVjvdkXy+5piNdL\nK2NjE3OTgIB1sdaYqZARf4Hp8u+u7nNtq3cI6eF1UeKbf9iLLjDBKhpyKZw/\nxUoD7E4u77A1o8KbM/QzRl8bkU2+uRnbfY0WW00kMny9fcguhaKaMULI0QyU\nII86gz3lTxJIl83yUioJ2OVChnJGaGiNaWtQZ60WAj4mEzlDbWsghJCnxUQx\nuMJwWuzMMoJ+hhwhBHoLu2H+gARq7T0o3c8XKvxhldZYTEvLEJ0mJ5mqFjae\nFlHyoSbXVkj2RbPu+60H+NjWzPIaxNQKTWVXlaJTxeOCD4NrBCKeK6kWm+ja\nYRi+3Fp1HSg/X33kZl5Lk/pNOh9IHBuRnrmLqLakHLnRfEPwXacekYUnnnEK\n6hI+oljI0arT6Sj4x0zwp7VxENJOtSxep62WtGb3KurkPGO5ycl8clY2CuXP\nkNECyjTi+W4GLJrUKpJ/TOfCv9u1LF4/6khze56R4hxaw9pdw9YLODS8gZ2I\ncI2JqX82tuxKQNpgSxLNd1BR5EVWLxQx8ZOWbHRoEHx4afEnPSSGVHHtG0G6\nb2TRqPKe/sLf+5bT36BDaZXQWJd9Lchv8JcB9A9R+208cFmxT5xvOiyrRsoL\nczKLWb4Z9ZyeYsR27SiASxjNMuseTu/44LWPM1080hanUyGOmETGU6tAQKw5\n2toq\r\n=WEHP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD38ikiJwMsnhWsRSPfTmfbZ7WzB1pp6zJArF2PiyLAOwIgK8sqhSnkv/DWouablg+tO+YomhDP/ej8viVGb+xPMIs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.4-20180822160505_1534954056989_0.27056682258121967"},"_hasShrinkwrap":false},"0.4.4":{"name":"@atomist/sdm","version":"0.4.4","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e707a0fd84044a7ef7c3be9948a9cbfbe0676c9f","_id":"@atomist/sdm@0.4.4","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OnSBeJCxy8I8TwUOs5XhUsxiWd6p9TByM7ZE9IB11YjcBOwM4evf1vgvOUa1DVCC9P6ISBGP2G8CSM1/OtvP2w==","shasum":"dee9480fc78b4f6072c6468985fe46c8f3dcafd1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.4.tgz","fileCount":837,"unpackedSize":1770488,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfYqVCRA9TVsSAnZWagAAOCIP/3Wl3SO18XewJ0OgNzlW\nWFAQwavKOOjqwOCVvuY7rMqTptlUjCGMI3YMGKWjG0BYrJxA2kwhnqFQHMjn\nwC6wdnxJcWMTAG8P788g2Wi9uk7EnS9E3JSTGmHtFfcZNox/SN60JIw7xvvq\nBHkIzWbo8UZErL8jflMjIAjtxWXjF9+4/DrjtbxE8rdSS3mIvHqUif3ZKBxn\nWoe01DXVzFt2yu2i9XKhtQkSVbFvbjSQd0NiF7FQLr+Nb7WnTs6xxzdmAY3Z\n2X97vOngefDxcIS761HD1aKLRQqfEAqb6Jq65FXYbXZcIwmqRs1JXMvzGYgg\nDIP6QvjX35Fm2XKdnd8V9ITqoxrGDfxd61W5B7SxZb2gOTId6c86zJR3ALmb\n3AD7hZlr+PRw44y7Cx8Kk0ARby54WXKtCrgeB6JUs6t7uMZrv4cXLhJKPiqK\nJ1Rd4IyXEXHXIuhzSS5Xb9UszI4Zx2ilmbnbqJKLE3p29r/LhPmIc/0Q6XRZ\np3IbW9qy/UYY410zcn1toD2H3pMaXKcj/aURfdQwLpLVHKQJJSDq4cAt3Fnq\nWeXlZa6QfzGHj7CkfMgv14d3TfA65aC7lcXM1Vaz4EroT3KeiyzM2GimdPk1\n5Lticq2zBpArWSzmVSB+694sfqjAcbyCiUuJzTLhCTXeCzycRiP79jj5ezwb\nVFF8\r\n=83GP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDu3dRn8ja3Lmja6eo7kzEt+SU3/qslWc62abbRGGZFOwIhALKro335aI1O7M0twcxeNw5s+zCk2GY0DtIhncDlwwmo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.4_1534954133152_0.23658711125096854"},"_hasShrinkwrap":false},"0.4.5-20180822161007":{"name":"@atomist/sdm","version":"0.4.5-20180822161007","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"373ec0caac561007c2d66ae21f0f79e80c734ad3","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822161007","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zQCnM9OgKKkd0gq4lupnCj69baNfPEjfKrs2AgswfqB0mIKppZ3AwZEnpDT/KCw6inpiCCMmYrByB8IQCJ8Nug==","shasum":"eb4fa31c4793c3ef3bc7bbb99d8aa97a6d81c7ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822161007.tgz","fileCount":837,"unpackedSize":1770708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfYt5CRA9TVsSAnZWagAAxloP/RNbMMhUkjb7gs/V4x/k\nzd7R0bvkK7RTwd9lmW0rkO5nMmjZILPEvK2v1IJW0J0hHTdiX2yJUVyVzppZ\nha5se5pc8eSN13CFAFIFepljh0umJpyK15LHDlhg1KTc5xS10ke23YpC1v25\ntDSdCps3wV0VQqWIS35uSRXFvr98g/lEf+K/PL+nMC7VxZRuf3669HYUAAeg\n32JEYWYPXVawwCaL/2sv2AU4YAFngtCklQh6kc0bV/mgMnUVyDbQennKdkBG\nigGgaenOl9iG23RYsCTqlzZvAgCKgP8A6OlhufYWKCZOS0TTr4D+R1WJq/Mf\nbdhlvJ47hMVFb4pzmLRYiSKr55hQzCxynLlNyFeNm1gfQWoetSjrPCvOu8+M\ndNr8yicEy6oL/qTY/h2syV4H3CHnis+u4aqsePdQFQ1mJvMY1ux0ez1qTLFx\n5AZqZI2wGFoynSyMLh1roCPN3XtRy0fdLwjfkkM81D35/DgRFyxTqjRahiHI\nP0PbcdP8jszXLLxgZCopAjJNC0BB9AikyD170x4lJwAsYyM5qE53nFPCN5ry\nUvibtKsByuflqaulIidRg8dKWYNU/fm6QhCauT0l8sMj1qa4klCM1EQoSb2G\nbLNWZe+ISssPMZfIO3n+t2k8zrE+BKDxGbU5qIcGiGdU7SJsmXrTkGFGeYdj\n9Q4d\r\n=6v8m\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTRlqujFEmDUbPb4EhQSAib/Ramy0EgaIWKLzndit00QIgCqeOxGtDeMtBm8xBI1zur/Pp7MQYJ22mmnSJTsM+EkU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822161007_1534954361248_0.29780906770176263"},"_hasShrinkwrap":false},"0.4.5-20180822172253":{"name":"@atomist/sdm","version":"0.4.5-20180822172253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c45091b77c54604a73954b5e6b4f7942345da899","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822172253","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-odW2WP+WnrbDjhhdsyt2rQAkWr6DUGJD8f5BsY0kqUXP+vt+vCj7f/Zb2mBVrynUTmor2VfTOy0CDJzmvF1IEQ==","shasum":"640730c51183e4ae24fb53ce3b95cc5d9d1f3ed3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822172253.tgz","fileCount":837,"unpackedSize":1770796,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfZyvCRA9TVsSAnZWagAAFNEP/3XtgIKBwQJp/XFGldeh\nTRyWh67fwbMP1vcwukr/hggdC4if9nX6csWwQCugsfk8U82glSXbSXMGPYZZ\n3lc9AAoHogpPuzi/9QapFQ4wnSYFM9Tujd+DnFLsrC9JoR0jbEciIICUVDB6\nZMKNW6N/G9G/1LobfUnItouE2v+xGedMAwLzfgCv5sTXSb5+K7V43gnQ0VE/\nyFUqzjLbkydV6qSuUHzCxT87848lupNCFQULOz3s9yQjeqeKZnsW6RltoV0U\nr8Lw3NVlWp0SOzvFPll69pCAJ6QYS7IQufhzUuffmsIIkf1n66lBmbEk53S4\nQr+/GdJrKtdhYR/mUSq6wA4aqeJuKrmHwr7BMxFENNatJ1+9vm4IOF7E8n3s\nUYsgIvPXbx4KYHwbBgbC2pxJ4+xw0tum+6Mb1ap05kaYNaegTFmLCae15YDK\nyA+pihrx5awucDsWxCqt5zbfvqHw4PH159nx6ceinbRWegbagGzjVYzXu5Um\nlwhIOcKYO3qj6MceVGgnlGTi4Wl+e+ZxQANDyo78I2pKztMytqDx3oTbVesQ\nyvjXq2TMg26V6iTWXMUaE5njJPO49X01dp3revCP6Dk24SGluxmJxGteH7Zd\naG9NJWXm8WhmOxf/t7DfMPqTuqKZKKa78jjXu0WmXrKEGQRwNJBb8x9IQUmm\n2MDO\r\n=suUr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCDNkeV5unbPHDg2X3P/o52wn5JlQUIlRmzc40p27lg9wIgU3c9PqR5v0xIK3etgA7i3eLhQ9PadSr6s+0HbzgwQPc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822172253_1534958766045_0.12850167790837075"},"_hasShrinkwrap":false},"0.4.5-20180822212440":{"name":"@atomist/sdm","version":"0.4.5-20180822212440","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b026cdd2333ee5c50b7ba0449adb531e6b49941d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822212440","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Lw8L5StlpR6LjCn99DxG6QEJY/2OVhZQ8W3aHdofgeinWrcFCCW9fYZW+FufDhVGVsk9Shz/Q7MVicAz4zXheQ==","shasum":"5780c9e1107dc2eeacde683215f9f23b7eee7fb4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822212440.tgz","fileCount":837,"unpackedSize":1771013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfdWGCRA9TVsSAnZWagAAVPsP/iGOQGwBYn7ijINVb51W\nP8+IdBc46TT9giabSGBsfQ947qn3pyXlCY94532hGvONI8qQnIisaBfTqNeP\n7gf+hVZ7LidwAyTZBp6gGURiEHhFQD/eqOKorEssZFFS0qRGZqkmbgsQzVOh\nEDk0SpT7x7Qr5JZdj3gx9cTScUbfwvRgaa4OOZF2QXnLtwdDkuUrcCwEYKcw\nu8CFSwMFgK0ZqEPXiBkaHU0G2+ZImY+P/7KWMM8Hj6HlpMdaX0vroBFPqhdH\nAbIyuv6WjctbivA2hbPAzpK9+FFxNuYcDlL+8I7DVr5Z+7Au8iNVn2D5N+a2\nMGgktIADjMAlduKV/tmUUXVo2GezwqlXBO/Prd2ypbHrUJxTV47t0slwefO7\nHke57JOLZ5r/hNCYaalicHJbrgENjN3po9v2lzehXLgPHkMmLkESXb+GyWYD\nhz7NTfXQltXJBbhBbTuHuv2MehVssf+Y0PH6JTUzCsYKUw8gSwYlCoOtdGXR\n1cdmJ/UGL9aUAgDcxuDCr9bLJ14UIcG0B7nWpRZHMFoAhcZIE5fTbALNKbxh\nc412SpltGzzzhC4Z3ofzp2UWzOOl3SbshzoTrqw4gg1i1L8YaXkUFZSTkKer\nM34FUmtkR1aaa3fZhkrOYzmoht0QlVmqYY5a2bO5gMT8cyYU93xnu/9vvDL+\nBEwI\r\n=SUKf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEqU+Qrq+Gsmso5YxYQS2xro9/Rn+UDZyG/lce9on8SXAiEAu9zh8gQfBVSP29Yr558ZD4eierK4ACdral7WzDMAbmA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822212440_1534973317282_0.8453728722402423"},"_hasShrinkwrap":false},"0.4.5-20180822213544":{"name":"@atomist/sdm","version":"0.4.5-20180822213544","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e64a2be44153df39cdc3f977701030ca72f3732d","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822213544","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3AEtdqngt0AEyBJTpt10EWUqyj7Qgci6R/U93/uqXxce+nx5NimnEnAvXXIoUmCLF+b19IO28FBTl3UZ2iac8Q==","shasum":"6d6df18ed0c67eb71fbfbad1f3ca8316ee52a38e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822213544.tgz","fileCount":837,"unpackedSize":1770943,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfdfMCRA9TVsSAnZWagAAC8oP/1E2QP0DUjnwmRkkCw4y\nV2388ZP+8plwc//QZ6FT3taerDBycwdIGsJH043PxrsfDBb3xqdA4M2ndgOy\npLz7zt1jXT/aFNkG7Wg8y6QJS6dHuJOivZfjilJQ5UT+6008pjiwWuVJg+qA\nTIROrXM9FBOlknAb4fc/K3s+LiiwAkYEevw11AFanCi228InLHFfVW6jyixd\nljNVGK4xTNGmmfQd4VyPVIwKByCeqdJP6PnyBQC5i60PsEWZd4XVpQldgzTF\n8E5pOSsP4UWPMSDIynOsphkqwYIqXA37F5y7pCBdifiFpWIiqYpNOChJr91d\nNHclrLl5C1TxBRk4uuDsCwuqBNUOuYrznIxi13tFEFzPyh1VGIymInw4n9oY\ntOzr5Ghnbyf3//N5Ub7ENb2GKOiO6+gEjzZ7Nq2PgyK0BieSwLBTduZwvDs1\ndjHbOzk48tXhGuxJ+puN68cYTHs4XHQQ7XYiu5wviFIj47uqAdblcdwzVKU8\nwEDPFL0l16yHPv5mq5R+64VLF0kDgLknowRW9Ow4X/l45jnXT4GgEUy75VqO\n1aOWb8FnUOKn0NSnZztZv5/sdaq6ccZwHMRgSkQOIFpOIxdy+DDC6qHUlZ/U\nHjGRj2bOKkyhrGni/N3ZxGw3k8WfzTrFoBfgje8uOt6Wo3hTUgG0GBqeQe5S\nL+YQ\r\n=5Lhf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEnxdd9yfityZhRvYP6hdbIwHslNps2cAs6Chw9WtYbiAiEAgQx/QhLWSkzd2CaOgVA3MF8pClLDvJ4fF8OVdvCSvqk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822213544_1534973899438_0.7758485051985144"},"_hasShrinkwrap":false},"0.4.5-20180822213720":{"name":"@atomist/sdm","version":"0.4.5-20180822213720","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e319c7bf7bf44c07bd2c1c8df234886929efe77b","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822213720","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ImMP6rTV3mCbRPOBd591AoEquU7uDsEo3uzjVUrIy8iQJTbbc9nj8ZhOK1GH71r2PiN8KNutcCyVP5KjkcW9ig==","shasum":"1e16ebf7572506ecb6adc6024188b89559aa06e2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822213720.tgz","fileCount":837,"unpackedSize":1770741,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfdgwCRA9TVsSAnZWagAAeYcQAJdmpn/+BlbbQa1S2Vr0\nsDdZwxMvIalXuh1Hy9uTpoVXOlLMd1P0y+xVV5IUv8RmElxmoixyFSpCsjj8\nIKFdBPHimScUK+osgeNE3CajE6RBwFUdX3eZFnzyVC3gsnC+34ggGqDsTqzT\npkrLxvLSs1/+dRe3c2q3T+K4nRDNMyowtQyHU69KrGBNIozGix5Qobw2zTLj\n/TaycuA4hBRkODNNcDqASzgyPS6bMTysCz9UC9NwTn+wWSBpZIrDk5se5dNn\nXifRN0hDoRYDyRWLQsEwyVmeHA4DUf33QxXIt+RWUryXTr5+KIKAc+xviowE\nmA4OaEqG3u/yFro5dsqm07gQJbw+xZVW/Rdh2hrrPa8haTXzha8DLU7cO4Dn\nvm67Zsnd1+VRm4/Bvhatd7o82fPwWTkrN2W3Q7CByXBR6nyWO93fL5OmV6ZH\nyR2x32BIkDiYTMKjjq2nD48C2nZO4dTGUg+gnHyTELAxGQPV4Bw4kvODXnQZ\nzJLJfbFDUwSTFDFzScfodnpLtge/PpOK1QAvY473XG+dlj+xck4SRoVugolj\n7T/vVeEMKI/zyHeW4ek3mB5pPqEr9hHx/fpo65EXNKBLiQsEk6/jL2hBo31z\npvGkr4vEDBTepM6xfynRNzMT3/goA1HEtxr7kOgRCrKr3TFDzzxJJrqjCDN/\n6PTk\r\n=fI+3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGxX3PbIpmBGtDlPCIe19/5txUeV2oNqjfV4yt8+2seJAiBEaBoGLhKGVipvCxKFTnriuNIGP1WTZdEM/70H0PVn0g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822213720_1534973999971_0.2721766070421674"},"_hasShrinkwrap":false},"0.4.5-20180822214349":{"name":"@atomist/sdm","version":"0.4.5-20180822214349","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6078c21dccd30a1f833d4e81e17cc4a544ac2fe5","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822214349","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AwNtI0ubFFeYm3MFwWgMja6+RnTCul5c96UPkEk9Zl2TNw3P3uNrl5NNwFCuTXS0+3wauRRNsMAWA9t/jlGHKA==","shasum":"1fd7c591f2cdccbe7c694663324deb340ab32b3a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822214349.tgz","fileCount":837,"unpackedSize":1770741,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfdm6CRA9TVsSAnZWagAAUzIP/1h/Co7vfXoUOLhO2jpz\nwbduAZtfclh+IBnK66XAtC7VX0AUZqdANKqVBQEXZC8jj4SJiV9fiIp8GOtO\nycHZ1lxJODDuUQqvQOXCbXuUgWu6hEaWmLq1TOyDrA3FU/8ck9R69lr9Exuo\nhXKdttKTov1EZPpkdXjuQTttiZOK0+J5vTiOlLVAKq30NvjENTaywpxupcO7\nGJZIHba6tMS6UYIUAtkNG/ni4qS6JFIrdc2R6x+NbqFIhtC6J+vsyaiZYjDV\nJO/HWHDWzykyQIjfPxAbeL9g48cCJ5WIPIPZFMc6ErZ5TbvN6XYMSUNog46b\nCQAlDojhPiZf1zgJVim8mjiwlk8HMHBlN+a/5EpdTc9jGQaRRol4XH48s0hP\nBvmFueeDOrONKqSesIlhYFgzoRMYd+FdDbHhFytgnqqSSxh0mBdBNpQrnwnq\nrhakV1vEwuLRLk1nULa/mElTt+kGB6vUtcVHRkqeRSJRVLNdS8fS4Pv7xRAQ\np7zw8MYemyYe4FVRLzq9438wWTb57aZitjca6OQS88FjKZnZbRIUUR34JeQL\noVEvCGsvA9aVB36RhJtMWs0kGFCYbtPq1eZIh8aa7SSN3aijHT9BFENVLne8\nWXy/Fhb+S4di88IHklqDkWhYVC6tywuc42prbdVgvc5zDnxgPwBILwARLxNq\nKOeG\r\n=p4PV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCIUTWKBcu75fjm1BNxSshTDbWGG15aPImPxL+qNDO1hQIhAMt0N6U2p04NXfbgijHjvDEB4rZ/Z7fK4A1ehLGwwnyc"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822214349_1534974393679_0.14634951962495624"},"_hasShrinkwrap":false},"0.4.5-20180822214840":{"name":"@atomist/sdm","version":"0.4.5-20180822214840","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4d76b93df270759a599b9322dc18b386277a348a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180822214840","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NMLx+V8EdvW/GNOTAktjVs3T8kuhYpvLSsydskAB71tEHjqA8X7AvjG2jIZng81mlfInYmwAjosSKYH8+IYQwg==","shasum":"899fca8bdaaa8eb2ba5ff1bf43e16b27eeb95086","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180822214840.tgz","fileCount":837,"unpackedSize":1770741,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfdrxCRA9TVsSAnZWagAAbXMP/iLClL8DHib1qhcccuzj\nY/1RcXNyvHqELssrIHSRhLF/cekuILDAVWUKbIqPNnRc8DebofhoAsWZgyfH\nDgUcfU81Y8MELPny1izyYbDfvyPvoLXaTcuPJSuMHtxJEXfmnCQnNVsCeBxX\nuuBx7duZmgrxiyMGH1p6goLPUjo+k2pHJqQCYEaqEEabkM3A46yV7ROEbEQA\nDwxgtSqS7j+hSRYiXCsS4CLcLTiirGCdpEPC3ga2YuO49xIpFbhzB88UwvQB\nNWKG8fCo4yXx9c6Lt28nm1S6xQwqImzqY7DoY8aVTUyHYLlLgZxhQZOEC9Bp\nvWuDsVlm93geoCeOgABi49Gzw1SSXWuB4tr+pwfxlb47594WJ9WtD39WTf4N\n7ViNP9TGgU6PqsNZxFIEVRd6m12TPPq8tBoQ1xdKz5YA86dyj7GSnOihADbx\nckXMH3jrne+lIvpeipUUPMgP2yZqH16qXVbau4aFMowrr9xRJYl3i1EEOFYq\nWj3xg3zhvFGzVdxCI/ylUbfFTGVNeo7YlWzkHLhTEICLYz60RjvKwHf003Zq\ng5/NSlYbK52XnN2Ekvmo0gG3YSx0WZCBwB4+jnN60p70ZwgxNS2Nk9HdF/JY\nkBUXp0ic5cerzL5MiEcC0XB3bNMBsDBZWTzaZdOidnXLWMn0iDWOH4NtaSdr\nvjVc\r\n=n5Y5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTiL48V+105pFLHWtjZIPtIYDOEIfQRtT/jZA299mHXAIgD4eeRJA44otzpLNHEdVMu0hz+MK3j2rv2sRoKaUkryI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180822214840_1534974704341_0.899749213703541"},"_hasShrinkwrap":false},"0.4.5-20180823082746":{"name":"@atomist/sdm","version":"0.4.5-20180823082746","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9c195fd0e2d5668675d9a3ca9e28ac2871ba6639","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180823082746","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tSG6graLdh1aMv51OsTuELFa0RJArw8TPfjWSCQdwtWrh9RwOt0oZvOr8IfzCT9KRJeUEM47gBoLcggV8UOpwA==","shasum":"b833de25424b5db14c10a90785b2606324ff9d97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180823082746.tgz","fileCount":837,"unpackedSize":1772295,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfnCVCRA9TVsSAnZWagAA5gIP/1Groqmy5xvKOZ+AgtAP\nEY38Q5M+TID6VHi21aY1n21HKoy2NW+x4QNaWfTVu4+8Fu90g5BAs99Hd0sX\n0W61/J18u+xWQSZIaW1rVk6YMqR9AHlxQ11oqIWshs7UmeMFVBi51C/agWQy\n/JSPZGMMK0ctpiQ6Sc/tHS1tMVdZe7TEB+BJ+WJVJbZYeoGHWe4NyJrIh0zx\nv1VawPVjkoECPinzJqLkcDctn3jBO/NqH+td+LcDaz9l5lzKhRyr0KJyjKiG\niR6uPN+K/mqrp5POU68XmX8WV962/hTe++GhHNUvOTbpJVVeZKvJzrs+TcZ/\nVxH0U4sh9WlDGgRdyhjVepTeopucNsSiiQGC5Rl+rGqg2J2wzq8oe91r88LH\n5oXFbdfOwXBABpwir8fQFX3KyzZXxiCRS0f+UGBdLQCXzno2PUmyNncqdC60\nT1Caiyrmk8fVZLYvN05CxZnyw+BheeLN/CUtlAgrC1//BXJz/YWCGzfTyrpy\nZwb2x0FPJY2vKsIMiUo8krnMhOg5myryFiUgym7N+0NNq/fD79fMqargvXwK\nYcFUPwEAOz2UlanVzM/g6w2Kz/RJ1snEXj+Nx86/F+zW8m5V99Ejpkg2vHAB\nBJOhS8/JPFPXSaIEeFFKQwu1q2OUcEDu8VMDHwz+DGkeO0H9L9N8G6lBxo1U\n1hjv\r\n=zrbZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCLeDjjtfu15E0r1Zn6LSGm9AJbjCpdxtessKTZP3h8lAIgTfudQqMYPe2uSNhBXLbfkcItO33ZrgomEu7fvigmvbc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180823082746_1535013013022_0.8309148591163922"},"_hasShrinkwrap":false},"0.4.5-20180823094122":{"name":"@atomist/sdm","version":"0.4.5-20180823094122","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3c8d754b38ce7505b618e95e0e0f6ef4905c2e9a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180823094122","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6Thyn35Y1g29vMjzCs8E1kRz+I442BhZuTC/QdYgt3CzCUB9uE6gIiAOaHnqC8qU7McKFUcio916E34vokJGTA==","shasum":"50824756f08c52e1973b9c56078629a25f4692de","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180823094122.tgz","fileCount":837,"unpackedSize":1829354,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfoHNCRA9TVsSAnZWagAA+lIP/1wPXCyraqEHaFAPgVvh\nRqv/S48F9YYNkx7PQX9K2fwk8SJ5jJbuS465XPZFEekynq77h8FbhV0msHcS\nA8tB1oh81NcOOvAUtR+wYggzz7mDIJ91DLzs5F1v5trsdHx+m19FySx6pTq5\noBo9NZa7ViT6gxwKYqw7hwn1oJz6B8M6Hn78FzvpRv3CdoLMogaVv0JkkW6Q\npv26ebGW3u+ShqRAjSM7kfctErzfG2ktm6kbbTYG1HuutRCTxvK/EjeRN+dD\neJWMFlHqcs8uGmT9KL9eSZfZYYV4IUllRPAW3qxYHT8/MedRZoyrimKSpGNL\nDQancOuV3EKO/VAdUjsl8r7E03lgzMhNJfQmk5q3W9foGwypAbz0olydeJYT\nmNWQ+5TGRJlMyLWjTSFKdwSvEtPqehxf6HIRwiWcuQos+n6R1OG/KCHrn43R\nAoecvtMtr3Kl0NBoapX34eq3ILs4LG0X7PpYQSc7Bt5jeuXZkM2OUAHYCzNW\nzpRmexmXQaqxbsSTT1nP32Qwt7s1/Jo8osSaCYGGu9nPfykWopBncPfQN71L\nVXalpZ/E9smThtaEnm+yLBwo0kkCYayDd7QLaRaFhAZ/8f4F5ypW7nlhb9jG\nlYj3m5Lo3nIv1EI9H4+A+3BNj6YTo8pgn2Nj/1XShyo+eGrYdui1P7UjfsKF\nSgOn\r\n=F0nX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICaHwBLsQvRn0GkgmYhwyiBtOICoHUvXrKu9wndamtTNAiEA5gCBSATCOqnqqWQWMe+LyWQObK9v9DtvN4PhTWToDkk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180823094122_1535017420120_0.503677038604915"},"_hasShrinkwrap":false},"0.4.5-20180823100141":{"name":"@atomist/sdm","version":"0.4.5-20180823100141","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8766bfe65619e4f1da7b645ba87864f84d84794c","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180823100141","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-flRwFuaFJ4ftguu4OySW6RsNMJE9rDV9a9wNhU8Se9vjVmPIMdeK9ygEM6oixLJgXjrAMc3u/+FTjz1ZOOEodw==","shasum":"45e1fe0f6f7fa942ee1a9fe0f72b2134c0a12719","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180823100141.tgz","fileCount":837,"unpackedSize":1829617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfoaoCRA9TVsSAnZWagAAn0IP/0JbQPs0ytyDZJl0TYPn\nDZ/yIvnxMiWgXeIcwv6lHlzuHeu/X+7W5W6j2nuMpv8pG7fRf5jw5lMFGA3J\njaxfGnXK5LweuGqGDVkDzukg4pZ99m2h0OAr/4sCRRn7rf9q/D8AkXGVCr91\nbLUt9HRd0B/qTYEX/TEmeFhf+gk/OJeV36Exkx6HA0hrSIQgCwWthH8FlazC\nhRMDCmpu3upOlHOtSLQj0HlEkGGsoQAF92v5itHL/XJ/P90bSbyB0dl/H/j9\noM8RBnJiqujf+QfFLc5TThosdnFh6uLw1F+7trr2uTimijgl2iGj6PCKwSUs\nz4QfxzNJLQnjRLO01zlskOFUvAwS8aBhrAI1LbChAcm6n/QaZomaVcHeLyBE\n90pAIvZta2V7FKrZ7CpGmdiDF7/nn7LNkpE/9SjoqmlsFpPHTPzcnfFkZVp6\nZ8NR9JM3Edg2vXuTYUa27dgg+u4MzFITmz5IajLuTws/tFfcS0UTQnAgNnkk\nT2XEcHCoyN4y6QHT74luUzE2KlY0XKgtM0PYpXTnxvz67qFd6c2b+EHlckLQ\ngDjydxCAsts+4By6jkyVjMuX/RK698NdGGr9TNaOtRXwx1/dg9/9bcy3jkPK\nsfPMesVvIhN3NqpyAy1kVtIPPgHQKTYDwXRj1VUXFT8dftqINBtBnIJy6KQx\nkFID\r\n=XrrM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAbcNmtaNUCBMQ00IQIWOrtm+poomPlEzraJesFkSUrhAiEAsL2FSeWbpvEDooUwW/mPKXrp4p2hz9G4+2xFZY2eOvM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180823100141_1535018663889_0.4689295458621019"},"_hasShrinkwrap":false},"0.4.5-20180823100916":{"name":"@atomist/sdm","version":"0.4.5-20180823100916","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b312254e820900be0fc852fceef98e256759bbbd","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.5-20180823100916","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Fljic/gXV6CGvyAzKUIAhmqgh07Y6RnxEF2OC9taFoZUPVwdZlCC9gJsk71kmBETPVP2Pp4H6ipMg/e3X3Cx/A==","shasum":"7217fc947a1192525226e4f3bae5da5eba1cf4bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5-20180823100916.tgz","fileCount":837,"unpackedSize":1829620,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfoiaCRA9TVsSAnZWagAANAYP/AsXKJ2+FdHhmB7jQp2H\n3G7DPofnSOEi1sz37LcMkHe/poPGEYBEExxRr4dvhjDIvrwt+VqelqwdfqwA\n/fnRBAX5hTNXO+cuYQw26oLNnr3Tjs9nK4Z2FR/yLAHl1vPLnuACd2sK0C4S\nSkvonk1h7qi6YFQWgns7Qqvk7vnBlCgb6zW/u0wQV8MVzgBku1bMqkEJpEBJ\nBQ8FcP+wWiBURuSy4ha8abp6Xb4a6njsYcl9h+VeKqpXGvv3IzQridQJ1cDw\na0bNZPkja51VxW/4I2vDiYNEAJVoT0enDyHY3bwGUPqDml7TZKGTO4iYuORH\n7NcIzd/PpXYa6MuDcxvk2jNmJiPu2b8Cun2kIkAyCZkK+X7GSKI2oEGH4mQE\npQDRISxgm8pNu8VBcU/tgdIFhFT/0fkEGnclv9rCKTJpS7O/jZ1bRdt3CUUq\nYzCG5NQ3oGekezSDg3VNSLbGdHFaATXmIJt1OIbZcw6q7U+J9aBmhvvGxudm\ngzLluoxNTrVT2VYCkUBDnaLeA3onjh1o4DPCM8pvoqEFbmOsBjwAy0MRsa2m\nSqzB4F9MCVhjwnwG+LghG8MEU64xvJ4tBWh5lRopsf9xRQotDLF//zFx0WjK\nmK2liulPIYCFv2rsu/sILRUO8xFIEGqSPc8nCYTyZnbeCcOfP8eSgpwPUv1P\npkwG\r\n=vF7n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDaKBQaxVIhanvNOEotpkTHbdO65IB/Mr02uJ2MGVd8BAIhAK7DaWuo9NwK3j3Y8JTtTgTfuU0G1XaD2bXBYVUoSQ2/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5-20180823100916_1535019162044_0.6325588646665015"},"_hasShrinkwrap":false},"0.4.5":{"name":"@atomist/sdm","version":"0.4.5","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b312254e820900be0fc852fceef98e256759bbbd","_id":"@atomist/sdm@0.4.5","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FAdISGNaRIBoUhr0Hp8plwqPXmWyflOfVnyx4OVVOnTDV0fgkClaF4hgFlHLJ6Nl3+Mg9E9Zf99Z9uV3Yq9LVQ==","shasum":"a3736c19d8c8b16388a7c756d4c3c1d08a2e2a33","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.5.tgz","fileCount":837,"unpackedSize":1829605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfoqJCRA9TVsSAnZWagAAQ2EQAIwC+MfRSpwQ09LSeneI\nVLmzsCT7i0ZokCz5FUdbE7k7mGC8xbVT1ccuV/h6GqE4/tlumEWjYj3NAkgF\nwMMhip944ISXDw29XxVJSQTfWupb8I1f9iRdF8JLXzYNWuL82ig/2yVGaylO\nohv6D3au5vK89u2UHyCrJvMJwzQYGYcZzMg7p0BwOGs1Jqz+DaT2MS3hhoGI\n6Zp7iHhBDcSKNxcDg6n+6DqVeBhOVVaBGHWGQS9g7uVwBHAyAl0gtlALZgK/\nzN1w4YGT5jKodWnb+nHUNpLxEA4vFLgiPPmcZ2n8mJCsdnf9XnnozhRgHeye\nF4xZhruMXh5xxsq0LqA7Ct4b0JJAHGLRJ1USLfOSPxInU6MiRc7/8x8z6XCY\nCvXQ13fHncA/v4BCySy1m0ZeSpByDIk25N9vhbbB4lQYXrykolmVevBSfzr4\neEzk6a7zRHojXG2tKM8peSM/PTplFMRbi4IDlpHjq7Rjxiwe3tPYcATKh1Ac\ntrGLlZpGE1W8F2iQwA1SQIklSO9wdeVGg27xYlX8Jtk0uAd61Xn4q9GOnuCa\nTGKEAXQO2b2p3AZFg5OH90+d5m/fRh5f3B6dYq/C0pNx7FUH4bi9C9rY9A+J\nLnWeiuQ4uPGUYz6SLD11WWRga4z+OV2YEOgClgz5JSKZYeA+pB14Cv9otnrQ\ntBen\r\n=7PW7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDAsh/5kuWauAM8yjfEDTQkuQbS2W41Hlw3FXusTMLScgIhAO0FoPxjxZBcTY4J53I41Kp6E0ttEzJWkecH6t/YOvcf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.5_1535019656764_0.589440271681454"},"_hasShrinkwrap":false},"0.4.6-20180823102228":{"name":"@atomist/sdm","version":"0.4.6-20180823102228","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8c0f119be4f08e6720979eee449f650efca3535a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-20180823102228","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z/76RE2wsANGVLXevokpGEqYO3+TyeF+/ujr4D1arogTDyVgXoi6ESS+/hn4HJ7iK+uCGjf+UgVedh3ioW78/A==","shasum":"8e6a4198840030c8c122137bdd82e6ddd939ca25","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-20180823102228.tgz","fileCount":837,"unpackedSize":1829699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfovSCRA9TVsSAnZWagAA/dsP/0Q/EQSajBa4FsUsYP/d\nH+0xnbqCto2pk0bbdlhCovjGo1/jPiNd5Nx9u+W+yNgRDLwGQg9CWh3YDY41\ntw/Onl2gITGT+iQWfW/tfdDlPKM6U85UgGtHPHEjRIG/SkfuUVJkXC69L99n\n99uRZoGlZ2etkeLGrzQQxgYIdhC7jKIxMy+dMCbSLHul0eGoo8Ck0DCGlPZ5\nnokSjS1Yj0Sb2q0FFQgS+V3AHzaqE3ZpmS7sAQHbqMZ0+/aN90zcca+083wd\nvsuuiD7f3t5qF1OQDiSPIXUKMpovf0pTU5NWuBrNMNPJHRejMmAG6WsXHOzw\ngC1Hhyh0VjK7LuJjnXxGbskRferbSS8D+qIJXoe2Txkt4z1s6P6wYwbWbmzc\nHxw0fvnkjh91/z8W9oPP/JJaEqiNQAfSstifUsLcJSQx2Bfoa7icE2DI1yQv\nvfE2EZXow4VQVYL38h+DceYqEwYYONPm3taC5rLjw3veBdfc/LgfK2vv7GHp\nCKj0xuRhFt9DL2DOGWSjhJ8y+3Vxu8RcTJQsM8nhtFfNQfuEA9oFHdenW2yj\n/jQI/6gDospEouc37uqcTzcOpTSBFGj9pNJqx9yIiWVuuGjk+dy5LmZ7mtvu\nByNyFUFPVkiAjhTxYjbKrG9EpYRwHScfXuWGEIlO7Vx082N7GdsIdGc3Oueq\nfIls\r\n=PftG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDyZniWGJ+MuPj2ZaPqxWgTFce7t52jegFFOQPnkcGIKAIhAMfv1gG5nDtabbep1X8oG9/BLoYFr9BVq7y5yfYsh1Wl"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-20180823102228_1535019985592_0.7862301607104154"},"_hasShrinkwrap":false},"0.4.6-nortissej.cleanup-log.20180823175234":{"name":"@atomist/sdm","version":"0.4.6-nortissej.cleanup-log.20180823175234","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"38d07e940f0f34b6cdea4895d88ffa7415f58900","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-nortissej.cleanup-log.20180823175234","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5g/5iogj8sUHf24WAza7a1s2wGYnl+PgreemEnj/RqgzAMi654jbPWCL7xqU7k3Jt3eJxoXqZd4+IzLbrErnqQ==","shasum":"12df50281e24dda206f331720e52f7cfd28d0f45","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-nortissej.cleanup-log.20180823175234.tgz","fileCount":837,"unpackedSize":1830217,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfvUqCRA9TVsSAnZWagAAJhEQAJyGJx7np9oID5thOK/m\nJPiHSLOjtSQF4sjxrdt8/sHpSTRulrViDZIJyN+J9M+ciJmymPlOXtfvsauI\nbSq2u2fZFnCGDjKtlithbJNQWF/kaoEOK+4nL+GZVm3eAOK720OuJspMEFUm\n/+W9zOby6JwyAPRtDx9jjSGXNaDQqldRKxvAqvpoUUSFzUsx/2oOuwhm8u+u\ng741bGRIayjwop25T9U1kvM07h4Pi4YW2abXtJByEx+PuXS/lHWN3IRYhxdD\nXqqrajhFGSq2AO+oZnS8ao2iItWySkXWJ9sKN56JloXJPkuvHuv2ITi98e2B\ng6XlWNHm1rmpxGt92Fo0bFQc5LbuMV2ejLRWwpopv2XSvUpXNDvC8Gav8ewe\nlLPn0QL+XIzCs7iYAex0Ii7owlCvLmzK6uP3XLq5Pcml6srLl04MPPyjeM3o\n117d5iYIFsQUo56WnAicfOyGhpjpnDBrFSFJUHAo4KBYd1d5vhgcbrQLld7h\ngw660NGWb8xjLqurG4J0+FPba9OUifuEglf39tEwAak3JY9qz46ahpKhtCbT\no/lWA43yvQEKQa4kcWkFtZy7YxowRxkvKqxUTp2a9jx1X2/gPmX9/0+3ei5p\naNQF8b7adVedGgrgGluTBGAF0n2q6KwSuacriiTE+Afb/FQT1NuqW9P3VR2C\nxexi\r\n=k7Kw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfQrXK5wipXb9hYqcYDm0TNw+LwwiIDfX9fWB7ANf/8QIhALkp4AlFtJMYmh1oYXQ8PfTgQTPJM7oFRrbqK67uoGS5"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-nortissej.cleanup-log.20180823175234_1535046954294_0.9026028867075933"},"_hasShrinkwrap":false},"0.4.6-20180823202257":{"name":"@atomist/sdm","version":"0.4.6-20180823202257","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"de570ca5ac4473b8ad149ac13873b0dcbd62bbd9","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-20180823202257","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-c7vdXiXvFIKumY4i5ciA7wsmFHm9mm/SVJDd+BItxMhfyqqBhAJ3RwzDLWp1fP7kuCG0XAoljhXWA+x8h8b7Bg==","shasum":"8eb37ac496b4d1395184a3f667eb15e2ed818cff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-20180823202257.tgz","fileCount":837,"unpackedSize":1829699,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfxhOCRA9TVsSAnZWagAAx3sP/272DTpkjqSQpsCAjua9\nZyE1DlqrTEKhbakkOBvAm7S2/RKzofE6iDo5e62tHPRkld5O/F10dUxQhqdx\nEHWGyyBy3h+Pb7K1NWXkgJy/kGhNWhHXP87hjhiCflX+SkedSN0eaVS5z964\nFdBlU+tw5lkiS2XgcIT6tmxRhyHfbsRlrYOaDtOdYQcZlO1Bpr9UnkvTeplz\nJlh78nuBzVmagrgY+x0MMFUK5rVVjfqzh+7PTMNinrGseGDT8fEOHozM+T0U\nZotWMdM++g2HTALw49xfc27DOhy4i/JinDmATEYc4falKadv/NtqjIkEf2Nr\nsaCdJqIZos/sUl43BwwvF1nnO46QlSjNfbKNpvQKVqlkG2Bd0MUNnONfGhE8\nRck7/hSD0mtgAm3n5TVQIZHxQJwkQBrwSo7Ft9/oWWkTsmq59eKiyt9ChUJn\nGoApBpEJooyPrvCeLDs7GzlvhGfUNlDQaAAGYPjqHwB5EBfi9VZhek87kD2l\nmwMZVyzbuX/hW16Xe7UhG7fIZkAWXsWfk1hkpC0k3hovJARM26v0NuaKUSTQ\nyqBdcc44ChOGeJf0rkOWVaVAn9dxiO/GeXhUtIO8h8/GSC5SJ+AuuQBG45eI\ntkF15KurVynk3fVPIJrYS1Wb6pG5gWw6wcdXWX2xKWQlzQMd1CaD9t1d4LP4\nB12I\r\n=CrK4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE2TtyGpB4UPxrOIzJcf502ZHO6ePGb0QcjTmsq39jVVAiEAsnUu1+RcthabHtL2Y4++uuyEvxtfCPqxqaXq0fziXsY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-20180823202257_1535055948756_0.6533785348791354"},"_hasShrinkwrap":false},"0.4.6-20180823211528":{"name":"@atomist/sdm","version":"0.4.6-20180823211528","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"77b5d929455b1fb7412a4820056e8a856d857dd3","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-20180823211528","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-40+BPSlnw59erECIwZYiydqApBp/fMKf61vE3eKJsqbT45xmaDXGUGQM4wZTGiHFWhBLjIVAZrjjbzuzyVQ+XQ==","shasum":"a919b241d6693b88022dceed4b65c96dc1126c88","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-20180823211528.tgz","fileCount":837,"unpackedSize":1829754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfySvCRA9TVsSAnZWagAAvrUP/1NjSuKqkqmEakimbfDu\nTa8JLjMfgR1iCADcMSxYniOf8jhB3IwGrlCCanScFLtldb2iHu/o+bpLFne3\nkPcfaFi+Oe7SuIblc9QXxQuzjb1LidRwRI4z93C60FKU0uC6fCNnMZx7RPIv\npm6ky/4kk6Y55Ir+C8L7q5T6rNXA6NbHGTcF+f6OVYrvVAPqedag7NmON5SR\nad7929rUN/oIMzCGf//QyYlBkYsIPcY/yf/Sso2cxi/UOqXO8iB2xUpoN5U5\n87a5EjML+OU1cBSQN+910duD3k6vYdb2L/LaEfCfULfxdZn4SAsd+X8ZATo4\nLAgNmGitwqqrwsDQLhDfIQrfQZBlIQx7uYXk0bWkPTYMQGUJz1XU7+xNRfEt\nKLreMVQ1lzqclnuzUQR4Greyvf/V8p5Aske4FdFnkfhne25OJQwQcE3bU/Pt\nU/d5hL6IR7iUgvyRMofbuEl7owIwY5GW++E58M7RsR0ggMM8SQzSbx3+QZvR\nzO9m/gfwz+DN3/yspc3EZCVx4muwrtMmPVBvDld3gKWBjM4i2vK/WvTg8LON\n5Kz3DdJcdGlx/aIQAio/S3NgQVtzOVzbrYN4x2zk/SuNR1afStILWKaTyBQR\nVrpSAUp/eH/JsmGoEmXBIxceMGkpexnQsQDC9g/o2ZWoWtv0m5iOauIFBk0y\nqjWr\r\n=3Eud\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDne1XrSBsjI0GaEvLIgd4OotyoQFKaoU+p+tuBGPmGbgIhAJyOHsn+Sx3ULOqNeBtYvWgM5N9IPMYxXBG4mNFn6LW7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-20180823211528_1535059118482_0.42920108982622085"},"_hasShrinkwrap":false},"0.4.6-20180823211731":{"name":"@atomist/sdm","version":"0.4.6-20180823211731","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"466119ddd4a0dce270aec13de5f0dedcdd735d6a","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-20180823211731","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ai5A2ec2qwyqXsX45wom1M76KEsgAFakYcdCz89nGtvMuzMjh4oP+MrUT5BIxjhg+EF/AaCChx1pud7uP7TMYw==","shasum":"f62bd2ca2dea9b1301e16ec736fd6c2f154fda0e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-20180823211731.tgz","fileCount":837,"unpackedSize":1829696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfyUwCRA9TVsSAnZWagAAEDEP/3opcOk+K/z2o5mhUuWZ\nVbbRklfdBus4wKXtN3L8pnrgi1mN2OjGf1dGZkM6dzbAEtmfux14vJJwT/+U\nlXYXS3zjSWGkC1QIICdGdV6WS9UyZkb0gIK4AqZ6g9duRGMYxKtJGFso1MNn\nTM46NA9Ewp2BQlv1KlJ3tDo/9I72nGZDncpyHYsqLE/Z3jOqB5/j5N7IDPMW\nzXjF90CZNwiO80pZKojdLoD3ExcYHVPv+Ubt23I6HbpHMAO4/WAEtDW9Gycv\nSUw8SfFqAIYWJv+SQr7lTe/tdXYha0cROdIZHRSRZmT03fGjgRoQks0DQoeT\nfDpg0KhG8UJtUiDAbg2dtyGnWUMcWGYof6py7aRiTWojzE6EjjjqFymXJIdJ\nKYRUExoEBdC2wEI66OAoJCGlSQo825JOZ916k3i3JtCSsITYgJFlbrJn14yi\nnbEhj7zjgjsPLwAWjNBGVOO36kguccJSX2jzAyOvyDFKZBuylSolDJDMIt+Z\n+tHp4RU7FNCY9adhNqa/hXu85KZ5ba05nxfXPpohdITM3idKLmb0Xra+Bd8W\nS7Gozv7mVfTRC1P88Li5vW/bThtlhaI37ZgJ3H8OpSRqKyBiMMMXEmK31h3e\n6YYrQMUZ3ZnBWFTIrQl4hScSeZ6Ef03vqm/HiOeKbUTJRCXzMMuO/ZE4FE7Z\n8Jjz\r\n=4jTe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBKHVqcmIYm3C69Q07r5HVLTBoYvW6fteZkbsE3ZepS4AiAArI+NUDhpF60VJIJcOByxb622aRJHhPETroEI4MqFhg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-20180823211731_1535059247375_0.968617445756716"},"_hasShrinkwrap":false},"0.4.6-20180823213826":{"name":"@atomist/sdm","version":"0.4.6-20180823213826","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"78367d63baa0e1cecc4c61ee4750896bfd8992d2","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-20180823213826","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1BZvVSwfsxMQbJyyQakR7Qrip5jBRFh5Mi8nrYUSR060+t/lvR5oT+4gfhA5kMZkHsfa/W2kKxU9WL0lB1E9GA==","shasum":"e9a9a6f9023b2f5db03d6b9c615216f941d3fc40","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-20180823213826.tgz","fileCount":837,"unpackedSize":1829736,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbfyoqCRA9TVsSAnZWagAA6g4P+QESIPSpLatjk2fSWp+t\nbKGiXYqZTZGyNcnSTcXjcxo7vPdUYLJcmOGL2ybuQwFDMImFqK29P8kv6CDQ\no1w1zPK3bhrTo5DLJ6rAMllL2OidyPXAR5rYYpRUjFMqnHrNkyL/5FodE8rD\nlPDXEUzstWpsc/HOptMe7QapWfpvrwodvqM5vDt8OZGKwOaD+NnoMVvt5utS\nM2XbXl2hy34Qx9Xtu5bfcEG5/hh43RHtekEPxi+5cmbGXZFacl/mPolWYWlX\nKHSuFTynjnt6bG/jSLG23r3RALHsxUOSfAZzwQOoj4NOEwcOZn6gazHu2wkK\ne8yKkbZYh8fTe8NQqMjHrhAdscwVhAPNZShgXYDCRtthAiw3ddx9fo4JaAQh\nQB2t695XpxinpKJIdH2uqAVpmIkM8DYSgM8rKpToQVUwDsg1LQSMn86VkJs6\noFyiT/H9nHsebNs6H7OprkjyunbdBNSkWgtgSr2QyEa2NIe2V9YLmwEPjjg+\nwj5NhIHIZ97WLMoQ2/9xsNtk2Lv9sYTpm0cioqxTu0hVvu7h1/yu1RzJw+9y\nehMORggg/e0bxZva/jySM/y291Z3fi/MvXWDLeFgCy9Vbq0JHRftOBtUOoSY\n6MLKvhITHz45SOW3OJgQ4EZMK9FPTnv36oZyhicltu2jTB/G4R3f0LN90qP6\nbjmu\r\n=ql1I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEpooK8JFiWpjVJf3pHr3vg7M/WxUDQ8aY4MHGZK1vp0AiBCih+sUhZUdCyliFBgdLf41emzkcGSc2u+j5Yx5mgcOA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-20180823213826_1535060521605_0.9184476576142704"},"_hasShrinkwrap":false},"0.4.6":{"name":"@atomist/sdm","version":"0.4.6","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"78367d63baa0e1cecc4c61ee4750896bfd8992d2","_id":"@atomist/sdm@0.4.6","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bb4Jla0mB9ZOR7tb2uxAtZsGs95e7qCYlmD9z66K87hpk2z3FS29s+e6P5e9heaPMIj1BeErTqaY8RR1DMFhfw==","shasum":"9c75ad28401969f9b07666dcc3e716353ef4c412","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6.tgz","fileCount":837,"unpackedSize":1829721,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf44GCRA9TVsSAnZWagAASHIP/iItLhZPUGv+uM3GHrY3\no0tEXKOzxkxWgb0baMQKQsWh97iaHpkoWjDU3wlv1volhJC+gq4A5goIgxoN\nnq2uvFNiAm3JpJZO58Eopeq4f4hUPpS6W01vTsprPPwecqj2QVcOwfvFt7QS\nedw3tPlkkSsayfp7rfwsHl6mfubS9meVqNGuDHU7qCw+3Tqr0fAsPhGERxcp\nSEoUKD/CNvstR7pcq9uQecZE+AN9+Zqm1kfzPD/ZwWyuiJc5XXQQduOubVWI\n5+uPdp6rYG50cWpGcmRtBSq1mv9GI0+sBXMeGg19UbOBtwdu05A+7L2EVyhE\nMrnBzIP6WVCUBkYr1qyPPb2pyPtgQKpLrNs0Mcs5R2JeC0nikhQlDVTWw+7Z\nI4HDittye0+vUpEqwhUkBy+Es2YKY5hzNP4GCmtvOqGWYbPUjS42WoV1y6bh\nMERtcMsRLLw38UZDR0cBHLUPoh5b9sRlqY+EqrBDQu5QlmZI+HnLKxsXFki8\nVpp5+z65pIDJrmC3pifFejJVrJ0d/NkZU2o+pU0OvTMu86wbsnufV1aBvO3D\nsv9uC59OrLBykMSreDpsPy0gF2WWIkncKz1fPGvJv4W8ihmSC3M6bKiu+kDx\n+R7ZrcM/gb18Zbgb3WQP9xBeO6awdy3fpneeWG7vpJZvwQ2xw+RnxWNkVzPC\nXowu\r\n=R1pt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMNuTXOe5PpeUH7iJ3vRlSpJFN0qjR68s6zSfDUTZSnwIhAM/uQmN8HYF9lnDv04/0I6AIECsoj+tJhmdMy/iJn0ZY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6_1535086085847_0.7468076400787214"},"_hasShrinkwrap":false},"0.4.7-20180824044915":{"name":"@atomist/sdm","version":"0.4.7-20180824044915","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bdd1b6af6b53473fae0660287120eed8a28f0722","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.7-20180824044915","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+Ux2rMmlKwkGImH/563PNBgncl4bv7oiJGsA8KNXInAan1koDpZfj8QDAi0FsaJRQT9LbAkb805ns12HjyngEQ==","shasum":"442ade9919219d508717861f137f5ff88ecad10b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.7-20180824044915.tgz","fileCount":837,"unpackedSize":1829815,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf48KCRA9TVsSAnZWagAAcNUQAJvA8wtkno1X8h9TDUvw\ncfC7H5CVe7gLLAWn+44Z8zu26XPh3Giufl5Z2Xl5BFScVOJXIeU0jA1bVktY\ndsv2XrTyit0kUP7qDGPeSkjKrI0u57sVbDtEX479XsWC1Cq8kjkrB3XQsZAS\nuw4QsE2eqoe4pkDQQ8mwWYwdsri9o62QrbClSv16gJtN9iXxJPyelDquMIuc\nEwXTpyqMblPFRtsAuAR9VPmBmkc+7ax+t01NeC9AJtxkTzVVycy2zscSoVgZ\nYjJZOSYoF+hukDz7W6VFgVErYuAma6onRfxAUC1vHbK08/rJL4+ySqV5GqQD\nJ4f3PX1HYqcmibPtIhbDwROaSSBkmDDnP/zFvIe6VPh1LEohFhUhcbvReR+v\nvO5aGIYQYJe4v1XXbKwZy27Fxp6HZNT9hnVB/Xj1v+KJ8gCDIZS52agX/Gob\nu7ouhua3+eZ8Vf141+VGQaufzSPCMQVWqFvXra9YmuX4yrewWKNVrefeRB+c\nlaf0FgxGuGDOV55uxutdJKJaabsZYZyrYshqaNlFLYCBoU79akpGI3VeqlzU\nG51TBH3zpFpl76y54YKTQ9F3v4Me+4Jr0OvYPHpFb5llluZ+4cANxt7uboBW\nVGOU2omZgyrLRg9AXDnMLxu6MXM7ZuwYIrFAGB+XkSJZV71KbO93xg4m7WKE\n0+Ne\r\n=3r1M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCIJQoEqqJaFImINU02H4CFXC0EsHZpuAxPpCXxnmgi5gIgJ2tYqXjfOy98zefFv3tPzWU207oD/M55hU7SURV0zko="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.7-20180824044915_1535086345797_0.6992910824357861"},"_hasShrinkwrap":false},"0.4.7-20180824050851":{"name":"@atomist/sdm","version":"0.4.7-20180824050851","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7ca6bf608bd853f69b6cdbf51a5ea3596f896f16","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.7-20180824050851","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rC1hw48I137oQjIFT6iIaXAIlM2e3pMzfDbJ/qrc9v1G749q2xS4vfvT1qypej7sVuMza+PeTPsWYd8PO3JM1g==","shasum":"89c4abaa2c4c1a8e6994afe8b18e31cdc88f4777","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.7-20180824050851.tgz","fileCount":845,"unpackedSize":1837408,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf5OCCRA9TVsSAnZWagAAhPAP/23PHr+hdB6/6QYg4248\n4C3cJf+5CjTNRqdRZtB2aHdO+6BXcl3qLsSBJ0r2V0eC3bungXfVz82hc5P/\nfct2RSq39BBVnyxeQzMWcUEhjmbzVt83H0ekDe6pom1Goq49IVyZGuP9sXSH\nephjtLRZA0ccPBnpKYbK9hddomFKJQIaf0KK8nLtV9ZrHb8IGF8KeqPD6zir\np9q/Ow/SvI6Gss15fYsW5MSLGb7fGaBmWJmYYCf7EkEH1txvkG3aACMFrLqK\nRVGwZ6PTteGQEOvt72nXNhtbeHq4f4ft9cM+3wPc4Bgs2UtDyp2tEBoFv1+m\nBwTUnuSsxDcyg8eGLmxgTLV9NaZ0HxGxnpUMQzkt7b7CmTN2CC5peaO96s9X\naV6nED3HRc4tmmWON1HZnGriPLn+FD6ltWHePp8syjVZsMG3qTEyA6jtBmSr\nGUYC4qoTCNzfsA31aa0IdWHcoU3LUgCLpqL+HLihxoLk7yMGxiHhxnZQIbDK\nlfBlijC0JSy6HzJCVDx6bo/K3CLsb0AkYSjhHJYjH3WbrOOugstHSdUquyIu\nPTfToi/Oa0SuAOxJC8g7BuyGQK/6fvVqPRV9PEkaBm/TNlBTmI+qzvRw9U6x\nQS+nHgQ1EScliykeUiWUObnj/6O4ljDH9NGm53cS8sm/WWEopsNsG3e56gwZ\n5IrB\r\n=wAGL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHvjoV4JgjHQId4FoBF6LKy8Z8B+o53pLxIiDB9aun9LAiEAm7AOiX+PH6aJZQYrY3fnGxB9HpKM8cj8KwuP6GKf6DM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.7-20180824050851_1535087490154_0.4463343562476725"},"_hasShrinkwrap":false},"0.4.7-20180824051434":{"name":"@atomist/sdm","version":"0.4.7-20180824051434","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c50fed5be00efd9e553e6591971c168f3b8b9738","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.7-20180824051434","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jQsNMPT+L1I0aYqAynJMM85/OsIR9I9XpeGlNZtBfA8TQDi9x4CtCHeFqgcbX7KFEBIyg76cPgJgkrm357wL7w==","shasum":"da05f4412e619474ccc92591820a9e2dadfadd05","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.7-20180824051434.tgz","fileCount":845,"unpackedSize":1837567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf5TnCRA9TVsSAnZWagAASRsP/RbCN1V35yoEYT6C8zym\nGVHWm1sL7MhLdJsAmj6qaXbBnUjYOF8tRuad8BoXbf6lq2BhAf2oY/KFPS85\nRXpJxVBZbcgWFIrcKypLg9MCvd4Nf/6VgoARtZI4IAj7fMFguLyouyROtdhQ\nBNeTgfbol1U7X4OsdtNtk1vHFkJSChyZ3hcAt7R+9IyppjcvpJnf1dwZwv4r\nL8pNRhjGZsUl/16g7jOrNLMfTcgiD+CXIP8Obxm3d68CWFMbH227WZ6dmmvs\nnnv74IdtsoRFNQhMddTBkWq0RCjUUlf/X0f3CedWZlpD0c2vqphuZZSPXGh5\nQ0pt69O4YXPU1PD6erKfNRKnNBAw5vQb+ow6g+uvPJs6TsIVnCi7bCFAp+FN\nOlzELu34SJ/mcZ5MlPjcjUlPNCDNYZU76T6ZnLJS7s0uhnk1jEn66Ruz1p8d\nJCXsYEx5N70vTBPauMUNBwi/hVZdHnefRcOcoH9tET4G9bFN8MvCkzUx4NgC\nnABz0dLHOu8fkHIWAelWcAwn0U59RQ4N+viuHJ7Ee0pRP2fQbiRnYEf6M87A\nFcMsFwm0gb47/JKk1nTK5/BuiugAwn3LKPm0vjOGrbfVL3tNK8pa9+M6ehSB\nCJxcaqTeiJory8XRJn6c05KyEbr/YX3uX8gPFjyvpvFU8wToEYzkx3+IRfGm\nvMLg\r\n=DFQF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIANPKABilFfKaWUR6rkn6DWvzfoXCiHiRi6S9tGKGbJYAiBMfsSXmEU1wtCZ+vxAgxikfFWFr1I5lwMCXTtM2SWUJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.7-20180824051434_1535087846459_0.24432925699767583"},"_hasShrinkwrap":false},"0.4.7":{"name":"@atomist/sdm","version":"0.4.7","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c50fed5be00efd9e553e6591971c168f3b8b9738","_id":"@atomist/sdm@0.4.7","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NMNaYkSFdkFJP6lqnClvlqFVRBS5iA2LPQZ7qGZEe2Xph8z3g4Y1LzUedQyLbpoftPeSXMfKu3qynTZLkn2qGA==","shasum":"1b4ee777f4389449da56ec2d06f343ee13fa6536","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.7.tgz","fileCount":845,"unpackedSize":1837552,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf5fWCRA9TVsSAnZWagAAp4YP+gKBxs1dbhtVFxeDlt5w\nmgsbfalzsXAmnaWYQYu51BgjmUPfUMRnZk+CGFzQ7Y3PeXP1sdUeFkj77bk2\nRlX5DOaJ9wwFmkQnw6rLwwsgdMIutJIL4sDzsy46kOagU88aopQOY+2e5nVd\n+p3CXEPnExzwYesgmdTI5AkwKmEVNCuQBnBdR3nwJc0hv5sJfSQwgzmR5DE3\n99pubzLgpuYruDRlRKKVQT1ehYmmKJg+3YVE1XZcxtyZF366Bsf5mZBrOHi/\n9jYN1y8lrrVvpWe9sfbNr9Zufb4XfzFEMgywyYnDKmtU/UZ/d2qDyaNcvAkF\n+cugfxI3Bb/KUxYx7szm3qHb30MkDhHhVp4YtTiYa9A5QR4DmEc+wa4d3/gJ\nGB3Q6Ivn6Vo+Ig3lpdyjKr367dUhMSnNRZn4AtWinaoRnUu9Ns9Vlgrte9gE\npXB0ShBIFrcbFa7mF6w0QTR1FuaKo+xdX3TV+wFLzEWzITtfpo5LSCWsKYdP\njPnh6ZcWrcPt+tlRgeGY1559C7qu4LLAiJW71h7IFNSbZtadknZ3J+cmEVuy\nqG2fxUq8sj0ipRZqumqQKYM5WZJM2e2e8ZA0n2bl+SRikMr4UOrLjTxYVZAk\naifBOvr0a2N8lCmFvEUCYUQ488q8eNgTrPrZXK+mBBazkSD7NLtLuvaB/j6B\nrQFs\r\n=3WXj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDes+NOmEKdbGbqixnOmMr1UytzHdDGH5zRBOMD4AJlsQIgJSdGfn8Xeuy23GOSlhAp7pHLW51PZ7E6sFR74hMkmWI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.7_1535088597495_0.3680940004208286"},"_hasShrinkwrap":false},"0.4.8-20180824053120":{"name":"@atomist/sdm","version":"0.4.8-20180824053120","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d0cfab67a7fafef371790ab23284ce73952a04a3","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-20180824053120","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7dHiQvGRRj1AUCqTM7aqXcMwVcP/ueAabdqxU+WHD4RQmZKb3uj/dmegBRjZg25mHgbIzR0OGA3S1ocDvoz/0w==","shasum":"1fe477d3a64ee799000b8b4d42b91845002c6c18","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-20180824053120.tgz","fileCount":845,"unpackedSize":1837646,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf5jcCRA9TVsSAnZWagAAfycP/iaQmyCM724eC9bKvFJ+\nee950ZmD87Z8T4lLr1TyLTRitmWgjh6T/b70K/jW1r0yfUOfxF4ZBBVix9vy\nt07cQnU514JBrvfvmxNg2Bb2bFD1mH+rGu/ZzJr5h/gXTPyk8LB2lcJCKG/6\nKiDJyX0mgUIvrzC5YlAvekLUyBamBZADlKphQWul809Ndf7REVKStGG+iuNJ\n5H7/i6eZsSN6baHiYMXedh4KriejB9sJDqhYscPuzd9sbQY3mGcxSvo9zgYm\ncAdKih7eMiUuo3yOhPDmf6GoOWDFhw5ipIZVxcbZeS9z3MQsfIgNmPDI1BdQ\nOgxMxlLvOjWHic3obaVHYUL2f2pKT32dDf5NSCUriWMsw8I65N5yfff/GCAE\nvPBSuw9Ogq48PB+XP9BBrQlzC5P0KvIVaYySYUvVPRWH9EuWhPTkf5bCPiLv\nmo19mhrOJCa1NYsXKEyRbgbQY3pIk9k2cKruQcaJoDW1OHaW3nlIrC8JSjFe\nzdGC+59N55xfob80AjTf4KU7+a3ToI8uDvy5xLtleROJiQ21Zwhd6Phf6VZC\nMN/9kSDnwI98bdmoMVip8zvgvaonm1XX6mpDf6Z5Nn4Kpjt43hhxH65OB0+z\na877w+9vuFl5tlH98uhV/RUSnAs79ukE3t2LwnGiShYnx6kGOcSYHQ9OV9UQ\n2q/H\r\n=aG6O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCifxcHq64CXe2ID1+2yrt8x6q9v9C+gL2b5tLHnAh33QIgXteBRTmcytfx2RB0aU6urVqL+qvzKU49c1f7XfyyF7o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-20180824053120_1535088859349_0.4915119745358536"},"_hasShrinkwrap":false},"0.4.6-nortissej.cleanup-log.20180824093414":{"name":"@atomist/sdm","version":"0.4.6-nortissej.cleanup-log.20180824093414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b4145d957c1a4cecd90da33428166138c9ad493e","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.6-nortissej.cleanup-log.20180824093414","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w1KESdDvc2O/9Y2sstR37lBhQJHRvgiSyvNOMc9QGTJFGC6vCnMsUl8c13+B8eqMMfVOJrEs8rIJX4PIgj5Fkw==","shasum":"d17c1e2e08fb5740efdb1f87160de8b6cecbe15b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.6-nortissej.cleanup-log.20180824093414.tgz","fileCount":837,"unpackedSize":1830216,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf9GiCRA9TVsSAnZWagAA53gQAIv8/wv/KvtbmcOHjvF7\nHURK5rcDRQZ2ebcVhTrhs764Tb1N0EIAdEJusY4zNoMB6WGKSi46kn0CeVjo\nGxQu0KT9HPjutvf5HuZxWsGulaHjXzHuXrTL2GS2RWVyV1khv0nW/aeOyKMF\nB7P+E0Z1Y9wMfgiA+3RZMZ6H51VJsAssmv0npd1HwkSB/5nOT9ULYhvxJMM+\nQTR59Z65Fkx4ZVfEVWIKSJA89AWsl+vycKewnhmUZYL3Lv53Q3UQC3+ps7qH\nrk0EVfSlzcnTwdhE+R0WQ82hdXri6OTR6rfQg7dUiAM7OKCiU0M5BbOYf7+r\n9936Jn1Q+iYzoDQEvgmNbfKwruK6iJ/i/LEvu/cqOtF04dlMGx+D2sYACpf+\nua7QoZQNNRssnlW+XY0VvKTUsbSDfLpfS0NJF7sBpL0I1TF3GqCU9c1tKNtt\n9IP54qq9dA1iubqgnUFm7k8FTsSeE2SmCUUIyRWTekIHAbpmqJaIWdOsZSwv\nDAHjAzMOk3QyI9JgXOzxk3W4p6N9rt+uyNrCO5GKEKGTZfkS2DxWtRO5xJ9f\nSOho6AS0VHGUSaU7CxfuIbJpH+M5FLUUp/68+QKr/hL2SJSnvkIPB5T4A+LO\nuLDRIPZ9PE2Q9MbHvIuT2LjyuvY0nIKV86DX3Ce9awiASEGD/pcOk7pOdTxC\nQnCL\r\n=g+Ct\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDByAvbwab/jjiNSUt/2IdBeqDFdLR3R4f+3DqyTX+SdgIhAOIFVogJszD13pWN7uQw14iyr9DuDoYk/6vi0/1zncz2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.6-nortissej.cleanup-log.20180824093414_1535103394140_0.2800966942080434"},"_hasShrinkwrap":false},"0.4.8-20180824093654":{"name":"@atomist/sdm","version":"0.4.8-20180824093654","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f29c277f537944d2b4147e922b90531d858e9024","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-20180824093654","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-stiI3jQBp2hoBo5pFQWLs2X1OtTuCZJrssViHYt5umefvkPbnsoZ8MJv8OiDS7rNx2Jr7TMt2d5u+lNuL2Pvow==","shasum":"de22c0d14a4c04cb483cfe7bec019a34e4bd9ca6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-20180824093654.tgz","fileCount":845,"unpackedSize":1838141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbf9JOCRA9TVsSAnZWagAA3l8QAJePTD9n+kJg8fYZZhjW\nUuQ7PRZbMZkFhHPPFJjGTE/m4u8ORJK5RQmhIX0OQBS4VFAsVrHQ+EnmqpDO\nAmjR8gcZylnlbYUgi/ANqxs4p9WusYsNaiuDQp+FWHP4uZvWibCsONpkyUfH\n8JfzH2n3FySAezzbmFfFzX/KiSYXv55bc1Q75ZVOaVaSS2POgefhP8ZtDlFs\nT7o5vTNfenHyNr2J3yPzqun/mjvsu9wkPmKhuVrsqvwHWr1sQAiCJ+mrm497\nTrmy7JTkAjX//Y3RnXZAPHKo0g3O2KdWC0g0CEsW1bRb9M7Um6KpBgVORz55\n87QjuJ9071R3grW1h1l1KOKqysEQ50UsugFNaq83wHxJMBW6IkW7bQAnN76/\n5YRAqhYicwbyCWvFvWP7QS0WoTL83dRnnjGsV/6RUPiBqcxo2yO2OOda2IYw\nLeUPxc2A+2J8VB3v2GwnIi6wyon/A/zKfZAj6JGUtKE3eTI8B/jsGh06ewkq\nBCAmVqPmpYNnusBoYtGqjbSFM5oIcFl4Lzi2yxOiFRYfb4GLvXuQ62ncSZtR\nHB7PihYPS+Z/AVglKLo3ZeSGtTKTT2Dai/FkXxn3oc7JbkCvwX6iSaSFd27X\nxIFZ4DwgoPwBrZjFPB3SUpMxxmBROL8dRFmegiWi9TYLb3JPTU/PaDGKCjIM\nosdB\r\n=pQ6P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/goHlwlzuFJQR3IRN2aBfmeLU2mnuCS/+cCwTETFWKwIgIJjz6/ZG7ADUYRJ/+N7lFJtNjvlVZkKKj1MyOgoqX1g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-20180824093654_1535103566184_0.1650828954617578"},"_hasShrinkwrap":false},"0.4.8-nortissej.print-more-on-diff-error.20180825231317":{"name":"@atomist/sdm","version":"0.4.8-nortissej.print-more-on-diff-error.20180825231317","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4ffb5baaee0c8e46fb57e291c33ffac712e3f55c","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-nortissej.print-more-on-diff-error.20180825231317","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sUWo3Wse22t+NpnbAy7O06w6pbEDvmkA/EqQ6Yqr6MW1lgKRUSUcHM9tGW94cZ52sFEtE+OWDHGwtT2HYTJ7Eg==","shasum":"85de9cf8b1c0898a6d3eaef93b4de906a4b04df2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-nortissej.print-more-on-diff-error.20180825231317.tgz","fileCount":846,"unpackedSize":1843792,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbgeMhCRA9TVsSAnZWagAAIrgP/jmylrRgXG1hjxbBXiVG\nQV8sdxB+BxWbcZX25lCih85s+S1Pxx+TWmgd1SJUpVJJGdxgbnZIAueGy66t\nqR8nzZAM/4UOfSYaU1h7qoIk5DPIlO+sAYSXHU530uf9ZdUbQpU7b4Bm/8DK\nix96m+jmxxdPb9JWlYxdJIgn1O28wTm3GfLi7zc+vTufwDatCbb1kWcRLXhZ\n5BCNHGa/Uzd3v9pg9H6VJcOvzjDRFR2oJ6n9VkfsXOtQnrTLPkYOBiWB7pUN\nBPTIxHTdsIiLTI6F93+Gdsi+e8ONyG9U+tHn4nEBuof7WjXWwm7N7/L71/Md\n8NAoEpObS9eVZ74B/TA0vvTKw8mDXUFoS0+FTi6eBnhL82LTx7SDFIbQ+veA\nheFPKUvGcyjMmKJuU4TDXemR7G2l+IF7lxWz//8orBsHlBxAZY/I3zBICRpJ\nXImAwELpbDPnN6AdNzSXzJoOAvSgae+QCVU3QbIT5ubvAgbOUOOBW+O+wNEv\n10AHxUEuQ2MBwJlr0bJGJrc9ZXDGyTIvWNmEkkNY2wZWiV2PSt3REnlXjFGU\nHzTo1aLzLPZUrFGr6z3dVjTl0Tkxh1GJSobxPWzVS7YFAf9dQ3Lh3sIdVfHL\nDpxOfxZibpHUHKpcpv+X51phv/Z2RYhJvs8N4Frd+wXAZQ4s4emI8nOomtNa\nz5j5\r\n=s5Ma\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQOeO71zEAmIwZW5Ip1y7p3wLz7GYwHHQQNFwRMV9NbAiEA0TKu7tr/LeHL/AdTl5POJ5mN6C2+JOWBTIH830IRs8k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-nortissej.print-more-on-diff-error.20180825231317_1535238944646_0.430527005871679"},"_hasShrinkwrap":false},"0.4.8-nortissej.print-more-on-diff-error.20180825232717":{"name":"@atomist/sdm","version":"0.4.8-nortissej.print-more-on-diff-error.20180825232717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"61873822614364b42dae5c5c52459b5a4abe93ef","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-nortissej.print-more-on-diff-error.20180825232717","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MK+EEnV9zkXRyfJ5UJn0p8mKdMIoXCqgWwAsKaSh4N7HU6PM1DgfMAk/3u9T9S9G141rbtX3g3BFu5Ifn7ZkhQ==","shasum":"63181a7ba5fdf3a6f4475f239417c068ed5cd5c4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-nortissej.print-more-on-diff-error.20180825232717.tgz","fileCount":846,"unpackedSize":1844171,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbgeZpCRA9TVsSAnZWagAAUkwP/AwjOu+dk089vTx+zk4V\nMHpG8DNPRoNATNa8jI/6nAlt4JRFBszVzJ6+EkMobw0HwsMCogP+mjF+VdhY\nqIOA6Lrq2xpcawtLWn9hy/x/yyvFDVFrnNtzjkCExU4wMmUq3Cx1ynsPsGo0\nWif6nafH17Ishp0pfRQF6z1lgV/spWCv1+QaGvDn6y+55ARysGgN/7ac2aPK\nfaJNbaXmTfJAg0mQ+PeLT2LHmnw3LhV9qYyX5dSPWH2MIQDqDNfOzNVTCD5K\nR7RV0yAvPsUo/3sE61GYSfKFdUWiEXOYB1764CThJwGikWYD7gypqm9spj2Q\np0y4nkqfu+q3QSR+8vwIE3KAoDtkVhr+j/Ar/uf37pMnSxV6n6RHXsuuBAg/\nw58FVhgByp+xVaFRREh7clHSz2cPKFCPwNbgf5HdfFBP/LJfACHj3Fd2x7nl\nL6JeJP8ig7H+l0hIaNUEK4Tqo7BGcptV7A7GpAOYGg05jqPXPPVk3br+Nulb\nbCpIIRPzPSM/MpVUL5PVTMCINEViWj/UJTNa9g16iNHfwcRTFN1arhBk4X24\nn8e3P4UTRN54OyvhJv+Mh3CWrxu/xf2YatUCoATqn2Abf/heA82/irhm+WKX\nu15Lw7XwHKDVx5kzjSJ2dPQfb8Y0fflnVObFL4X9QhEMhV/9Q1VBoUJ3cKg9\nm+Zm\r\n=8Flz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGLeZGFcN0bQAVg9HPkOLQsW5TayeMN1CfVYksPhHMNFAiASZvSXBWfcNsUNIQ2T/H4lcXAvizrTpvjg/9jZvV7fRg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-nortissej.print-more-on-diff-error.20180825232717_1535239784555_0.05438058872111151"},"_hasShrinkwrap":false},"0.4.8-20180826124729":{"name":"@atomist/sdm","version":"0.4.8-20180826124729","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0d9146ca91c716a8eafac5481c67ac6152f61ae4","readme":"# Software Delivery Machine\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/e20aa31a-6ac6-4887-b8aa-10f089ea73f7)](https://app.atomist.com/workspace/T29E48P34) [![npm version](https://badge.fury.io/js/%40atomist%2Fsdm.svg)](https://badge.fury.io/js/%40atomist%2Fsdm)\n\nAtomist framework enabling you to control your delivery and development process in code. Think of it as Spring Boot for software delivery.\n\n## What is a Software Delivery Machine?\nA **software delivery machine**  (SDM) is a development process in a box. It automates all steps in the flow from commit to production (potentially via staging environments), and many other actions, using the consistent model provided by the Atomist *API for software*.\n\n> Many teams have a blueprint in their mind for how they'd like to deliver software and ease their day to day work, but find it hard to realize. A Software Delivery Machine makes it possible.\n\nThe concept is explained in detail in Rod Johnson's blog [Why you need a Software Delivery Machine](https://the-composition.com/why-you-need-a-software-delivery-machine-85e8399cdfc0). This [video](https://vimeo.com/260496136) shows it in action.\n\n> Atomist is about developing your development experience by using your coding skills. Change the code, restart, and see your new automations and changed behavior across all your projects, within seconds. \n\n## Get Started\n\nTo run an SDM of your own that connects to the Atomist API for Software, start with [Sample SDM](https://github.com/atomist/sample-sdm).\n\n\nThis repository contains the API and helpers for an SDM framework. The implementation of the framework, built on lower level Atomist capabilities, lives in [sdm-core](https://github.com/atomist/sdm-core). \n\nSDMs based on this framework process events from the Atomist SaaS event hub. The architecture is as follows, with events coming in from the systems that matter in your development process:\n\n<img src=\"https://atomist.com/img/Atomist-Team-Development.jpg\"/>\n\nYou'll need to be a member of an Atomist workspace to run an SDM.\nCreate your own by [enrolling](https://github.com/atomist/welcome/blob/master/enroll.md) at [atomist.com](https://atomist.com).\nThings work best if you install an org webhook, so that Atomist receives events for all your GitHub repos.\n\nOnce the Atomist bot is in your Slack team, type `@atomist create sdm` to have Atomist create a personalized SDM instance using this project. You can also clone the `sample-sdm` project.\n\nOnce your SDM is running, type `@atomist show skills` in any channel to see a list of all available Atomist commands.\n\n## Run Locally\n\nSDM projects are Atomist automation clients, written in [TypeScript](https://www.typescriptlang.org) or JavaScript. See [run an automation client](https://github.com/atomist/welcome/blob/master/runClient.md) for instructions on how to set up your environment and run it under Node.js. \n\nSee [set up](./docs/Setup.md) for additional prerequisites depending on the projects you're building.\n\nSee the [sample-sdm project](https://github.com/atomist/sample-sdm) project for instructions on how to run an SDM instance, and description of the out of the box functionality.\n\n### Dependencies\n\nA concrete SDM (such as [Sample SDM](https://github.com/atomist/sample-sdm)) depends on this project, on [sdm-core](https://github.com/atomist/sdm-core), and on any number of SDM Packs. Packs bring\nin functionality related to a particular technology. For instance, there is a [Spring pack](https://github.com/atomist/sdm-pack-spring), a [Sonarqube pack](https://github.com/atomist/sdm-pack-sonarqube),\na [Kubernetes pack](https://github.com/atomist/sdm-pack-k8s), and more.\n\n## Core Concepts\nAtomist is a flexible platform, enabling you to build your own automations or use those provided by Atomist or third parties. Because you're using a real programming language (not YAML or Bash), and you have access to a real ecosystem (Node), you can create a richer delivery experience than you've even imagined.\n\nThis project demonstrates Atomist as the *API for software*, exposing:\n\n- *What we know*: The Atomist cortex, accessible through GraphQL queries and subscription joins\n- *What just happened*: An event, triggered by a GraphQL subscription, which is contextualized with the existing knowledge\n- *What you're working on*: A library that enables you to comprehend and manipulate the source code you're working on.\n\nThis project builds on other Atomist core functionality available from global automations, such as: Atomist **lifecycle**, showing commit, pull request and other activity through actionable messages.\n\nAtomist is not tied to GitHub, but this repository focuses on using Atomist with GitHub.com or\nGitHub Enterprise.\n\n\n### Events\nThe heart of Atomist is its event handling. As your code flows from commit\nthrough to deployment and beyond, Atomist receives events, correlates the incoming data\nwith its previous knowledge, and invokes your event handlers with rich context. This enables your automations to perform tasks such as:\n\n- Scanning code for security or quality issues on every push\n- Driving deployments and promotion between environments\n- Performing custom actions on deployment, such as kicking off integration test suites.\n\nThe Atomist correlated event model also enables Atomist to provide you with visibility throughout the commit to deployment flow, in Slack or through the Atomist web dashboard.\n\n#### Under the Hood: How it Works\nEvent handlers subscribe to events using [GraphQL](http://graphql.org) subscriptions against the Atomist cortex. The following GraphQL subscribes to completed builds, returning related data such as the last commit and any linked Slack channels:\n\n```graphql\nsubscription OnBuildComplete {\n  Build {\n    buildId\n    buildUrl\n    compareUrl\n    name\n    status\n    commit {\n      sha\n      message\n      repo {\n        name\n        owner\n        gitHubId\n        allowRebaseMerge\n        channels {\n          name\n          id\n        }\n      }\n      statuses {\n        context\n        description\n        state\n        targetUrl\n      }\n    }\n  }\n}\n```\nWhen using TypeScript (our recommended language), an event handler can subscribe to such events with the benefit of strong typing. For example, this Atomist event handler can respond to the above GraphQL subscription:\n\n```typescript\n@EventHandler(\"Set status on build complete\",\n    GraphQL.subscriptionFromFile(\"graphql/subscription/OnBuildComplete.graphql\"))\nexport class SetStatusOnBuildComplete implements HandleEvent<OnBuildComplete.Subscription> {\n\n    public async handle(event: EventFired<OnBuildComplete.Subscription>, \n    \tctx: HandlerContext, \n    \tparams: this): Promise<HandlerResult> {\n```\n\nThis underlying GraphQL/event handler infrastructure is generic and powerful. However, many things are better done at a higher level. This project provides a framework above this infrastructure that makes typical tasks far easier, while not preventing you from breaking out into lower level functionality. \n\n> This repository\n> includes event handlers that subscribe to the most important events in a typical\n> delivery flow. This enables dynamic and sophisticated delivery processes that are consistent across\n> multiple projects.\n\n### Goals and Listeners\n\nThe most important higher level SDM functionality relates to what happens on a push to a repository. An SDM allows you to process a push in any way you choose, but typically you want it to initiate a delivery flow.\n\n#### Goals\n\nAn SDM allows you to set **goals** on push. Goals correspond to the actions that make up a delivery flow, such as build and deployment. Goals are not necessarily sequential--some may be executed in parallel--but certain goals, such as deployment, have preconditions (goals that must have previously completed successfully).\n\nGoals are set using **rules**, which are typically expressed in a simple internal DSL. For example, the following rules use `PushTest` predicates such as `ToDefaultBranch` and `IsMaven` to determine what goals to set for incoming pushes:\n\n```typescript\nwhenPushSatisfies(ToDefaultBranch, IsMaven, HasSpringBootApplicationClass, HasCloudFoundryManifest,\n    \tToPublicRepo, not(NamedSeedRepo), not(FromAtomist), IsDeployEnabled)\n    .setGoals(HttpServiceGoals),\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, not(FromAtomist))\n    .itMeans(\"Spring Boot service local deploy\")\n    .setGoals(LocalDeploymentGoals),\n```\n\nPush test predicates are easy to write using the Atomist API. For example:\n\n```typescript\nexport const IsMaven: PredicatePushTest = predicatePushTest(\n    \"Is Maven\",\n    async p => !!(await p.getFile(\"pom.xml\")));\n```\n\nGoals are defined as follows:\n\n```typescript\nexport const HttpServiceGoals = new Goals(\n    \"HTTP Service\",\n    FingerprintGoal,\n    AutofixGoal,\n    ReviewGoal,\n    PushReactionGoal,\n    BuildGoal,\n    ArtifactGoal,\n    StagingDeploymentGoal,\n    StagingEndpointGoal,\n    StagingVerifiedGoal,\n    ProductionDeploymentGoal,\n    ProductionEndpointGoal);\n```\n\nIt is possible to define new goals with accompanying implementations, making this approach highly extensible.\n\n#### Listeners\nWe'll return to push tests shortly, but first let's consider the SDM listener concept.\n\nWhile the goals set drive the delivery process, domain specific **listeners** help in goal implementation and allow observation of the process as it unfolds. Listener **registrations** allow selective listener firing, on only particular pushes. A registration includes a name (for diagnostics) and a `PushTest`, narrowing on particular pushes.\n\nFor example, the following listener registration causes an automatic fix to be made on every push to a Node project, adding a license file if none is found:\n\n```typescript\n sdm.addAutofixes({\n        name: \"fix me\",\n        pushTest: IsNode,\n        action: async cri => {\n            const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n            return cri.project.addFile(\"LICENSE\", license.data);\n        },\n    })\n\n```\n\nThe following listener observes a build, notifying any linked Slack channels of its status:\n\n```typescript\nsdm.addBuildListeners(async br => \n        br.addressChannels(`Build of ${br.id.repo} has status ${br.build.status}`));\n```\n\n> SDM listeners are a layer above GraphQL subscriptions and event handlers that simplify common scenarios, and enable most functionality to be naturally expressed in terms of the problem domain. Listener implementations are also easily testable.\n\n##### Common Listener Context\nAs with all good frameworks, we've tried to make the API consistent. All listener invocations include at least the following generally useful information:\n\n```typescript\nexport interface SdmContext {\n\n    /**\n     * Context of the Atomist EventHandler invocation. Use to run GraphQL\n     * queries, use the messageClient directly and find\n     * the team and correlation id\n     */\n    context: HandlerContext;\n\n    /**\n     * If available, provides a way to address the channel(s) related to this event.\n     * This is usually, but not always, the channels linked to a\n     * In some cases, such as repo creation or a push to a repo where there is no linked channel,\n     * addressChannels will go to dev/null without error.\n     */\n    addressChannels: AddressChannels;\n\n    /**\n     * Credentials for use with source control hosts such as GitHub\n     */\n    credentials: ProjectOperationCredentials;\n\n}\n```\nMost events concern a specific repository, and hence most listener invocations extend `RepoContext`:\n\n```typescript\nexport interface RepoContext extends SdmContext {\n\n    /**\n     * The repo this relates to\n     */\n    id: RemoteRepoRef;\n\n}\n```\n\nMany repo-specific listeners are given access to the repository source, via the `Project` abstraction:\n\n```typescript\nexport interface ProjectListenerInvocation extends RepoListenerInvocation {\n\n    /**\n     * The project to which this event relates. It will have been cloned\n     * prior to this invocation. Modifications made during listener invocation will\n     * not be committed back to the project (although they are acceptable if necessary, for\n     * example to run particular commands against the project).\n     * As well as working with\n     * project files using the Project superinterface, we can use git-related\n     * functionality fro the GitProject subinterface: For example to check\n     * for previous shas.\n     * We can also easily run shell commands against the project using its baseDir.\n     */\n    project: GitProject;\n\n}\n\n```\nThe `Project` interface is defined in [@atomist/automation-client](https://github.com/atomist/automation-client-ts). It provides an abstraction to the present repository, with Atomist taking care of Git cloning and (if necessary) writing back any changes via a push. It is abstracted from the file system, making it easy to unit listeners accessing repository contents, using the `InMemoryProject` and `InMemoryFile` classes. \n\n> The Project API and sophisticated parsing functionality available on top of it is a core Atomist capability. Many events can only be understood in the context of the impacted code, and many actions are achieved by modifying code.\n\nPush listeners also have access to the details of the relevant push:\n\n```typescript\nexport interface PushListenerInvocation extends ProjectListenerInvocation {\n\n\t /**\n     * Information about the push, including repo and commit\n     */\n    readonly push: OnPushToAnyBranch.Push;\n\n}\n```\n\n##### Available Listener Interfaces\nThe following listener interfaces are available:\n\n- `ArtifactListener`: Invoked when a new binary has been created\n- `BuildListener`: Invoked when a build is complete. \n- `ChannelLinkListenerInvocation`: Invoked when a channel is linked to a repo\n- `ClosedIssueListener`: Invoked when an issue is closed\n- `PushReactionListener`: Invoked in response to a code change\n- `DeploymentListener`: Invoked when a deployment has succeeded\n- `FingerprintDifferenceListener`: Invoked when a fingerprint has changed\n- `GoalsSetListener`: Invoked when goals are set on a push\n- `Listener`: Superinterface for all listeners\n- `NewIssueListener`: Invoked when an issue has been created\n- `ProjectListener`: Superinterface for all listeners that relate to a project and make the cloned project available\n- `PullRequestListener`: Invoked when a pull request is raised\n- `PushListener`: Superinterface for listeners to push events\n- `RepoCreationListener`: Invoked when a repository has been created\n- `SupersededListener`: Invoked when a commit has been superseded by a subsequent commit\n- `TagListener`: Invoked when a repo is created\n- `UpdatedIssueListener`: Invoked when an issue has been updated\n- `UserJoiningChannelListener`: Invoked when a user joins a channel\n- `VerifiedDeploymentListener`: Invoked when an endpoint has been verified\n\n\n#### Push Mappings\nLet's now return to push mappings and goal setting. The `PushMapping` interface is used to decide how to handle pushes. Normally it is used via the DSL we've seen.\n\n```typescript\nexport interface PushMapping<V> {\n\n    /**\n     * Name of the PushMapping. Must be unique\n     */\n    readonly name: string;\n\n    /**\n     * Compute a value for the given push. Return undefined\n     * if we don't find a mapped value.\n     * Return DoNotSetAnyGoals (null) to shortcut evaluation of the present set of rules,\n     * terminating evaluation and guarantee the return of undefined if we've reached this point.\n     * Only do so if you are sure\n     * that this evaluation must be short circuited if it has reached this point.\n     * If a previous rule has matched, it will still be used.\n     * The value may be static\n     * or computed on demand, depending on the implementation.\n     * @param {PushListenerInvocation} p\n     * @return {Promise<V | undefined | NeverMatch>}\n     */\n    valueForPush(p: PushListenerInvocation): Promise<V | undefined | NeverMatch>;\n}\n```\n`PushMapping` is a central interface used in many places.\n\nA `GoalSetter` is a `PushMapping` that returns `Goals`.\n\nA `PushTest` is simply a `PushMapping` that returns `boolean`.\n\n## Code Examples\nLet's look at some examples of listeners.\n\n### Issue Creation\nWhen a new issue is created, you may want to notify people or perform an action.\n#### Listener interfaces\n1. `NewIssueListener`: [NewIssueListener](src/api/listener/NewIssueListener.ts)\n\n#### Examples\nThe following example notifies any user who raises an issue with insufficient detail in the body, via a \ndirect message in Slack, and provides them with a helpful\nlink to the issue. Note that we make use of the\nperson available via the `openedBy` field:\n\n```typescript\nexport async function requestDescription(inv: NewIssueInvocation) {\n    if (!inv.issue.body || inv.issue.body.length < 10) {\n        await inv.context.messageClient.addressUsers(\n            `Please add a description for new issue ${inv.issue.number}: _${inv.issue.title}_: ${inv.id.url}/issues/${inv.issue.number}`,\n            inv.issue.openedBy.person.chatId.screenName);\n    }\n}\n```\nThis is registed with a `SoftwareDeliveryMachine` instance as follows:\n\n```typescript\nsdm.addNewIssueListeners(requestDescription)\n```\n\nUsing the `credentials` on the `NewIssueInvocation`, you can easily use the GitHub API to modify the issue, for example correcting spelling errors.\n\n### Repo Creation\nWe frequently want to respond to the creation of a new repository: For example, we may want to notify people, provision infrastructure, or tag it with GitHub topics based on its contents.\n\n#### Listener interfaces\nThere are two scenarios to consider:\n\n1. The creation of a new repository. `RepoCreationListener`: [RepoCreationListener](src/api/listener/RepoCreationListener.ts)\n2. The first push to a repository, which uses the more generic [ProjectListener](src/api/listener/PushListener.ts)\n\nThe second scenario is usually more important, as it is possible to create a repository without any source code or a master branch, which isn't enough to work with for common actions.\n\n#### Examples\nThe following example publishes a message to the `#general` channel in Slack when a new repo has been created:\n\n```typescript\nexport const PublishNewRepo: SdmListener = (i: ListenerInvocation) => {\n    return i.context.messageClient.addressChannels(\n        `A new repo was created: \\`${i.id.owner}:${i.id.repo}\\``, \"general\");\n};\n\n```\n\nTagging a repo with topics based on its content is a useful action. `tagRepo` is a convenient function to construct a `ProjectListener` for this. It tags as an argument a `Tagger`, which looks at the project content and returns a `Tags` object. The following example from `atomist.config.ts` tags Spring Boot repos, using a `Tagger` from the `spring-automation` project, in addition to suggesting the addition of a Cloud Foundry manifest, and publishing the repo using the listener previously shown:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n      tagRepo(springBootTagger),\n      suggestAddingCloudFoundryManifest,\n      PublishNewRepo)\n```\n\n##### ReviewerRegistration\n`ProjectReviewer` is a type defined in `automation-client-ts`. It allows a structured review to be returned. The review comments can localize the file path, line and column if such information is available, and also optionally include a link to a \"fix\" command to autofix the problem.\n\nThe following is a simple project reviewer spots projects without a README, using the `Project` API:\n\n```typescript\nconst hasNoReadMe: ReviewerRegistration = {\n    name: \"hasNoReadme\",\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments: !!(await cri.project.getFile(\"README.me\")) ?\n            [] :\n            [new DefaultReviewComment(\"info\", \"readme\",\n                \"Project has no README\",\n                {\n                    path: \"README.md\",\n                    lineFrom1: 1,\n                    offset: -1,\n                })],\n    }),\n};\n```\nA slightly more complex example uses the `saveFromFiles` utility method to look for and object to YAML files in Maven projects:\n\n```typescript\nconst rodHatesYaml: ReviewerRegistration = {\n    name: \"rodHatesYaml\",\n    pushTest: hasFile(\"pom.xml\"),\n    action: async cri => ({\n        repoId: cri.project.id,\n        comments:\n            await saveFromFiles(cri.project, \"**/*.yml\", f =>\n                new DefaultReviewComment(\"info\", \"yml-reviewer\",\n                    `Found YML in \\`${f.path}\\`: Rod regards the format as an insult to computer science`,\n                    {\n                        path: f.path,\n                        lineFrom1: 1,\n                        offset: -1,\n                    })),\n    }),\n};\n\n```\n\nThese reviewers can be added in an SDM definition as follows:\n\n```typescript\nsdm.addProjectReviewers(hasNoReadme, rodHatesYaml);\n```\n##### AutofixRegistration\n\nAn `AutofixRegistration` can automatically execute code fixes. An example, which adds a license to file if one isn't found:\n\n```typescript\nexport const AddLicenseFile: AutofixRegistration = editorAutofixRegistration({\n    name: \"License Fix\",\n    pushTest: not(hasFile(LicenseFilename)),\n    editor: async p => {\n        const license = await axios.get(\"https://www.apache.org/licenses/LICENSE-2.0.txt\");\n        return p.addFile(\"LICENSE\", license.data);\n    },\n});\n```\nNote the use of the `addFile` method on `Project`. Atomist takes care of committing the change to the \nbranch of the push.\n\nRegistration with an SDM is simple:\n\n```typescript\nsdm.addAutofixes(\n    AddAtomistJavaHeader,\n    AddAtomistTypeScriptHeader,\n    AddLicenseFile,\n);\n```\n\n##### CodeActionRegistration interface\nThis registration allows you to react to the code, with information about the changes in the given push:\n\nFor example, the following function lists changed files to any linked Slack channels for the repo:\n\n```typescript\nexport const listChangedFiles: PushReactionRegistration = {\n    action(i: PushImpactListenerInvocation) {\n        return i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n    },\n    name: \"List files changed\",\n};\n```\n\nIf you don't have a custom name or PushTest, you can use the following shorthand:\n\n\n```typescript\nexport const listChangedFiles = i => i.addressChannels(`Files changed:\\n${i.filesChanged.map(n => \"- `\" + n + \"`\").join(\"\\n\")}`);\n\n```\n\nAdd in an SDM definition as follows:\n\n```typescript\nsdm.addPushReactions(listChangedFiles)\n```\n> If your reaction is essentially a review--for example, it's associated with a known problem in a particular file location--use a `ReviewerRegistration` rather than a `PushReactionRegistration`.\n> \n> Important note: You must have set a `PushReactionGoal` for push reactions to be invoked\n\n#### Fingerprints\nA special kind of push listener relates to **fingerprints**.\n\nFingerprints are data computed against a push. Think of them as snapshots. Typically they reflect the state of the repository's source code after the push; they can also take into account other characteristics of the commit. Fingerprinting is valuable because:\n\n1. *It enables us to assess the impact of a particular commit, through providing a semantic diff*. For example, did the commit change dependencies? Did it change some particularly sensitive files that necessitate closer than usual review?\n2. *It enables us to understand the evolution of a code base over time.* Atomist persists fingerprints, so we can trace over time anything we fingerprint, and report against it. For example, what is happening to code quality metrics over time?\n\nAtomist ships some out of the box fingerprints, such as Maven and `npm` dependency fingerprints. But it's easy to write your own. Fingerprint registrations are like other listener registrations, specifying a name and `PushTest`. The following example is the complete code for fingerprinting dependencies specified in a `package-lock.json` file:\n\n```typescript\nexport class PackageLockFingerprinter implements FingerprinterRegistration {\n\n    public readonly name = \"PackageLockFingerprinter\";\n\n    public readonly pushTest: PushTest = IsNode;\n\n    public async action(cri: PushImpactListenerInvocation): Promise<FingerprinterResult> {\n        const lockFile = await cri.project.getFile(\"package-lock.json\");\n        if (!lockFile) {\n            return [];\n        }\n        try {\n            const content = await lockFile.getContent();\n            const json = JSON.parse(content);\n            const deps = json.dependencies;\n            const dstr = JSON.stringify(deps);\n            return {\n                name: \"dependencies\",\n                abbreviation: \"deps\",\n                version: \"0.1\",\n                sha: computeShaOf(dstr),\n                data: json,\n            };\n        } catch (err) {\n            logger.warn(\"Unable to compute package-lock.json fingerprint: %s\", err.message);\n            return [];\n        }\n    }\n}\n```\n\nFingerprinters can be added to an SDM as follows:\n\n```typescript\nsdm.addFingerprinterRegistrations(new PackageLockFingerprinter());\n```\n\nFingerprinting will only occur if a `FingerprintGoal` is selected when goals are set.\n\n## Generators\nAnother important concern is project creation. Consistent project creation is important to governance and provides a way of sharing knowledge across a team.\n\nAtomist's [unique take on project generation](https://the-composition.com/no-more-copy-paste-bf6c7f96e445) starts from a **seed project**--a kind of golden master, that is version controlled using your regular repository hosting solution. A seed project doesn't need to include template content: It's a regular project in whatever stack, and Atomist transforms it to be a unique, custom project based on the parameters supplied at the time of project creation. This allows freedom to evolve the seed project with regular development tools.\n\nGenerators can be registered with an SDM as follows:\n\n```typescript\nsdm.addGenerators(() => springBootGenerator({\n    ...CommonJavaGeneratorConfig,\n    seedRepo: \"spring-rest-seed\",\n    intent: \"create spring\",\n}))\n```\n\nThe `springBootGenerator` function used here is provided in `sample-sdm`, but it's easy enough to write your own transformation using the `Project` API. Here's most of the code in our real Node generator:\n\n```typescript\nexport function nodeGenerator(config: GeneratorConfig,\n                              details: Partial<GeneratorCommandDetails<NodeProjectCreationParameters>> = {}): HandleCommand {\n    return generatorHandler<NodeProjectCreationParameters>(\n        transformSeed,\n        () => new NodeProjectCreationParameters(config),\n        `nodeGenerator-${config.seedRepo}`,\n        {\n            tags: [\"node\", \"typescript\", \"generator\"],\n            ...details,\n            intent: config.intent,\n        });\n}\n\nfunction transformSeed(params: NodeProjectCreationParameters, ctx: HandlerContext) {\n    return chainEditors(\n        updatePackageJsonIdentification(params.appName, params.target.description,\n            params.version,\n            params.screenName,\n            params.target),\n        updateReadmeTitle(params.appName, params.target.description),\n    );\n}\n```\n\nYou can invoke such a generator from Slack, like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/create_sample1.png?raw=true\"/>\n\nNote how the repo was automatically tagged with GitHub topics after creation. This was the work of a listener, specified as follows:\n\n```typescript\nsdm.addNewRepoWithCodeActions(\n    tagRepo(springBootTagger),\n);\n```\n\nWith Atomist ChatOps supports, you can follow along in a linked channel like this:\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/sample1_channel.png?raw=true\"/>\n\nNote the suggestion to add a Cloud Foundry manifest. This is the work of another listener, which reacts to finding new code in a repo. Listeners and commands such as generators work hand in hand for Atomist.\n\n## Editors\nAnother core concept is a project **editor**. An editor is a command that transforms project content. Atomist infrastructure can help persist such transformations through branch commits or pull requests, with clean diffs.\n\n### A Simple Editor\nAs you'd expect, editors also use th `Project` API.\n\nHere's an example of a simple editor that takes as a parameter the path of a file to remove from a repository. \n\n```typescript\n@Parameters()\nexport class RemoveFileParams {\n\n    @Parameter()\n    public path: string;\n}\n\nexport const removeFileEditor: HandleCommand = editorCommand<RemoveFileParams>(\n    () => removeFile,\n    \"remove file\",\n    RemoveFileParams,\n    {\n        editMode: params => commitToMaster(`You asked me to remove file ${params.path}!`),\n    });\n\nasync function removeFile(p: Project, ctx: HandlerContext, params: RemoveFileParams) {\n    return p.deleteFile(params.path);\n}\n```\n\nEditors can be registered with an SDM as follows:\n\n```typescript\nsdm.addEditors(\n    () => removeFileEditor,\n);\n```\n\n### Dry Run Editors\nMore elaborate editors use helper APIs on top of the `Project` API such as Atomist's [microgrammar](https://github.com/atomist/microgrammar) API and [ANTLR](https://github.com/atomist/antlr-ts) integration.\n\nThere's also an important capability called \"dry run editing\": Performing an edit on a branch, and then either raising either a PR or an issue, depending on build success or failure. This allows us to safely apply edits across many repositories. There's a simple wrapper function to enable this:\n\n```typescript\nexport const tryToUpgradeSpringBootVersion: HandleCommand = dryRunEditor<UpgradeSpringBootParameters>(\n    params => setSpringBootVersionEditor(params.desiredBootVersion),\n    UpgradeSpringBootParameters,\n    \"boot-upgrade\", {\n        description: `Upgrade Spring Boot version`,\n        intent: \"try to upgrade Spring Boot\",\n    },\n);\n```\nThis editor will upgrade the Spring Boot version in one or more projects, then wait to see if the builds succeed. Output will look like this (in the case of success):\n\n<img src=\"https://github.com/atomist/github-sdm/blob/master/docs/dry_run_upgrade.png?raw=true\"/>\n\n> Dry run editing is another example of how commands and events can work hand in hand with Atomist to provide a uniquely powerful solution.\n\n\n## Arbitrary Commands\nBoth generators and editors are special cases of Atomist **command handlers**, which can be invoked via Slack or HTTP. You can write commands to ensure that anything that needs to be repeated gets done the right way each time, and that the solution isn't hidden on someone's machine.\n\n## Pulling it All Together: The `SoftwareDeliveryMachine` class\n\nYour ideal delivery blueprint spans delivery flow, generators, editors and other commands. All we need is something to pull it together.\n\nYour event listeners need to be invoked by Atomist handlers. The `SoftwareDeliveryMachine` takes care of this, ensuring that the correct handlers are emitted for use in `atomist.config.ts`, without you needing to worry about the event handler registrations on underlying GraphQL.\n\nThe `SoftwareDeliveryMachine` class offers a fluent builder approach to adding command handlers, generators and editors.\n\n### Example\nFor example:\n\n```typescript\n    const sdm = createSoftwareDeliveryMachine(\n        {\n            builder: K8sBuildOnSuccessStatus,\n            deployers: [\n                K8sStagingDeployOnSuccessStatus,\n                K8sProductionDeployOnSuccessStatus,\n            ],\n            artifactStore,\n        },\n        whenPushSatisfies(PushToDefaultBranch, IsMaven, IsSpringBoot, HasK8Spec, PushToPublicRepo)\n            .setGoals(HttpServiceGoals),\n        whenPushSatisfies(not(PushFromAtomist), IsMaven, IsSpringBoot)\n            .setGoals(LocalDeploymentGoals),\n        whenPushSatisfies(IsMaven, MaterialChangeToJavaRepo)\n            .setGoals(LibraryGoals),\n        whenPushSatisfies(IsNode).setGoals(NpmGoals),\n    );\n    sdm.addNewRepoWithCodeActions(suggestAddingK8sSpec)\n        .addSupportingCommands(() => addK8sSpec)\n        .addSupportingEvents(() => NoticeK8sTestDeployCompletion,\n            () => NoticeK8sProdDeployCompletion)\n        .addEndpointVerificationListeners(\n            lookFor200OnEndpointRootGet({\n                retries: 15,\n                maxTimeout: 5000,\n                minTimeout: 3000,\n            }),\n        );\n    sdm.addNewIssueListeners(requestDescription)\n        .addEditors(() => tryToUpgradeSpringBootVersion)\n        .addGenerators(() => springBootGenerator({\n            seedOwner: \"spring-team\",\n            seedRepo: \"spring-rest-seed\",\n            groupId: \"myco\",\n        }))\n        .addNewRepoWithCodeActions(\n            tagRepo(springBootTagger),\n            suggestAddingCloudFoundryManifest,\n            PublishNewRepo)\n        .addProjectReviewers(logReview)\n        .addPushReactions(listChangedFiles)\n        .addFingerprinters(mavenFingerprinter)\n        .addDeploymentListeners(PostToDeploymentsChannel)\n        .addEndpointVerificationListeners(LookFor200OnEndpointRootGet)\n        .addVerifiedDeploymentListeners(presentPromotionButton)\n        .addSupersededListeners(\n            inv => {\n                logger.info(\"Will undeploy application %j\", inv.id);\n                return LocalMavenDeployer.deployer.undeploy(inv.id);\n            })\n        .addSupportingCommands(\n            () => addCloudFoundryManifest,\n            DescribeStagingAndProd,\n            () => disposeProjectHandler,\n        )\n        .addSupportingEvents(OnDryRunBuildComplete);\n```\nThe `SoftwareDeliveryMachine` instance will create the necessary Atomist event handlers to export.\n\nIn `atomist.config.ts` you can bring them in simply as follows:\n\n```typescript\ncommands: assembled.commandHandlers,\nevents: assembled.eventHandlers,\n```\n\n## Structure of This Project\n\n- `src/api` is the public user-facing API, including the software delivery machine concept that ties everything together. *This may be extracted into its own Node module in future.*\n- `src/spi` contains interfaces to be extended in integrations with infrastructure,\nsuch as artifact storage, logging, build and deployment.\n- `src/graphql` contains GraphQL queries. You can add fields to existing queries and subscriptions, and add your own.\n- `src/typings` is where types generated from GraphQL wind up. Refresh these with `npm run gql:gen` \nif you update any GraphQL files in `src/graphql`.\n- `src/util` contains miscellaneous utilities.\n- `src/internal` contains lower level code such as event handlers necessary to support the user API. This is not intended for user use.\n- `src/pack` contains \"extension packs.\" These will ultimately be extracted into their own Node modules.\n- The other directories unders `src` contain useful functionality that may eventually be moved out of this project.\n\nThe types from `src/api` can be imported into downstream projects from the `index.ts` barrel.\n\n## Plugging in Third Party Tools\n\nThis repo shows the use of Atomist to perform many steps itself. However, each of the goals used by Atomist here is pluggable.\n\nIt's also easy to integrate third party tools like Checkstyle.\n\n### Integrating CI tools\nOne of the tools you are most likely to integrate is CI. For example, you can integrate Jenkins, Travis or Circle CI with Atomist so that these tools are responsible for build. This has potential advantages in terms of scheduling and repeatability of environments.\n\nIntegrating a CI tool with Atomist is simple. Simply invoke Atomist hooks to send events around build and artifact creation.\n\nIf integrating CI tools, we recommend the following:\n\n- CI tools are great for building and generating artifacts. They are often abused as a PaaS for `bash`. If you find your CI usage has you programming in `bash` or YML, consider whether invoking such operations from Atomist event handlers might be a better model.\n- Use Atomist generators to create your CI files, and Atomist editors to keep them in synch, minimizing inconsistency.\n\n### Integrating APM tools\ntbd\n\n### Integrating with Static Analysis Tools\nAny tool that runs on code, such as Checkstyle, can easily be integrated.\n\nIf the tool doesn't have a Node API (which Checkstyle doesn't as it's written in Java), you can invoke it via Node `spawn`, as Node excels at working with child processes.\n\n## Advanced Push Rules\n\n### Computed Values\nYou can use computed `boolean` values or the results of synchronous or asynchronous functions returning `boolean` in the DSL, making it possible to bring in any state you wish. For example:\n\n```typescript\nwhenPushSatisfies(IsMaven, HasSpringBootApplicationClass, \n\tdeploymentsToday < 25)\n    .itMeans(\"Not tired of deploying Spring apps yet\")\n    .setGoals(LocalDeploymentGoals),\n```\n\n### Decision Trees\nYou can write decision trees in push rules or other push mappings. These can be nested to arbitrary depth, and can use computed state. For example:\n\n```typescript\nlet count = 0;\nconst pm: PushMapping<Goals> = given<Goals>(IsNode)\n\t// Compute a value we'll use later\n    .init(() => count = 0)\t\n    .itMeans(\"node\")\n    .then(\n        given<Goals>(IsExpress).itMeans(\"express\")\n            .compute(() => count++)\n            // Go into tree branch rule set\n            .then(\n                whenPushSatisfies(count > 0).itMeans(\"nope\").setGoals(NoGoals),\n\t\t\t\twhenPushSatisfies(TruePushTest).itMeans(\"yes\").setGoals(HttpServiceGoals),\n            ),\n    );\n```\n\n### \"Contribution\" Style\ntbd\n\n## Roadmap\n\nThis project is under active development, and still in flux. Some goals:\n\n- Splitting out `sdm-api` project with the contents of the `src/api` directory\n- Extracting the extension packs under `src/pack` into their own Node modules.\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-20180826124729","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-22M1W/oVBAATipk6K2xOQ8sFkPAETvIaAiTjFeSjGQA59nbFKbAPJK1q4344bIBzVauR+2w2TBqKH8KoKrNWyA==","shasum":"c3c8f41f6a4e5a9b3c6019cf54f6ef01b16d9c07","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-20180826124729.tgz","fileCount":846,"unpackedSize":1844136,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbgqILCRA9TVsSAnZWagAA+F8P/RCuadcPeTXGpgkTQGzS\nGRxQrNkWQNAuOG4TT/1zGAtgLzxiGAlxADxDg6YYV3Osnbyvz2Q2LvHLdUL9\nHPLwX0MDdOYBhZhw4L4XL+oIFXJo8hRG1UT+NCEyTJVMwFADhucDvozuSa8l\nNgE88oj4NF0GGhVUKeN+bYjYqpnGNH7H89ribg4fQKXw/UVLMBCVXW3SmNfv\nQns6j2mp4UQQhDi3SbYFR4I2deoNTTfhuAMTaelscmPekebKZwX7dxLBuF9K\nhObQOTpwb7RbR5PZZy532z1n536efmYChJL4G5V7qBS3SAa8ZGTwC8mRd3xS\n3jJ1GPMIMrGr7ZZ/1mOfoFs6jbtz8JgYfqGgTaRpvOEQpyOW4OGGHDZpir1y\n8fIfR5qtvhA6g666URr6gHB+fecC4LrM7e6NE0OieY/RKUdGtxh6uGaDdEh7\nRMsDHcTpZxYzWVNlKZogG4w71t6BbrfZ9S0YH4VA/578ICH2XzjdphTlvuOn\niO41ss41INQkKThbS9YkBg1U9iY55WU2AtzL7w5YqP6xhRXIIfM2zN6hh8JU\nyIGbCxey5oWxWE+GnMkpPEZLMSTyVueSX/UiEj0bVNgOvIb4t8pgu7Uz53aN\nXwubZT+/VI6Ohmqgo9lKZCTrNRM3zfzNlypsTIK5JVrKBgaatWyZXqWde0Xh\npliG\r\n=p9kF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmR2dPliExHKPgrb210wIyQDNZ3juZhUneHSsN4AYQ8gIhAM7nUSj3bSLNBcEGcKtPTpKxbhM1dIgkwwH+hMGyinbE"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-20180826124729_1535287818905_0.9298777321937248"},"_hasShrinkwrap":false},"0.4.8-20180826233743":{"name":"@atomist/sdm","version":"0.4.8-20180826233743","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7f830ba14c4cae7a531fc965ad1fdbfb9046675b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.8-20180826233743","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bMYBMBGB4rWSLdiy2jmgu2LkBEGszAiV6Ip5QVbftdlUbU4ZZRdD7xj1JHDvVspXYkzK0rP+1dGQn0u+jnuc6w==","shasum":"63ff941a9bc60617278f4659bdbbb191f674632f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8-20180826233743.tgz","fileCount":846,"unpackedSize":1809543,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbgzptCRA9TVsSAnZWagAAR4MP/2d0ZnSi30ONwgIsYbEl\nRcAMJ24hHDXDkEgy22lFe14BA3fM7REvzncr7Sa0hwCAgUwJRKotGKJR3P8x\nVYYOk1kkOqc4yUiWTjNBf6d8PQnZCP0/gHCD1YxkN2pvA7GfRePgigbTUufW\nog0vCr3my2b2OpL+oEY0y/0TSIkujFb3Zom5JDKv9Hxc6Sn0Me6zWsygxW92\nJLnsTwQSi2GTXav9f/xGd7ugq/u4CeFaEAF0ALQwFVMx6ODSLZeFNNxUqXIU\nFb/+yJp1RkzhF9yrsfNycrnFwldZTGaMAj0AjkynY6YVSPida5JXdcMjPgKf\n+o6VZL+ccRaeY5cpR9G4D+rV7W7RhrY3BOLu8V+2eWNhnOLiT5sJd/a9wgvL\n6AXwdPwa56QyMO0mZztRmff6Z990ieRt1BVqdSpWqP9rjloCVYSrr7RkOoAN\njpI/v2iXhy1v48UIyKUm3hnRPAb5N17OcPCuGNVzXJR6lbPuW7gKQearMrn1\nyjviYZJfWvTAUTFg5qPV9jVhnZuShkrJgd2zYlY9+2qcw7Hr5KTuuirS7VNJ\n0/LXuK7syAqbgZkctYCaDcB95zlsIW+pWVcaWZ+NPIIV2rUR2W+1/BesXCQM\nIK4c7ncEQUXiLiwiCNysj4cZr7QqTflzwdhkfe4iQeHpNRfVF+I6jKd1oEwy\nTmSs\r\n=2JCT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwL7SOwhM7TdZ59k4sRKhoO/UmHmr1G5i/1I0iN1On6AIgXD7s/caheYwM3bRKlDTbXzVRkInX/QWbu3MoM3eHJXQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8-20180826233743_1535326828816_0.7814529087020516"},"_hasShrinkwrap":false},"0.4.8":{"name":"@atomist/sdm","version":"0.4.8","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7f830ba14c4cae7a531fc965ad1fdbfb9046675b","_id":"@atomist/sdm@0.4.8","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/nsFgQgk9HYlgxSFSmS4lnNceLJp+Y6pUP6YP8z+WTcBwwoju3AUpnj/wYk6b4t/TSeuoQI14h3ZJk5aFX0N1w==","shasum":"f6962ff258430fac639a7ff85ad42ec29c892cb0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.8.tgz","fileCount":846,"unpackedSize":1809528,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg3q5CRA9TVsSAnZWagAAn64QAKIB+vrvQjFWNMSOmRMf\nP1Xrc29gidbWOQkDvoEpZFxUxeY6W/6GjJrO+eupldPl2thLLNgDFi4jlYGu\nzqg/3aJ/FVlnG07DP5a4J7/WiSdKd+bgufri5dCnCKLm+3bZGcdqx6AHG75k\n4NIbm76wTnM/Um1cMULJPQsnniN3tuXJyQmEHvW8Thn2hkFMjCtfi7X1TRae\n6mU6Sf5CVOOH2Ard0e3lRRnnh6Aeiw05gnYFhwvgbGAD1pvQ6VJVQ3u/PRA7\nHwLTkR9z/qZVJ42fh0X9is0vXTQPJ0wFJWKgAw7BznFkbhfRon8iC09KRpwY\n8tUFAiyw4QMGoW0f2IYNnsy71j2M0l5o1BSclQN+HargBJJXhTyGLbOgs/RH\nwz+4sju1vmts/RQn4+uqh7dVWU4fNfEav0FhhCtNtOyD+Jx3ofDb6lo7RxPy\nCNoiaNQDHNEebVgxvht+3w7Tc0FBABWps0BhWumyvO1xgpLQwkET/jUPTwc1\nyTWHDBkEIOUlTbYMl++CEMfLpq6gfHv7dx80e8yv62RUQqFfv96lrTR/yZvO\nnxkpzspcC+MBQD2tZVEh2zrfA71JEgit6GB5/KmGOfgl/LbxWf4U7OnRu04T\n5Mx7WL4whs9BsLD6j9BpCN5Eu8o5e3RAn/tyFrPATNA+pvuGshmnfqDmEaAi\nLO2Q\r\n=5z50\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICcjO6wDbql/7pJxnYbM4TRiipqLfvA4kKP4ZRxzIrVNAiEAsrlT4Qk0oSiHJPJUJQPsGWJlZeRug8UARmlsu3SwrPI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.8_1535343288927_0.687203267124898"},"_hasShrinkwrap":false},"0.4.9-20180827041548":{"name":"@atomist/sdm","version":"0.4.9-20180827041548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f544aa436c95b7de16879db039ee6330d04cf8a4","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@0.4.9-20180827041548","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-h0BaWxN30w8ZZ+rSVxrOvyieyIuvxBCz3lV42luHpxDguXMtxtnuzrBWsqfMwDxP7ivhzhjne30KS1e11dbudg==","shasum":"fd7c10ea95ccebe4d32add8b7dd9660416749076","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-0.4.9-20180827041548.tgz","fileCount":846,"unpackedSize":1809622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg3uuCRA9TVsSAnZWagAAqwwP/iGN9HJEiJoWnbU4Z8qD\n2iNe5JOrsRk/hfFdsqqamJPZMpzZYg3/hJA3u+djAP9AXNvUcPv1EykXjjbL\njUJOQwWyxWNOWt2HdlAY/gBT4zWkd7T3hWM8C0QzhO9LMGQVF6eTivYSXbcz\naRWdssrXpYI3O4aUGbwQDoCtI0wezORqw8UFfPBd8DLeQfauG0QU8BBKx5bx\nJBy+BKC4zORmuM06tHxxsmFqV/ZZKcLG33eQyrDn7p2rKRbbbenJJAIhL4qX\noJnKHLp9D+7wkpofIktj+s6g/XoX58ZaRRJZ8rJC8u0xljrbp+Ij7K7bhFah\nqK7keqYGf0b0hyTO1nxQ2ILmiUhXymMHj0xt1YkQq5Pwm6xGU1F7A+jQujD2\nVhYAnRthGcFCMYcdK7UaUJC0ZEU67eOLzabQSy2kmrcIsJgyD+v8Tul4sqJn\njvhM2SxolQ/8AJbxbYjzhDtvAdgvGFO/avwIuNyF2wUufcjysYEYeXIeNWRe\n0iUIrcag2RJ5eIeGHG6KXpGa9Na0MoqTdAnMa0z86OlVaZb0saZasw+NniLM\nZtOQa6770igGMd0kIKlh6ZYKmCXrouqvBATdFWTIOVBXZeELG37ysOYMqq/s\nQkon0W5zlCl1lJ6pXtUlh2WSgftl8ftIuwZ6rWyIHByDQpMV9sDYH3hD8yTp\njOIB\r\n=rPaA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5brYjZkj9PoECnU9GkCLDnbFnC+BBnbsOJWK+60Z+zAIhAIuEPKy+X07V5jleEHfuHwTVISxzBQzxZTVz2UVfDMcb"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_0.4.9-20180827041548_1535343533843_0.5639946876457809"},"_hasShrinkwrap":false},"1.0.0-20180827101919":{"name":"@atomist/sdm","version":"1.0.0-20180827101919","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^0.21.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9da612b132ba1622740ae0cf87689369c8b61c1c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-20180827101919","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lKnV2ax4bcNpiARy/Olg53RiatcQnk2CcxufzQox1nOUCJ7CyDC9v5f37xldZUbvXMKSGr+jHp5WJYRGtDd1OA==","shasum":"ae9db996600da49907d4d4da4557c54d42398ad6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-20180827101919.tgz","fileCount":846,"unpackedSize":1809622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg9DcCRA9TVsSAnZWagAAb08QAIOj1+tHHIxrYEAy7mT1\nYwhD2MnXlnx4dkdlVr4hAvExQcrxuus6QyJWZUaqyf4uz5/+4hcQOvDiBLpQ\nSdMtSYgfJVOTFwTqMo6dDnX1K7WyBwQqr6g6hj93sAN/2Syv3DP+aOfI9Xn1\nmgRJs3tsl+JbjUmJb0oOzIUCEiCH5qpqg2t3T4mAtNKRikH0InfKL9s1DuEl\n71gsoevpcrBu1/e2U6Q+9wMNWrRdZRXA3FIr15AG1uaKE+fdmo6PeSH/fj/L\nOmvaULbHmzezdsdKxJpJpicKjpEZOQ6TPQDekv2V/ffaxuDHugHUlx4laBqZ\nMaO+B1UMwYGq+2fYl4mW3cHP2WITlHSgZjrK+HClQb+5TwSiPynR5oCpBpAd\nQLkWZuK13O8wVsgGVSHwEg3r3kWx5rSgXG5itNpmwW/sUpgKBCuAbwcE418M\nDfTKbdoPe0rxjxANUovhZyqxpSnyy7Vjkf+4BBfBKaTvspPWzmyKqKCf7Kdr\nNZO1yGiPBxfEEbdFyz3xFvLS6+LwCUpWhtz1rWtBEkay3QvD6AC7wMgsaLTk\nvYNzmjS2l7qFH/dIqawdWamkIO7457exQ4e4xXjjBLIzQi/mIHdJMELyVXQQ\n+5VnDAASdeI1bd1BtP1Kb0hFZSJB+Rx9mtKDNaAdcjlM3VvlnMFAyMhcQuw+\nmkpB\r\n=Naa3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDc7P1vafF6CT5KeYyBHtUWhAWP9cyFw0RLpfdblHMgGQIhAJrpZSsvekb51hkopBjDJhC3pStUbbivlC87JNsjQy32"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-20180827101919_1535365339866_0.714225807196357"},"_hasShrinkwrap":false},"1.0.0-atomist-update-next-1535365519905.20180827102529":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-next-1535365519905.20180827102529","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a4b6d9af71cce0cf6f0f4e157c49a4a91904d418","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-atomist-update-next-1535365519905.20180827102529","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3uN7CEkXI/jdsR5TyEKlnBuwOLMMtfJOEFCGbYdmv9S8BkJWtk74QMM3S0sML1ij5zpWNkpbg/rRxFEsW51jQw==","shasum":"06caa9180db3b772b7094f7d3f07cd522863b065","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-next-1535365519905.20180827102529.tgz","fileCount":846,"unpackedSize":1809658,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg9IpCRA9TVsSAnZWagAA9PgP/R9J7dzAA5qfJigME2r8\n9CLOF0qEsSEs/8DtFJFnXGcuzy7pX/+aQJe2BUjHB/BXst4++jSTJdK4z6jw\nww8LUP8gn6t66z/DPkcXLK4znDQLE7YIgRm+6qb3fAb5WcojzZegiZNX25wz\nkFcLwGPxf9bQCq5oZ2eT8ydz8TImVgW8RZACso3azCFp6Bn2jxQkdXd+iZFQ\nKmUF/7/ZZyWKbcB7xsL0N5voICQFUPRSFDJzy/bgOqd88Ktbzx0aKdZtXskf\nyDeO26pR+CCvIj45ZvhSiy5uAd8qhoBDZ4eRa7SCXHm4HZI1X+UUvz57vnd9\nPmO6aeRjUmm+mghGU0Isvy+jo9rPuIAVB0SUVD8SoDuvXwMRswJx6f+hhrT8\nnJqke46STwXT/DCdIuXp4vCm10F8fn+uCnVmGpX8G0MgbZAqrmDXEn5LF8vT\ns+iWQpDxsFAHVFpuWkLGuXww9Te9HgfqtgPtr3ac+YjlygfhuYXxVUUY1tTK\nc8FoeoGtILxYxoXh5WnQt857mBp91pRDufkcN+k4TjITSv6Du4oRn/ml5dxh\n0HmmMxe+KWAnOhgkbRsUqkObtWZrtvziZhPV54JzbuslSncUJ/3xUGHDA+OU\n2lTlGyuCNbbzcVAAAEJl3QHJ/UfSYs/e6vBDPacvoT6XlwOBvwbQVpxl/Zxj\nf2ky\r\n=itXA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC12F+2oCZJgOOEp3WA7Vu8itke1/H4us1qDN03Wq2ZmAiEA9DGHbqgjVPXKNjPBMpgSEICu2hXQ3OSGeuntMgchDkQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-next-1535365519905.20180827102529_1535365672898_0.42075230474489445"},"_hasShrinkwrap":false},"1.0.0-20180827102904":{"name":"@atomist/sdm","version":"1.0.0-20180827102904","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cc4896efdd583cde1b4cd384f5d56226dba3069f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-20180827102904","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KnhiWt1HFu5v37KoSb+61xT+836KPCSnzHEpmfWqAK0PzrKIcuBIrS0YsKVckHu485LRiWFNMbRmE9lttUPhig==","shasum":"032e70520671da7402d1b01a2b1e16b6167b358c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-20180827102904.tgz","fileCount":846,"unpackedSize":1809767,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg9MNCRA9TVsSAnZWagAAaOAP/AoeXc7JFQd0RMHH3kdy\nJd0GuxBkGwG1W8WUntDVF7dtvAyATxkR6CDuvAGSdbrPIq/At6joNy22PQ+/\nwqpRKPIYFZ7L+Qd5QpIJuty7W6Vq9VNiruOTRbsse64uzG0d5Ktv7jPqKndc\nh0b++eIIWdxO8HwngEVUzTbOdWcDQ+9vxcOKmHv4m/ZVQggEkTdLBGwVsewa\nivt8GGryQXPb01GTz3c4PgnGTKXAo2yfPE1elV34btpmWMUnAAmb3n1rPMZ0\nmgfS1rS3PwNzgtUC6upk6d3UfmlJ00rkhN/sh2ECFGpqzXLNiWGKZSL37/0s\nyz9grY/mz75DO+bcvHvRQXx9Y+aG/aJ4EShNeoypSAlAx15FZzrVZo67r2KA\n4cai0rYYSL1IB71lJviWuXEAXqWUEtiBzC4t/V5F7//jye4a2LTNF385axuI\nUZHEZORRFHsiICcwbFjTqFf/2TxW1eLI+oFeG/LjOzcWrrH+yLhC+sb71tnL\nd45F1t1HlJUsM+F0hx8r1VTsxH6eathjNe3QhyYK7rt01Vb1zr4+yYaVRWAj\n+2epjF3aW5UVKlNoPmMhjCa67no/5tmAgLCdEPTX5FfBLb6/VQjoPPTYZYXD\nbo7pT9KAg/zSjZqGOmuxT7Fsue6slUZ4l63PJcUeijsRjSvq85bFWtqyxXAZ\nOzi8\r\n=MWXP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEoCfO/1rpZ+lGSGQGXfW0Hqz1tY2vDbNy8hIDAgMyfMAiBfQN4bC7pQ0xQ3/hmLe6UOzV3iSdwGyKCTqcds9U10iw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-20180827102904_1535365900320_0.5533171990307328"},"_hasShrinkwrap":false},"1.0.0-M.1":{"name":"@atomist/sdm","version":"1.0.0-M.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cc4896efdd583cde1b4cd384f5d56226dba3069f","_id":"@atomist/sdm@1.0.0-M.1","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mdixmFEdxQXWnA6DJyVBTzMy9DA+RImkCtJNJeG85KKxI8aR7wYAE9WqnnW+4A7uYbqpcWLa9XrjTPTK3tOd9Q==","shasum":"8264ebd97675c0d9f7fe12a6f875a5694202ed79","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-M.1.tgz","fileCount":846,"unpackedSize":1809756,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbg9RuCRA9TVsSAnZWagAAuCUP/iVHA/Ld8cLTmBnxKa6h\nnOIygrF5lqjRcqe/JueYDVPSQuuBu4lHFBqWZ5JIyC+nVn8Li58iRcqXtdGK\nTZUXR3a0v6Lo949rwWSNlDBUkye1yaiPGCUiBf/gmJqc/VcTjmgrtrzjvJNU\nEVv/4qAfE2iNAcfiQlXLsLKTyK5Yl5j0i/cNBnNC0g5l5GFXdngTGKxb5Tvr\nR1U1fb/cfAhU4SvFYSTC3OQDNXIa8MOPE1XNrsB6S9J932iY9+IAuUjx0jBy\nCTjB4Pmxe08gUoeGU90pWFFJW1xJV909wOSabun0OK9UcBe6aodfQGOnNNFz\nVsn1ZffdTxzr+F5LbqJnnAE/4gHhzzpHk0mnhvcGjxF5JYU1VJUi0Eqhnv2d\nsfi4clG3GS95LPTeS0jqzMKdKIrTcvlL6KsSnbihWx/rL/B42QFBdNS5qNBd\ns1DH7OQm2NK0EtPQJMbvtlyHyUc7wHuRWhJIwQdZ4At8oH8kdEmkcY6cny4M\npR1S5PrumKbpyfQwNs8wpSk444cQWscZUn1xEcd7NaptQruf529rjywFYKW4\n+CMwhdSIlinBJUuMUBJzPqitJfS6kopOYLjsjaoTnHQbcPdesrQVLgTsb1nM\nmd4z+s41nQUv4d9PaBhstbGo6TBavE7TCxVWlLDe8cNRNLpkrfqqdDx5EiD2\nJ1S6\r\n=qLyy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICAAQhMY2D6yIqkVobK7/WYONV84rZjHBGxX9TyqsARuAiApv8EjcNr8kESTZMkbxpLHyPPmaJtd23pcv6keVhtcAw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-M.1_1535366253483_0.3833271729704626"},"_hasShrinkwrap":false},"1.0.0-invariant.20180828232825":{"name":"@atomist/sdm","version":"1.0.0-invariant.20180828232825","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9fb8cab7734f9594cc5b8a39bd53c259c002b39e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-invariant.20180828232825","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dJDzhbi0u34NDxKrBTeYu07nDV1DVASseP268Dm2WGtjgLAoTBvwlsGbVUnj/XfoY1TgEmAhqAXLABDCjrWARg==","shasum":"62abecb1c07664efe29da8c5b961e444aefe6b3d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-invariant.20180828232825.tgz","fileCount":850,"unpackedSize":1817734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbhds1CRA9TVsSAnZWagAA7SEP/0rfbOVbkVz42PUwzLKf\nvIyZbjz/T0GEilDJXRFGVqZcOrUuDI1UrANe73OqF2GvqanPkwW3+xjsvxRg\n8hGlJTK5QEybCiHQbC/rdlf3dJTSE01GP9JN4ANNExghAeikIW6ru4jqAUNQ\noO2LHMALrJi8P0EbVMvihSVvWcYptqai4UiydrrGX4Y8iwl3oOt/vURgmbYJ\nKctEtxnrHxme3dB0TemwQWAH7tzgiD2cqLomGD4sIZB0/s0AVJtfnysg3ARy\nGLNXu7fs/4Hdw5ZKeREXyUob8XKK8MaUGRlQUYuB4pEjIkkBQHPiqUvgzzMt\nwPdw+aaO5QSmvTUxpUJrG/iinwkOresmx4+KEsHuH9Sbhn1kmnx8va3FIBA1\nVkoGByMvw5dIHmGXqi6jzPZfMfraaRz1bD8EzpLJMou7eoyheD4NfNBL+D15\nn0pSs/UIpAHj0Ct6RXnUbdTf0/hQl/GsvwxnM8AIT/kwqmJSCvMKOArDBLmF\nsemd6+NHnJJsQ/PdAS4BqoJhsHkUh8knDeXBpmizZi9wg2fZllJhMVqzhPCd\nznuPEgoNIrUYMF/y2TzQV1TG6Z8z20lUQZ5WYyyYWeqR5poUpEYMMRlpP/YN\n4QTcw7JQSfQ0cxCyFmvuxRXaS7A+lXbtyVasNrkfzfL7OuiPsGvY49/bnQrt\nknPk\r\n=fQS9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE5bVxqdcZ53mnNMm+q9WKF5fsbx43n2rZqpThSHu9KrAiEAu4y6xlN9HY000THlK5wIUaKyeOCHWZYP2kyEBTckHaA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-invariant.20180828232825_1535499060584_0.08283638371984936"},"_hasShrinkwrap":false},"1.0.0-invariant.20180828235510":{"name":"@atomist/sdm","version":"1.0.0-invariant.20180828235510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2c900c510f26eb8df238c6b14bd1a2b23e01b045","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-invariant.20180828235510","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TwrizeBI/YvXpVGchlPRRFAiiJjAdNr7FojuhvyDTX3RDlu8uDrqkDHDB3P1NquoIP6ryVj6A8FThZMNZEcVug==","shasum":"8f6e7aa1979983ac5d98604ec39a84fcdee43260","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-invariant.20180828235510.tgz","fileCount":850,"unpackedSize":1818415,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbheF6CRA9TVsSAnZWagAAxAYP/2IZDkfzPDWdV0CG+aT/\ndpe9rqSPbDy3pivhsjSwB9c4Xwkf5z7Qth45Jk2xAXi+NN7gOOexoq/WkoHF\nDz/AYU5bvAACQG520uQJU/PIGvN1WRoe2jIdkyT/tUPdC94PDs3tqBJHu8OQ\nid5nTNbOBCDpstyX3r5t6hbmeQuBWT/0Rzxx26l4FmwW4sU+snklVSkgXRhZ\n9/w7BhT8hDDexyMDrUsPEn+2Fo9t32bRhXo5z9Ka3lI6/pvIGZuUJ6uBTEVm\noec68wcxd/siO3w/Nq8br+bEFXDGgnKTBRUI3GqONFOswM+MA8ifFY3U9LRa\nMc83cePevgeyUb5JFguUXKEDNDA4ktQmeYILG5wedGXMPBaeZHV8HoHFnrA8\nNkUtW1nU68fllmI3Ib45G7pqu67UerZjGRFAk6tnbGOkKPN1HkbZ03ia5C4y\nEwwP2MXXYfFf3O+Qw3BFNomSf/Ui31C3X2fccOv9k1Oc/ooMEVZ6wHJ6zVQa\nxKkJ/Q7bkGADfja1Lf/efZ0tIRDgtpCvXoa8aePgZfndFTUQGotO46+hag3R\nEe0ZFEg7ELfTO8LaVI5zHEj8sKyMfiOrqtYZYfIsWiUq+nv+alMwQQXX7Wpo\n49hAayWSQ/pFRB0p5ZT/POfZYLc92Gw2MYvp80GmFIrJ4cHRwKGDblw6pLX2\nYT4l\r\n=rxDC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGbww02CPpJkBjTbFlmtdmowGmiK4CNVS0wPWloJ+ShKAiAo7DRzF6qDmRaWdMCDabm/GTl18BtyHE8xOyGfVLIV9A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-invariant.20180828235510_1535500666167_0.5309597683111664"},"_hasShrinkwrap":false},"1.0.0-invariant.20180829001446":{"name":"@atomist/sdm","version":"1.0.0-invariant.20180829001446","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"ac47075c2bfa60406fba811a601a252368996909","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-invariant.20180829001446","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mInsE5XB4zQeCayPHLoHDigqAlgAH824imFrLBn0kxbLDaqNp98AnwpKLhXsr3Q2sAqtScs08mVXRDn4Q0IIow==","shasum":"040ed58230248ae2a571a3e86f33e8ba4961b92f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-invariant.20180829001446.tgz","fileCount":850,"unpackedSize":1818535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbheYNCRA9TVsSAnZWagAAl5YP/3+JoxFAwGrNYZcn0fDv\n/P4xasw/vC4VCR2NdHlyVvYfKUYXbD8eeU88quo9s18sEC+KwObwme/WRN39\nLeTm1+l3+HtObgIsWnmLlx9LWBnmBySDtb0Vjo663YBMLWQozyhJqCOhr90D\n91f5DbqrFpoFV0I/NqwXejIJiB+hr7jyG5gW4F4/FPXcTtwxqel69sAASjup\nDx/jzbECBkLtLqzXk+XYYE4NmxDP4nT4jVwIutpuX1QLHLoDctrKhmHoevfT\nSqUYzwh3v8b5eGIvtbRHo7gjNp+MCJvkwu7kAbVbA+aGlJtMB90vEhqipsAT\nUYfg0XXFNdTVIhWti40jr8jNYMYb5Way0cj2+5LipQoTiEQj3HoWC/XpWWch\n6IXx4+v0GVog5DUqlMKbS56K81co+MViKNVabtCNy95seHfSjNtwtzFYFJnt\n4Aw+QRZ9At+T62syLnxf35waWfHoXjZ3SmHapf495kTEaqfrzb7j+yD4fgBj\nTIPU0gcVjLUsdDAyE1CBzOghUV9cYNGYRjbD27jcZG2hPR89aboRuqJgMNrK\nSXJ64yKP3OQlGyykAdDEoENPdUtpfRIR7JXGMFQ8lnBPPTOjzaf6U0nPfpEc\nzoFJ4r09vT13RqWXfs0dYFSIi3brmrgzhBwUAMV0sLmtI1gSo0ihoUVXUMaJ\nVzHf\r\n=36KK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC6DxJ78dNWI6YhtADSvY4TRY1WCfntIl29sKlyQxh+mAiEA8YS4R6l3B86c8VHBmiMEZyYhXNNG45YwqPHDyjatchQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-invariant.20180829001446_1535501836808_0.7797170621874614"},"_hasShrinkwrap":false},"1.0.0-master.20180829051001":{"name":"@atomist/sdm","version":"1.0.0-master.20180829051001","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8e5ef4ec000c5b2bfabcfb991f312e40f7dcb869","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180829051001","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/OSCknbbe+lI4OH7cDKQYLl54Ia+ZZsBYQd+pKKntW2qaLD44PAu+n3emPsGOlQT9/WewcaYekzKeV/2mjFQUw==","shasum":"91487e306076ec6dc1e46c526759f50ba7ed9103","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180829051001.tgz","fileCount":850,"unpackedSize":1818532,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbhitTCRA9TVsSAnZWagAAR6EP/RRmqrJpNBrM7WW+aJI3\ntvOOwS2TjK0jXSs6Vw/gaSx5YgQ8ruHUDHE20BXHbfanrKI8Wb7xP0J2o9pe\naL1T3YmD6Uisn3qmDRyZnJjAsoSvIs+d7g38qQzq5VcfbpHLvSIq5kC8uWv3\nXch9jf95Bj0OOQTsf8XrPdbg0qHDlsiulNN5yKWIhoRSlQ5BL6p6ssVi6bUO\nKyMXiV1upubc4I63xcpDsl8nRW9pGyFRrJJK5pX8M9DjLnu9xfks+V6A7xQw\n0XVwRskiNR2vGWFZT0p9j7aOsUTXW4UIvHM7mO6Uxx0NdFKxsteFySWgE4WK\n9DA0Sqig6gcajTB3WDFqsVgXY4C5fDDQjZkWfmIf/PPg3WVgQf5Sny4QBNCo\nRe/RFBz3gIiEBnx+sD3/xsa+WukdhHj0MdEYcmCrqePt/tvCfSCPXUHUMGUp\nFrJAgkoNYjutcHNATSoKc+N1D4QYx26UZmCy0oj4Yp+rHqpWNTQffGt8wbSv\ncHJK4NwV09j16HnT9n4gojZWL/MfjTWLg3Nqpg5UnqDzH7tMFwwVl44OPaRG\nn3LEBPoMaG2Lnm5WPDpbXWobYaxwZwBFjcoCb+LPlKQEvL/PF3wdefhWfHSm\n6o8ZmfK9ZFIuwH5ZAQZUrmwWXjBHkzmrG753C53oTxUtVeF2SXHf5dSi5gPq\nsSiN\r\n=goZQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICKfz3HWfNLxUndWI3OYghBlQjnlRGmCLu9lXYL+nRKWAiEA+GVzaOO8iUGxlxjOBY7A2uZUjCuPONIETcFXb+5T6MI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180829051001_1535519570354_0.23969831300810363"},"_hasShrinkwrap":false},"1.0.0-master.20180829090751":{"name":"@atomist/sdm","version":"1.0.0-master.20180829090751","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2aede5cb3bc67165cb8f5a6f1ccd45600f9b9d8a","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180829090751","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Gtu9lfDHvjy46nDftN8RLGHzlhCbeXgLbYaedOidGE+uU3emL5687ItLM22PCav9wllxBkVaB9P5ZNLr8HBCoA==","shasum":"b8f0f0122e05358b638098344757f61eb1a6b4dc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180829090751.tgz","fileCount":850,"unpackedSize":1818620,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbhmO2CRA9TVsSAnZWagAAtgMP/jqjMPgKZEwm7pc5VJex\nh+UqxeSAM6bAqyzhkS8NO+GhhghHMqPRa6gOVYLdbqpbRGBYyCaF4awQ6J7a\nhwhe/HOSPAyWzQfeawUQCkQem3tAZYWUpXjRZlqFhLGvvtHxBL3Bf78jbR+U\n3DbS6xOzxz7L9xZIJVmkssqAzfcVLFUuqXflV4z74ha34Q3jX3KysV8J7qzh\n01xZPEkfPxJqMj9FXPDOqWe0v+Ip+S5Tol0bWCmcub/rF8V+oF+7ta6grblJ\nmVf1AEDjpQa2TLADRd2rXGVRnk4WO/vBysUZWo1AARV9NLPVA8EyCXM3yCvZ\no8mgSMm7W6H1ImsTjSsomwfkHRWDpUqMx1XlyUMCqEyxkzc2CqWxBpeV5zuZ\np2De9Noa8VAeOuSN1tftnsSOWuOCvqtf5f8yt2mvuvqP9HZbYh3di/Zb37Zf\nAE3G4q/lPOE2HCjEszziV459UfChd/pJk+grK50qdoW2p72VpFDBP4X8YMwG\nrMYP4/SyGdK/5YiwdxRa8gJQASGY1oJk/cLM0yHNAlK6uBrK0TfqrYv8lkla\nIuKHdvrA5wUSkKX43eHH3DVMSpF4OgE+pIC/v8O4Z+zPGCwi1A/G1v+VoLvq\nZlQm1cc0c5fPe+mDEHAVzjBIc7ZpGMm38IWQDVAXtGT2u8sq0HnJvaoMv2mB\nJGe8\r\n=YeaG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZP/vlFN53qd4VSd6E8/ktaPHtUJQoiZvSUk8d67r77gIhAJdVJWOuAv2+8LfFXisOz/naRXD7lg1eO8mZvyU9Meqq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180829090751_1535534005727_0.4213762584712677"},"_hasShrinkwrap":false},"1.0.0-master.20180829151906":{"name":"@atomist/sdm","version":"1.0.0-master.20180829151906","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bf8a2ff4f5cdf6d4640282a7edfa64d129d78330","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180829151906","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Cv1gU07oT382oUNIQieg+7MPBByUgZwtvUma/v3Am+31qkuEBKQ3JSJFdak/ukPVKNk7IK0NFdcnZ+BIINx4uQ==","shasum":"3a525f028d34be6011175352918fe88cf11f1020","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180829151906.tgz","fileCount":850,"unpackedSize":1818889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbhrqpCRA9TVsSAnZWagAAwzAP/ipnichlAQyr4KgroTDi\njXYl/x/hbz+cIYVl+y4EwX+1CD0RT3gWlmtl4EZFRt1gApPUK1ui5s42RpYP\n5fZeKRYJ+zgJr9lGhE8uMN8ZmgXZ1gtzA87GSnilWgUdrQKyd5T9eQZELzJR\nCcb48eLKB2A6Erh5GJLSKre7J1pM9bR3UXJMQHz+ePBCrOJETsjt7azmVYUQ\nInVPUqsq06fn9cmewD3KE1ASK3TWE/ErFocl+Vm6R/jAOlQlLVTMBjA7TRA+\nOFJMkVHBmhcWPjvXAPiHryV87MGib999av9ASQlsyh6J2GfFMQqxpv3Kg0Nn\nnnhIEOWFqmbDvGQjgPkRf2BW1ni3NxNs440QNKxF2SznKw7JcbieGiHNuVGe\nxqJd+RJSGRam8axThiRcGm+dF2Ilzanl+lY8vq2u+jYOV85zB0X5ugp/hjmy\nSHBO4kehaLmiqLIgg6c/YZwLQf4xzz+BinUUKXrf929PfFAD8btX1BYG4vfs\njo5ykrzpp7UQmIG3/vgKEw7kJ2504xrhnnTqerzqEHMDlz01KPSOldULjXNc\nBxJAMTYNDqptgp3JL+VeP9ghslz1GeNUymaTWcXn/+zJ/WxKnKqPJn1Njf7J\nt0ahiPJswQEjKnNKI7ouKIFYptJQXwPsFYH+mZoMah+MBZ9M7Zp5AjiJw2l8\n3cYK\r\n=s+lb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD59rsjTECea2uqIrI0w245UGD6MT3uyistXUHjwpOZnQIhAJw5sjGObKOtVD0jwozE2UfQaIi1WRMs0p1TUXbcnXyY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180829151906_1535556264909_0.15688520466048606"},"_hasShrinkwrap":false},"1.0.0-review.20180830044852":{"name":"@atomist/sdm","version":"1.0.0-review.20180830044852","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2b036fa61fbd210bd0f8d27dd7c614e7ab2e0d22","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-review.20180830044852","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YWDRdKpdacWGjgcxKBzhWZpmFoR1pG83E5JE/7MXQcc2Fra0aW/9PZl7NDc9TWx3t1NlSwJNxklbJYHjow4K5Q==","shasum":"3ac807b7aec7060df846e3813c4f032ba5a7f25a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-review.20180830044852.tgz","fileCount":858,"unpackedSize":1827686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh3fUCRA9TVsSAnZWagAA24sP/RXbUf7kwEJnAsLPOOq7\nnmHyTv0fPgifnCmIJzASk5aJixBA++UH60tYwxHg9Alccd4TxV/5Y/pq7Z0P\nyyIVTi+i15VkgM8fOYBukkbJQBLW39lJ3c3lSKKHvlpsREl984+GW33C0EmP\nsbDj+Y+C4A0+VUoMcr4TyXtEWHIUGGVF+v9u2+gbgFKBegFWAuTYyUXKvg29\nVcYKzInXCucp1W0vbjmD5n3nX3sJ95vMyOhzyG2JCk+D4Pbo8ogwXJa5KaOG\nxd55MO44Cwp42QL5Gko0+SO1QnrvJr9paSD69gsVMCzhnM5gZ+b4xFIfb00Q\nMuoYoVojwTuSjk4PrSvbn78vPHFCcyvJ8qR/NPYFf9dydpa3Ia13e3yvJk1p\nwh/6/NMP0vnr2LLEXeckjmitcEH/Lkm5BYruqDoL7I7m5psLzB55Yf4xZnjP\npsNgNUFR8W/3rp7DhPIV2dwaWXVe8NsRP2ASnxgcTNtiIT8AlpL3fVrnmDz+\n/TASdcAapesiQTw0DUusOWL9gh5/fPfUgFTXtFloxiYYfNFaQiRfwgXU/cE+\n7WUOfoqSFOa2/QdjAC2lzjNfTEfpwMx1Ev1fZVdUGNDxCROMLg51bbIh7uj7\nsIA0W43xUc6L94vYyQfGC91LEobRDNu4VuUdx4JEkhGZxH8bZ1lLcJCu5MZl\nl8cp\r\n=EYDw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDL4WoZzthQlmpvvWfcmoGdCjsTs7qzEBHQM82lIJSQZAIgcA3a6uXBJCMPvzLD4aut6tQLQuKMNRaKbeyMil0DBeM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-review.20180830044852_1535604691989_0.9395975754882704"},"_hasShrinkwrap":false},"1.0.0-review.20180830050717":{"name":"@atomist/sdm","version":"1.0.0-review.20180830050717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4dd989c2e50629fa10429d657640eb9403df24e5","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-review.20180830050717","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+vN0eIlvZwElG/fq9+Auer3hRixegtraz9gKtKTqMS6Z8RgfwAqx690sMFZ6uiP8oFVJGb4f0ApKC+BI5z0/kA==","shasum":"cd93a77a91aa5d8763fc9e00700a82055b5d387f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-review.20180830050717.tgz","fileCount":858,"unpackedSize":1827786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh3wgCRA9TVsSAnZWagAAHu0P/36tPaoGqF1jblF3d7Ro\nVOW2c+UGsKmGPvdL2LZsjsOuxUwDnqgF2gPbon0y7p8+nhBM6MkpyfYs9vfn\nWlWwBP8GcanBDMBdpOsT7RWQK9TyAWyQpZj5AK6ubuAHQzOrrzNNV1IN4drD\nZnV1Qxh4T4AwpIYfjA3G73ZULgn0O7gBrSaybfMcdM4pDCbBjaby6iBx7Jj8\nuu5MizVr+JJRVOyznJJrO2nPghSAVYd9+Lep8mjwusChJT/bJlb5sp5wxK90\nflDEgVNdFHgBIiuhFpX4Myw8ijVxvUUFsWerOjlZXRqrTri8BkPhbrvbv9aC\nId6lMWOLTLFWC5vUbLG3SRz3Ezr0lYlJDpa3X0s1C9DQ8mRgH7SwOaI7l55O\nc5ISCLH5UeTsf+mecoTjt3FLfXrMBIgoJzxjIFThXw5s4uRqdrldIOImdvfM\nhhmJRW/AtsR54Fvft/n9XumrmL1N0/G+rzD/S91kpUqDsLNnB6dVMHaqUnqm\na8gvkvmRwWIvA1w8E4Z6RGCW/MH+snz3ciHja5zxQhcRaBwWOnyzJquGxQMM\na3kZlUJbZYiEmR8+cRPI5SXboj022Erf8VZEchjWCRMfzwKel5OOlgpt7AuK\nDe40C/8833cRbyIYZzKBGWTN4dqFRWREvpxM8rI5vO7nX0Jte41NwnvRbWYF\nq8mo\r\n=ebzL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBOWFintoD9k8i+pDsXKY26J2nL9VE6JER6JNp2XhOfaAiEAiofnL807RbhF5uN5uVvcc04kTMdF50zUhkOUE2momPY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-review.20180830050717_1535605792233_0.6402133785740218"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830080444":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830080444","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist, Inc."},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* --e CommandRegistration.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fbbc3278972b979914db926e000098c0f1640a0e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\"> \n</p>  \n\n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and related projects.\n\nThe SDM framework enables you to control your delivery process in code. Think of it as an API for your software delivery. See this [introduction](https://docs.atomist.com/) for more information on the concept of a Software Delivery Machine and how to create and develop on an SDM.\n\n## Getting Started\n\nSee the [Developer Quick Start](https://docs.atomist.com/quick-start/) to jump straight to creating an SDM.\n\n## Contributing\n\nContributions to this project from community members are encouraged and appreciated. Please review the [Contributing Guidelines](CONTRIBUTING.md) for more information. Also see the [Development](#development) section in this document.\n\n## Code of Conduct\n\nThis project is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). You are expected to act in accordance with this code by participating. Please report any unacceptable behavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com](https://docs.atomist.com) for [developer](https://docs.atomist.com/developer/sdm/) documentation.\n\n## Connect\n\nFollow [@atomist](https://twitter.com/atomist) and [The Composition](https://the-composition.com) blog related to SDM.\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [node][] to build and test this project.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nUse the following package scripts to build, test, and perform other\ndevelopment tasks.\n\nCommand | Reason\n------- | ------\n`npm install` | install project dependencies\n`npm run build` | compile, test, lint, and generate docs\n`npm run lint` | run TSLint against the TypeScript\n`npm run compile` | generate types from GraphQL and compile TypeScript\n`npm test` | run tests\n`npm run autotest` | run tests every time a file changes\n`npm run clean` | remove files generated during build\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830080444","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JiaXQWzbWAG8bywMeMfniMZ3j88i4arClTczj3++aLRTRPhuDqREJOWkzWAmhuNf5imZwSKk8dLvplN5Vg3axw==","shasum":"cad24421a93d61d891e42c9079ba8cfca04cd9e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830080444.tgz","fileCount":846,"unpackedSize":1817684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh6W4CRA9TVsSAnZWagAANt4P/1f2d7eMikrxGKpaUbBN\nf+XH2/YRgSsUa+pRSWuRmBPz6jyBGzQyb4cfZjkK+wUsI948sSe5Vi2/PVxa\ncg7E8XlJTZaLsZDmGb6ThxigwGlzsNh1gx/xRfOsXknvL821EI6EjEhZv0xe\n7fenvnzB/N3ErUFSmi/hZNaHKw4ZHLLJRcBmXJqpqTTwzyzskplXuva+HNr+\nb1MeYrJW7hkzCn//z3DkAWWeo6p3n29DQWpfoAMS6F1h9TQdCA6ziAV1oT9o\ns1seQh+X76WjlMC7mAKKPwXB6XF/OoUNJA5b8cUiMr/2PGY7qWT0di7EV8Zk\nNn6r6SkThKrnjbIP1kFTn5aWScqTZ2t84CZo3EuCbQDncQ0Ezslik43gAErD\nh2T9uLwfh8isim+NiBsjT14JnOTKvILEt2nIkzhl58yjW0BO3koEtWWGMU/f\n2DVYJwSHM7ec3I5pvVI2rQQTNb46HBDpHyZb5dc8MhOOGX6XdKOUDwzDWxk/\nKR8TIik51axz8oETCsGhaDZ8eytPLCMLjZt6gnYdn2eCIiIBXgirpey3O5j0\nLLiYuOfQhpGHfxdiEgErrajn5klL5x4VqLezbJtB1K57QPxSK9sRuQ+f6+jY\nCHGvIutrRZ/gaMuNyG3nF1QIMZ8OmVO+ICMiQHeB7lWu9fe1NxLfaM2tPX/L\ndTgn\r\n=vAkA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSAClKauB2r2zw9uc3wkAppm1R+Ye2D6q/jGmk/EiKXAIgLXG2BLWqZna4kz+YbMnGSqQlj639axfeyu3HUvTr0Gw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830080444_1535616439786_0.5815207217174867"},"_hasShrinkwrap":false},"1.0.0-review.20180830083524":{"name":"@atomist/sdm","version":"1.0.0-review.20180830083524","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eeda2eeb1e8aa2365f6d721aaf85f80977e457c0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-review.20180830083524","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AkvofDfXy7CRuckHqIRoAN75jwgGzrfCbXJZ2VDliubKdJf2TIG1GwJ9ur1+v4VR2+bELRzE2XbOIB3u1vbubQ==","shasum":"bdecb6a43afbf55d59655299f77c320f5d953703","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-review.20180830083524.tgz","fileCount":858,"unpackedSize":1827891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh6zrCRA9TVsSAnZWagAALUwQAKTpUAilaBdXOomGjFsW\nxZwbyFGD2A9QqW4BloMiYXBlyQM3dSWfuD2DMXRqCJnNdiau+S1e2k3TIW8X\nE2SNusSq8Iq8Pb8znt7WC5+skp5l5axhHS6FYN5iqz3JrI97Iqsq8aygZz+E\nUq/WVYMxuEY7LXxzWTtHKF41GMAhkwOUAGcuNNoWc8eQbTog7aGm72FrjBZP\nd9FQI3qXqPi8UGiJvwtRsU/KTm/pmGYxaTdoa+5s5SEp85kFRf973GQ59cyE\nkmPqD9NGOantaYrW++hcscla9rf/BmbvB5d0jGNbiazRcQXS1rg1X0nUVG21\nVprVB+c3VVM9V7GaqAH/brCasKrbw2bRVwIqVfGnpHIQdR90984rg8wGTST7\nvAKvxEy6iddsVlbYNFsCH8YpUNGcscZH5rmOC9kfhoiQ0b2L6Sh+AnTLWKUk\n5pIDgU83qWOmNWi+kacJclnbMbJ0KNP4DA6Jn4DChdOr7HB1rNYz+GQvz9T8\nc4bpwj6DcR9xBWL+2QeGjOi+1mGygC6yt3TWlerKnMn6ZGAAM25dCleKJ0Ia\nsvem9/pMBsnqxgIgppNG6NXBu4NlNHDPUdi61rySj8snQXdqk3XvbCAx6ZCr\nweaiJK6lnZRMbIgL2isrG5u19Lq1222ZTPnmHfFD3VF5cd2u6Kd9aSnKrOHG\nPflv\r\n=EZ8N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBAi5Jd0BqBBaHvpXEZGeNsG86sm25C6AaDGrAqQ7BE2AiArllu42PfIkBp91w0dEDEfKuNymk4QEChBfg6l9PvPOw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-review.20180830083524_1535618282486_0.26068495269622605"},"_hasShrinkwrap":false},"1.0.0-master.20180830083824":{"name":"@atomist/sdm","version":"1.0.0-master.20180830083824","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"50fe5e176bd549a4382502d4a8694b65179b1e40","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830083824","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jTrvK55JqKBLReod+qRBXqMpPz9BhheWHM2KA9qWPJ42cY2wU7ahGfhItf3uSAQSvz04LMO3kAaB45WBVtjjUA==","shasum":"373a36614a8b1b0423985a4c8a5cac5a009e8e54","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830083824.tgz","fileCount":858,"unpackedSize":1827891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh62wCRA9TVsSAnZWagAA6jgP/R84k7S8zG4ZUaZRrRhg\n1Cdv8l8PqooqEz/ydsxsdW3tz58eXgORJjHCPf6rNIP1eCGN2z5PqyP0GMXE\nDCpBjx4Jbaad2Ghsp7NA4XnfselvgdYZJ5CsdPaVm6Vkw1HAaCr41zERP0qN\nQvw2JTibMJIvlSj4FYWulsWNznB/Bh9D52ywicYQY1AN6WUWxXRSnkXI2eP/\nHXJUpyWrRZ4QpbYL4rRNAvrqmAAEoL1EoapBB/kbA89QnBrNuiU4DiedhyOY\nNXlmWfq/Pl1GLoDjBMpxnGQdeB7haznbDVThyeFZSoj6jMUcbo8S5VI+Oyt0\neYY+pdFCjKFz0gg+9hqRwu0g/DLY6IlX9DAx2bVOSLl6I1n595uxzveGqspo\nJiPwMTvO2LbYggWZ9aRjzP8Hdwe3jOcdpHJWX8zXQQq0SaYC7t8Pfgrw3lfk\nHxituY2Kbyx32T1A/8wWx+XlBGO0v69/hjEsAC98ghXlfx3g/C2W5m+23ETk\n3V4fnO6/Gl/cY/XHEmQSq684R8YMyzCFWKPGHjf6HURKZupbajQ8WeRDmctj\nr3VyveXKlrwcQSKbc3RAlIeHDCAC/kef4GS/arts/cG7mD4Y4eB3wN2bLtcb\nIK1gVgTN5KV84/SBRZrVDRYsP2BBLvXdHmN8Pp1LcZGyuSwu5pzzIZmGehYI\nov0E\r\n=KFFG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICzn+i5PdvjYzKk7F8VFMc8BcYwbiiGdHF1zxYE/JtFnAiEAgxYz1h/AUFf2d18uAibYSh6UnC1HdTGXAweIZRkkqes="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830083824_1535618479355_0.34215519815930295"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830083958":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830083958","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6a367d98c9365391f62c6b260bd8fafbc1d2b1f2","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830083958","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-elBF72TIYqDXNWKtSR9jAVLEc5nFRZKwj/J4RoENEdgteNVq4i2glhKGGTClvF3gk3c2nr46+yS0DXasHDMSng==","shasum":"2e2452610bdfd0cb1ba8ce51c8c5e82cdc536297","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830083958.tgz","fileCount":854,"unpackedSize":1827038,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh634CRA9TVsSAnZWagAAZFYP+QAQUXHpoC8ggeO1L5OR\nOEv41SPr5Nw7/M2iCLC28/ErKIF8LbtRtCKG6dDleqnur+4TWRiC1GXr5rTa\nnz5NWIIjmY1uepPOC1XETudj6QK1L/Jvcdxn5OTnXiIzLSEtG7+1a8qwO8px\n/MvNa20rqUlKQSbDCJSQPOKufG8xhzp5ihhSDcogrZK9PFR2N+d6kQ29kuzQ\nR5TR0haraIyDybCtS3TCKXGE/1YKbPvIQFoTUmC3m6648bgqXMz2aoXHpvaD\nkYlgouaBA6cX0QDLO0A7EYSg50EoXhTyBjfCSDK8kUbPcOb0KcZBtWF1ZekU\nE7SV3hhzS8qZT0jGhXKVIzjF0axB9WU1wKx9Grma7HdnmQHuZ22ZE5yhs/To\nPHnPGo56Sc75UaqWh/d8gXq00Yz3A7fKv2wupV8bChDzlPNBTy2GGqsmZSlz\n6qn9004JqwtFlRtg3xjWdDul8ORzlLVO7y2yRVfiFkD0RmKFDW9XG/txFy5D\nsmc+k4qnUKErjw7URGlz7jcKtaxYFhmG75tC29o+8aEusADswSrA7pPgEexw\nxMVkOkEpDhr/KAY2y/NPt9C+dAOotfLBQJppudSqd5mu2EQFf1NJRSYtap/T\nPCRAsGCyYnSEsJzmTUhSw2+SBEdvoYgv3v2t+6AiY3FtkL7iNULmfB9vvPDN\nR6vd\r\n=sk8E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpKY1FbBC7abjOdvpEwrmJzqQOzKqx2xy2Y+7FuimsmwIgXHNwdaMFASgM95ktYREXKCLIEIsIYwfSp1wgs8V0fhk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830083958_1535618551856_0.4931147261828799"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830084832":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830084832","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0847b5409166b6c8d8f7ef4bf5d1052e67179ff3","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830084832","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PkBo/8Nl7e6vTJhY+3h57oUnEDKT1AN6BJU5w1TtcotMiMLD6dJ7eApW3jKRTCxbOu8vQyaJfPEJqxKPcMr61g==","shasum":"cd1a688f1e288351f85a8b2883107d8e98423ba1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830084832.tgz","fileCount":850,"unpackedSize":1822863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh6/3CRA9TVsSAnZWagAAnSoP/jv9IoSh6cKFbaAZOY5L\nUrI5WGH5QuPQSP8yDFna3ygvAFsI708PGI2l+uBv4vCf8Wx9O4DhiNuUQRDn\n1u1hiIEz+Ow8pQj0nka77bhLD2Gxeew/1Fkn7yqVlPJwZ7vT30CXiUrHMeVv\nZ90jg22L6MCun3efwwMub8HPoBNIlf8bAp38CFE02gGCW8TtA8W8AYsErrqt\njXEwW7NlPz/Z7xbvvAbneFrrT4dIHV8YRcccw73Hge0IEy8ktA5oqW3qcdSM\nCgPF2+FMLi+AGnIkSQ1mvtzo3tzHefZjfVMxRFOxtazY4XV8SRuPwWaVLXxA\neJaOTt3W6pvUiBYhZ3TKulEiyNBQZsRS1y9dwGwNqvgS9g/jN3rE/fvKw6YS\nrNoJwO9JOblvDq7wWNwbTkXZOeDDnEOG77oQQgWkXHK0TnTxxaAmzJlSJQm1\nIU7NPOcWV401zmHRn9J886RggFgBsJ3QU+etSBVDKGtWSRUZ++tEkMJ/UFXM\nHLo3LlzEUF8P5iWeEgojUgJmGiXxH6Z3RPivqEIrkQcJn9F7ouu9/VYQC0hi\ns1mmCkaR0lXShdIYLJWX6CO4GjrcWi0gOXRg+52dNJVaQZdwRJDMIFGqYvvB\n4GtCY/WPWSyGbxGll+trQt0h2xBWITh306nYKGHjwsv3/S8HBwGEecmELM41\nNECS\r\n=PbJo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF81fGP1rZE4idt5m/oTXtihtV2/pToCiN8tqWIj+1zQAiApCrLHojf4RdZKiMOrq/zvps41xewiCdOPiipJUF2d0w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830084832_1535619063198_0.39108923094484327"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180830085419":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180830085419","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"264e3eb2dcc96773f24b6fc1dab52737aacaeda7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180830085419","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dLp20yPpLlEEw1Z0UZtJ7/AMURMxadDDIwooUN/S0xkniKo1WAY+/6r8fRJGQx6ev0iXgtYBcVTioCSkm2qyBA==","shasum":"1a1ce62680d399e74d3eaeadd1e4254cf3eed39a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180830085419.tgz","fileCount":858,"unpackedSize":1828707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7FcCRA9TVsSAnZWagAAcuIP/iaU/qEBCVct+b9IAKLj\n/Mn+887NeQ3dvADl+LQPkST3z9nomLGr58pqZ+Rn2a+satwBya3kpTeXam0w\nK0Tlk+SRtrk8Bpd7Ov5gRq/AMEPqnXBIA6Oy37XIcqrtdGbGAOd/csAZmipc\nPq9AU0QradMy3mkrKmv20C9Meizn1UXO4scx+IUjGeaHlmqw7xR0HRnXay9/\nrruhst3ORh5dlIoTZAm55h5WXROkdQssgMyRvyAR3NyD/ww1mTbwK1jdfLUk\nKU270G+yCejuqRQV7ehnmZMy7/WJ6ot5VdOITbu1dV3ZQUYD5euF7mDkRr5R\nP2ascmjBxCG7oQr/ydwL56EAnv/J/yRNjp5kOlm9DNhnyXsPqi15KFUV1m1M\n2ZlZ/Wna3J14Kf+ofvAIUemExKlDzsax6S7C49VUfJ5PChnITbGlIizoiilg\nazf25KPL3EjO6iEryH3LTEegz3Er8VVtkXYwNon5CoHZB5rYTyB+3Gs820UD\neXAUhNF7nftH14X8mSzpaSACVbtrIv7XfAaPCpGmadozFZf2IXt7xH52kEFF\nLtSQ/jmrQXvVHm+bkdU9UWmO887RtKbKiIWp9b1We29nXZ8zLwavL3ScOY4l\ntaoHqieSV7RaH7RMGVF2nQ3VGAVs/ATDzTzzNyAI3hTTIUWD6ggAYhsafmpe\n7f8T\r\n=mBqf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRCa4bDF39LZazo3zoovyzEUwcS8WiAi4LENonOzXr3AIhAIyMCM3/Z0rn0g7TxQ6LtyL/rGDMxOV8H8oLFa4eiij6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180830085419_1535619420076_0.7786007981295149"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830085631":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830085631","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5c8b217eb394402b90a123b63cdfcbd56804013c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830085631","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nvY23r00TwX+18StX3SZ9aPglSKFiu7rIbsec6Oe5Cm4GD7DjQ+f68z1wAtKjGzM63TQm/RGBVGy6nFg6eUTug==","shasum":"2f9a7c5e66f30f56f2edccba101098ed75da62f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830085631.tgz","fileCount":850,"unpackedSize":1823002,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7HNCRA9TVsSAnZWagAA6d8QAIKA8pzDStEpH+aUmasj\nwq+xxqD3IjOycZ2fsNEd5lSYrFhtIx+0V5k5Pzz8faC3aiPFmAhxDccqaG3b\nOLF2ULY80wrAnG3D3dPVug0tKqP4Fvyo/ENZqHMgmi/9AzxEjIxHBZXjLXcQ\n2pjBINo03R+qU6cyh4Tw0DnAZDeIDt7XyeC9SL7FIyp8mDab6GbKmkOI7PHm\nyz+Xat9EoMcJpjS9iKZzzdFfVmwTD847D3B4dG13AE1MBWPiGLSozP2ySdzI\nVm3wusvH1eJzq43X1y1rnRFtPtn/09B789jq4A7D+4f+InzQrJBIrTsufX/8\niCuX/8+v0kzSWG03QbelRNTH0d9jBL6SRsEV1FxlabvyCtlG83MNCc8/16To\neDyxevjxX43sLnmhCD9n8si2zqffltPEVe40RdYM7vFDGjOdtO1XOiqYhl6q\n9OTrtnstOPCE/j/ZWW8vm7v1QMUj2lliYPfpToPk06UJ+uLMTPjysBI/pq6P\nMFNRff1mG5cmalpbFLvKoqhyuEl5qg/9oIKoojEWhKAih8nxW+58v7q72f4b\nDbq981P0LwSm2iOzRRNfwcbFfi5dftSYFJagP1QtfPR4LkHFo3XPmjjidxhA\naniAmpj9XYWFovdlcuJUMHlGFLhl5eEWLn5ssOeNPRE8/zS8vkJl1iIlIhxi\n8Fdg\r\n=bLGW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID8Z6B9g+xxG3USULwLcakrbHQFt2lsbPnZDIg/fypxtAiBx47yWj31b2gO2xym/VnAzcizdlUKszqK74xobOBrnZQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830085631_1535619531943_0.9498383989774752"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830085850":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830085850","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0847b5409166b6c8d8f7ef4bf5d1052e67179ff3","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830085850","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BrRekYg+cwZPoQpB3AoHfi5VOYhu1EDuiW2h+EEE2SZEyiZM6QPNYKjaUgjK9asCMlPMN6EnAnwZYTwVUwj1QA==","shasum":"bc7a45828418b7082d6cadc63b5c15407ca7c081","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830085850.tgz","fileCount":850,"unpackedSize":1822863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7KBCRA9TVsSAnZWagAAV0IQAJpPLbbmb8+KovLgT2MK\nWNJl5y5R5rsbbpsgiMm2NyHHArNHTFLefeA/FyeA6CyOSkIwYiey2q1Aq0wW\nqNNVtkNyxdLnetMtVDahs+pSHHaIUi7QJ5joiQDFw4IlfS5JK8dfh9wKrbGm\neSlqw3j7kZ1TeKW58jQTiCpibn41o/iSglYsP2bnMYerBN27UiyZMQstVtt8\n1ofSrjJUuj6YRr0RM4mDGfq3NO65Qhn1eYiQ09agJY2U3tXX+mXrPf1aIqYP\n8k9PQEWkodZGc3enV5huwHAPMd1nDM1lZWcDW+WFjs/yNtN4gUexnZRhrkZO\nl8OzhtAsm4qF+SyvSujSu6BapR26wZUdvuHgov+RH0c+J+ghGJDl7v0Fvx+Q\nx21O4MmoD1PFlA5tswJRiHkpBL4kCiAgnd7aTJAJDq8NCtUI8XPQa/Ns4aSf\nF9bR/9768tmMLg3yQmyeUaZUz4T1RRb+e6iOjBAlrUL+E7obNE0Q4Vp4WA4A\no7GETAy8x182GZYKCiyIYROm0sTFNFou2cM5d1jngduFWIFd5bh8+dWJLCWo\nuNPohVo0N9huTnNrt/3zpnAYxSk6Ez/RUun4Dhh3N0MWMBxtey9pQhAzhmrd\nEp3tc9QtL/iOp1Z/b8gSl9zefS4T2LxT0rdT5MrdGkLIbkAUoZAmg1MdPI/o\nnKUQ\r\n=Z51v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICDjD2M6virO+kWHSaPEJmfd6Ms6nmBXO5p6JR6HzYgbAiEA5VnY69XVCZJ2wBQajXZls7YyawKPYl8KZ0Z4tZ6fIDU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830085850_1535619712916_0.9827999006015362"},"_hasShrinkwrap":false},"1.0.0-master.20180830085826":{"name":"@atomist/sdm","version":"1.0.0-master.20180830085826","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e30b6c15ffc3b35bf1fc09cd822f9a6fee1ee5a6","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830085826","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-v0AUfwO3ThEUv6i/gk5jV4/tRMir0gqDRjFVmYF7gUFEVM4Jc1hWmJNe/anCDhopz5M2cHwT1gOIqXTO6CDO9g==","shasum":"ed2377be80cb3997fbbfbb30246a3ece79ed0e86","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830085826.tgz","fileCount":858,"unpackedSize":1827522,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7KgCRA9TVsSAnZWagAAyasP/2MQpCTspjUOg16uqH/h\nOaqvv6sPiHx0RkG4WUvq8Fm/PUMtfDnUsT4/HBkbrZJMnRQtmZ3WFU1F/1fQ\n1dsexRNHfC63HsZePRrqRqB3Ft+N/wOuU51m6Md4oj+VJ4ZZcVlkyTt1rnAm\ndrVxCKk8QxLWWSYkQUyt7QewdfsjQsMIdMWysDJv6ITQMKprM8nihSdY4kni\nOzZ2vAxDEDvEYEXH5Vml+5ky5AUfbVQDGHobtJJ9coiA38o+ciP5ttoWEWgP\nULYyR4jQzdsrKyAw8KZikEq7rVwvaUlCIXHWCzTE/UZlbCUxz8mkJJrQV30M\n3d5I2M3jKu0GnXISSB2oc7xqzn95SKQpT0OCvVFSpOuzu/1t+S54TCv+Cezf\njYt862t8FrcTranIUi7vzCdnY40uDw9Lm76KPFZ+Vn+mYCguCKs7jsC3/N0E\nhwWfryuyZUN1x3ldfsBef8mBuvb6K84iCI63LieWbkx1wTYHyZRAHgkQflUw\n0eEVr5V+D0BlN4vGyZBzpTWp9rKAfMhyvqrYaT09KocpnmeNAnkkHpioZd6K\nMhDX+0zy+sfWQZhjPBACuJEz7Ti+q4jCCy1WjpEiFeceZxDfcl7Evk8mcEpr\nu5CllEMe1O7+Hqq8lmrXU5gA6c67QbYFMlk7UsZ9I04bQWzfDG4SNBxuH6k4\nKzwK\r\n=KHFK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCgKBIoIRuunMHAG8rIEGMltrIoP7jJavBzN47H1GZf9gIgFhFCDPE/UuuFZV5/3D0+UbiIQY+ORJRmr8Kmd6rU0/I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830085826_1535619743914_0.03672953614808816"},"_hasShrinkwrap":false},"1.0.0-goal-implementation-selection.20180830090216":{"name":"@atomist/sdm","version":"1.0.0-goal-implementation-selection.20180830090216","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"48330f4f5a82e77344dc8ec60d4a6989be78a505","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-implementation-selection.20180830090216","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-J0VoprlgBjffihdJjW+QTCYENJvaWd0Ok4xHeJPSjsgHbBv+SFJ0JLgITRbokE1Rmruj0U74sQeDtb+MH3+TEw==","shasum":"5dbc975a10ae04bb7360b2bacaede52ec7db601a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-implementation-selection.20180830090216.tgz","fileCount":850,"unpackedSize":1822866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7MXCRA9TVsSAnZWagAAE2EQAI4PI4PPKaHJoaPzj4h4\n41FM3NVfGKny9oBELohNlzE7KJaKyJYZ6KffcQEvO6DTJcVXeDAkYqvQ1gby\nKtNGzOldnQzDYieyJGwGazPDNM0eoKfc3CnQ2p7xcZDFHgMQcdBbkxPmNTPa\nbd5Ya3kviKAxX+7q86owSJDRwTjDc0xcLOunvozuFQOIAjttUvdufFJU1nwu\nfII41a93AiLUFmrTPef3PJ8yn4rV4Mu5hG2u4VkxRTbPsIBEAt/rYXeSSOPv\nNyqV/KlkIwhbUPpUWFpFxzQpBoBnqX9ll8eC0ekfP2ohxf1IOrY13GvUfn/e\nnpgwdsQzUWaCo2tdWqEBDC77iP8uhsfw26OgX08z8ThT7p6O6MF2RasVjl/7\nzRx/nawBmjD+kISpqIeydpyBC7rQw6g5VdnKp4uhTr1wgyr/DHJDzZbsRf5J\nXT80hrw8lfeLe/T+etlnPAQpxGCnQpFubauh9JC5mFKzRifkFFl6cXAa58tt\nn8IMijLYXmitBXSByygN5fxSaCjYprE+KvbvZbrPxKIU49OV4XFxBgPUZufi\nMSbIV1At2D++ilCvYW7AjGWcxA2nqMLN521OkaokmnMSSUOwgufzVd/gzXYp\nL4r8pW19sUdwCZM/aeph9qwYWrXFnClx0IAw9FhmGNBSd680fU71Vb+PAMwT\ngsK2\r\n=hGHI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChswRC7ENRVH9ipA1aPUSQkNZosK0lsyej6vj4KjdZIAIgOe9+kzs/mPZwXB0f2vAqzQOoHIlng+iRG5T4gFUErTU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-implementation-selection.20180830090216_1535619862595_0.3923152917277173"},"_hasShrinkwrap":false},"1.0.0-master.20180830092848":{"name":"@atomist/sdm","version":"1.0.0-master.20180830092848","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d3f94f077ee0c3479bb35ee7610dad5cfc7848b6","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830092848","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3+JhkbtLDsxX/+0+BSigEqBgKLdkf7PO2rAvuvC3m/yf70uHZv34PIlPnDnUTCTv+9ACf1/AsctoATIAguozNg==","shasum":"7fd493ebbf3d623d038ca40dde8ba6ab60ef1086","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830092848.tgz","fileCount":858,"unpackedSize":1829697,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh7luCRA9TVsSAnZWagAAW4UP+gJ+EW+ZOahAhoItZyit\nhVloVIUq2PcUobHaAC2BDG0s/3QqqNYOqLyt55EpdmrMPvzz3wYvB/vy6ciH\nCFpD0SB0y3M2Rfdfkcwu0NcoD7i9BymtmbReHof//gYkA96YaezuW3NU5tU8\nLVZU9VFoI2hmsnzPi1z18kVWknqXHqjoxLgoBPwzOu8RCxYPR3wGkZL0wWcy\nX3/e7z5oW+ORqWQFh2+Go2q8VoYFzmNhsY59ZcRXSZpFPvKFKqtciowixqPX\nkHil7+YbC/BEn1pyx6rb92X6Yxw9OATvbbmY/4YjG8s8mxOykI8dxofxl0Oe\ncdlWwqyI+y2Vj0HnJZ0prsKnyqXJLQd2/BD7wFc1T0pEWqsYLA68VKF+K/Je\nuq/09A9dXXQlzWHMj4U2HgooqDX/f02q0Qrutq1E1gU/vqP8OpIKZ5APStq9\nReFzwP2Kpg124r7FopYS1CFG7pwZWE5T5ogdF8Mky7IUn0LUeE1VJ5BZr2aW\nod/jRPKAdSeOm5m9u8n/WC1MCRp3O5VYyc9SU8a3IRIoioujP9EE9J5Jb8r8\nWxOocR7I5T82nligx/Bq+8/ddU4Km+XwIlzTbSCBA4rOQgfwjuRFDYcdUFPN\nj5P2Q5VgxllmlHc4kjij7r2e5k0Lx+R5BQFBgcxlvqHN1ZvHQrh+AqYQKJmO\ncZSM\r\n=5ePM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDuB6/kz4RbzverUeKRjfM+oKy5Aq+dDCtPSVlJ+4raAiBhzdj60t1Bk7b4mvgkOd9gzBN4SXaTx0zRsCyWq9dlRQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830092848_1535621485309_0.8149380118521026"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180830095355":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180830095355","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"205c3b14407e0a3075222d8cc98dd8412d86f34b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180830095355","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-H6lJ+DIc1Dn2Do/FX/M9MjtWQ1RDifQdPK8yvKogpkLZPPdkRK5h0g01cFh/0FI7FGq+4QG1nghPhvi7g87Biw==","shasum":"f061157919dc8422a8448c15c97767b16637c7d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180830095355.tgz","fileCount":858,"unpackedSize":1828872,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh79bCRA9TVsSAnZWagAA5SEP/RybXihTRk4MuohzhLTU\nIex1Xpt/z4Vfu0P2Ho8TJArDhsLIyUce+y2hWmQzdK8+plLVmO0BvzvUBP70\nyNPGPqTlHWZUJbh5b7JxAy1yY0LLCG4EuLmHHyKEhDbPJamD0lkToc0b7LbT\nZQWsI0Vw5dqrLQ0WiyTLk27ta0nZmQ2MgQhhlaFDgEyTvonPJIJzCMaS6PXf\n5vBXGOnaFzpHzMs59YxpwCg4UvUtdY/q+3LEUcfC852+OBu+924vDk9Qkpa0\nK7FmM803fVDCzL4V0CvaznaVHNSwhEiP0Ht4Dkgv2tIHRFC/ddHg2DbnX5No\nzJpo2fRh+UNTmGNVbaKlMAARpc1F50fsPUG7fVQPAt0FKikjpJNk3bhoEM28\n0s3+tvDtxLWnX2YEhEaoN1MWIh6kQUoFExyZjhl86TSSmWAhxPpMatPzIGoN\nGax57tihiU9TaluvGWbaWVSUtcbDCwUqQ+8D42aHDG6xFIWFUb17RyJFgyZb\nyuAbMxu+xoN6SxBiiecrXlYlm0tgZn2fg8WI9XdwRffO/pJxj7ZqmDRZadiT\nYZxwCUAaGAaxuwOj6scPOdhCC/N3o6FS4O7EPb1KMJM4Clp+w7XuwjxXvsCz\nNlU/V6WmpG8K3r6uNZvCSE5oPkr7c+btE6GpE20jdG2OJ5FQVW2iL3wcJnUQ\ncrdo\r\n=ouZ4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCDGYtjbr/+xjTQy892mePZHQ+trb6LUDEIabNRgETZ9QIgHeo3WdWQZLvyCJHDshQDnewGmPFraqHUo2yAU0nOgH4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180830095355_1535623002740_0.5199123627823985"},"_hasShrinkwrap":false},"1.0.0-master.20180830102832":{"name":"@atomist/sdm","version":"1.0.0-master.20180830102832","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5822dfec5ea571b72509105d94a8be1d5dfd69d9","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830102832","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mW7+c4e0hyORfviaJYT0FhdztICLO7WASyCmw1DsSjgWM9382P44bl6Rq53QambuBBGWfJx8LQbcAT+Q1141cg==","shasum":"626063586cd1f11e51fb909047b5abdf906e6ad7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830102832.tgz","fileCount":850,"unpackedSize":1825018,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh8d7CRA9TVsSAnZWagAACAkQAJdcroEMdpqUEvE5M9ML\nWk51lNVc5UTOY4k4apEUXfGIp8HozlN30XRS9vvHhVtCj9mnjttKQKlS2C7L\nx/ArvXuF0ryu1jLNHJD3H6FKmwKHILinztWhN0WJRbhrUp7CKNP+hlCF0djC\nxZuWVngeLemG6OC7w/eKUR3sxLJDsIF8pAF+Opid7F6hvgNfbpZx1XBSEny+\nGJZvLLJYepFgZJwxTe9JorvbvJVUM1M9Xx1R1Lw4RYNIjNCKzLvanbheMoGC\nqc+t/cl41VjfBFLd4aTSsbLbRqRA/U7QiOTse2enaJl1Io8UOa7a5Y1jnAw8\nYXllXyQSV2imzPMFXofIKbERaXB5w6IyvaCidnCXAWrqJ928YFCCBEEmBKRp\nFXzHL+mCRHmLXucogoO3xbkcs/asbXtTEPoFQr5165g9aSY22xiiAdi4mDS2\nebHsUjXKbk4gm3kttoxb/EahTeIJtqfa4xCacVQLx0SvB+IZrEIV/102yowX\nKmZzHLAfg4NXX5xVjmX7Sfab3of0PLWICbR9cgvR9P7XYRjD7uzRSabd7tMa\nmAzEDrBiz28eJA+OXY4Vo2cFUA8pY/06dscuyPSiSZn16fFs1yCODraivGNK\nfLX5VQQLwSjTkFjg4t8+pH0YrbvzKoiKhfXyQbaR7StwNjX0WqZNlh/R5v3T\nQ0KK\r\n=lRfQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFDDTGQAT12iByDz5pRyzlVOScrV4Wwdy3YiCX0vTpN2AiB8L3tp6O/DseV8Ns2TCKRov1f7ptvQMI7Zj1O7o8JacA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830102832_1535625083116_0.6167582458537153"},"_hasShrinkwrap":false},"1.0.0-master.20180830103116":{"name":"@atomist/sdm","version":"1.0.0-master.20180830103116","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2feb033570eb113bfb94364d117e4ba773a1580c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830103116","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cQF1tvtUZsTDpF6OyoKo7M7YXXrNjlDS1+lktlLIsIg7HEXXIb7nqlEBnNqtJGvBUNQ58Ep5r9R6NA3HXv/WzQ==","shasum":"c832b67ff1ff0d9a0cb0599b7e649f632c1dd3f4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830103116.tgz","fileCount":850,"unpackedSize":1825139,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh8gfCRA9TVsSAnZWagAAr1kQAJTBI9FN+QnQUh6anZIj\nyPszfROvoPiRyl+1JWpqLo7Mhmcr8uM129dVTBUR2EDIutMfWqBDPSS3U1fO\nIF0ncUml6/IMbFn/62tJ5ScPNYVnK3GzJ4QdOBDg3st+LhrOdThSIC7wXQvC\nPwf3i3Ni1K2bLpLfD/T7q/9F9bajr6roCqBONDjFgx8WcjCoeF2EgLLzhEh8\nNgk1kfsg3MbliZM0SZXgQRYSMOhPDBLf/4Imgadof63RLhKw3pz86w6eE7jx\nmhrvOoGmGhShn9GQqnMhgcsPFwez16JmVE1zJdUHmEdcivB5vUeu8aaWP8/5\nIBhIMo6KxfZRGzcxyqtjekrRt9l5c1ScI8ZPUVmB/niHKR+m49ZCi9Cs3/WQ\nfRg7pB6/7tG7xPHACOim3emG3Yxgy7WTpBBmBu0CregNpye8Mm3cf4AKanX+\nvCYJiTTl0W8VxaAkvj8ARYNawzrsoe+9qlihl8BomJACftvgGqphrT0s8br3\nPBZmc9HSGIZd42rxEDdKA0GgYgXJvd3EJich4KAiEZo8FeAciS5xKN0KNgy8\nCmGl8vj7vADELRX+sL2QV5tpA+Svo6OF3RK69uKfvFS219SgSYfiu6/30eH+\nzlhEqlQeKdztr56uRbtQpetWTC25z8hInG9NymUnnb8i4zQFvT2TXAS8rW4K\nBqP/\r\n=th6S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG3BuSWPILuwfKMLmdxA+p4TN4xkbWD61i7JGA5iK8ZsAiEAz+8QcVcynQ3jYQtAKoyhaCFoMt5T5BZpVjWg6dV2l5k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830103116_1535625246861_0.5088320764407439"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180830112324":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180830112324","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.2","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"54f2545f5481a2b9f48f161f74996460cd5d6e23","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180830112324","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MX9KqL/nhzr706V1yHm/tfgfXhixrlXaz+NxTcgY3ElvhyU/Lm6uDYuOVHhtQpY/LffISpOrAOFPsIQphAPiJg==","shasum":"a34b4f96391a7a7def059499a34d6c9f6ceed71e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180830112324.tgz","fileCount":858,"unpackedSize":1829076,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbh9RSCRA9TVsSAnZWagAA7hoP/A4qf068WPGHM3cTpqL6\nvWo7m+GafUHWhmD4S4/TI5LJvMk8YU5Iakvpy4ifLXKyD+iuRrQvrqHXUQUT\nIDTYhwmRdJOP7LpY7WNWB95GxUiVoWRAUAyJzy6WOiyzVXv/OqmniNIkaTCh\nIrpapvRfjqkRjtIEIdJsbbW044pI9AWcjLqWdI6swrTStPf74CdTBtL00YFK\njuSzujIUZgfWDpZuW+U3t4zKsf2yerfLf6KgKY44batTjAYnotqqGAcuQrNT\nxNsxni3PVWlyRV47o5Fiy4qbrWbUUoQC0WHHbKeMYVcRPcH/R6ZAUMfeh1zj\nXnNnwmyGXXj6keozB6i/Uela4OlSwB8VzrF8bX5sOg2Ir6fMNimdaRAb+tRJ\nN2Bqzc3FND6KsBFV/GZEx7h4jsdMjSjwgTI/ZEqgITExoZ2iGoHAJVFdcegL\nUttlk0+XmMCmMxE1nMWUs7TzBFHd4vykXY/1bZDhrZfE80YTp2d6WiBM7BHO\nXF9lQ6yQq6Qq0qRkrxSNDf3/v0NQicSKas4B7fhjY0AoVpNsA3vQzKoOd+6j\nC7sBVo4H0hs+OCxBKsvnOmNxy22ySqYdu6mrhqPxiEwdshQ7hnylwsk/obJw\nDFUCsBMO3rptlbxVuU+JcDPWLZcWgjRDDiHm5rWivyXw3rm4hhxy2TKiLLXO\nubvq\r\n=9C9S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkDedbLbyZ8VZ6S+7JIAA7wqnTPaLGKzunTJRQ76rN0gIhAPZC45tPKoHTyDOXeFW7+ZAVhfS5ia1YX+JPyzxtcaWG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180830112324_1535628370132_0.9544183450746739"},"_hasShrinkwrap":false},"1.0.0-master.20180830163028":{"name":"@atomist/sdm","version":"1.0.0-master.20180830163028","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1d18bf9a7da13a103d5364b4d5edb5aec6bb10c5","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830163028","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4OHdZ2JZH7tOewQeZN8oejy6wWzk/VE7MMT60xATcD9065ztq5bYIyTMXOctbu1L2y7Ozy4MAEfKIup445fBww==","shasum":"a783528e75ace3c2f43b6f250cdff5b19ddb1340","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830163028.tgz","fileCount":850,"unpackedSize":1825612,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiBxTCRA9TVsSAnZWagAArckQAI5UcVXSe8ivWVrqIr5x\n694Q/NtlXgzzZ8i/z59eTV2sqPJiLQDnl53wHf19556y9mo19rMZGR8vY78P\nyhsmdRHqYoP2shRYzp2ik7XXJH2wYb18ipA9N0soE1pQQo1K/6kzbHgPocQ/\n4AKj0VomB3MwqCi+mi0pmIkJiszbhRbQvIzPBawBkNMn7a2U0PFAS5Y8rpkd\n8euKdIj8B9ag737aYGR9l7yIoTPzQybO05cJjNK6PUoWNaBi+V5YWYFBqbiK\nQhs+q4N9I2YYe5fu9kVPdwaPl07ToY1vmvqAAjMKWCe3jVyFfw4JsA+k2t25\nxV4Z5NBpR7wEXKUos7GqPBIkTtvLpKQZz409yIK/yRgM/ljAF3gh7KeogNk5\nB2ss+rnIlflsRxzY2Gxmo/s/lpoKAoPtw3Lm1QTMS4ClxkGlRMaOO+0VTfkK\nzvZs/RW8hdM0YRD/Vg3gXAjCUMkqtWurr78H1vrm8FR89d7Jcj+sn8ei55Au\nzxd1O4HL+Ac7DUDUrsE2oVcHnotGp/Vy1CvCiSodQO02AvjEZnEhMtlp+XCB\niyOOYRHG2XvelTH+eVTf8qjlTUhV8a97NLp3dX3AEQoug05ZdLRDcHL2TzS6\niWWr6sCnD5exYoYrIr4w9k04Rtmg5RnfmIObpWzuv2PwoT1J98IxWQdXJRwC\nhxic\r\n=/FPT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHSuFW8lH8ciYez3hWsXkDlQJ7uT7bNDe4KAc1OvuddQIgQ9lLyr2t3psuTKM68rWygDDWHiiwvvmNHzpIY/L/mf0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830163028_1535646802461_0.5816964264132383"},"_hasShrinkwrap":false},"1.0.0-master.20180830204234":{"name":"@atomist/sdm","version":"1.0.0-master.20180830204234","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a0c2e06c01faa6a1911d48f5f1195b9c5e281bf0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830204234","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YFXu/KDGN8Gq7Ed/SXHvqVXT2c9idVkyY0kDiXie9OzmDOBsyLZFa1x9iKSGukl0l1xGfDzD9No08ogudJUIuA==","shasum":"15815c1260e2fb238089d951f2037919ca8af1e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830204234.tgz","fileCount":850,"unpackedSize":1826075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiFdkCRA9TVsSAnZWagAAoH0P/j6eapY5zcsK0/a0Ebgw\nO8lTnVD+EsihZozzTNl0BhcRR58NXHepKVk2DXJHDsktdod/aCD+2rJYRB/i\nyFi31PMyMW8G5ChM9Cdm3fVxOK/suWZ7PjqjlIYBz+S0cRPJFb6ohx62XbtT\nr0tRpxBbcctMp9btMyEweK/45L642c5ob57LD4w8kSRirNcmrvGMQprM174B\ngGqB+7o1+jNnJTWRZ9+jJS8Vf1M8OIzBnl3rThaLU0zJkJwNGsR93CBlySSx\nZvyz7yjQotKfm6F5NPh3GNkOrBO591+2pxCKu6Lvu1jRb79c8JE4ppBXEeZJ\nygXA52lsqfpecvW2tfnlWi2/j4k3v4z7Z7CnSvIAMCf3Q8DqbELpIX+vxuQZ\nlvi2DYuGrj63p90jP+N9Lh0eAPP/Mtbq2bwIHaAtRxnLMHxOvHRozWz/ihnc\nk/ozYFtjF18t9IW1X7gY4JnCjDeTrleM587uGiWWiEx/ztluDe+WiOT4ghg1\nzpiqobg6OKgQrUcLWOqg0/oE1xiFJm7iuuPz2f4eNP7m3G2vdSAbXSxBv+zS\n8haFw61nDSvY3pelQvN4rRxcyukEZECxtXHNuTjGtoKolLXT89r8OoO7CxLt\nYSLNoNGhA20XVQmNnZcyZuu/DUeMGtTPJYTd/5kOPpoKIHTRSnS8/0dXcZoa\neCTs\r\n=xoUW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKK9cGF0h9Qy/jtciKHsZ1gVjzqOF7sCJk7Xj/C8u1IwIgVCTlY+j/xtN1hSKjTM1VAXhK5LmKXbx/emp0IOzQr0Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830204234_1535661923407_0.6822377068453602"},"_hasShrinkwrap":false},"1.0.0-nortissej.update-print.20180830210428":{"name":"@atomist/sdm","version":"1.0.0-nortissej.update-print.20180830210428","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7aa2e429749ad1906072765d2061f9b3c5a67333","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.update-print.20180830210428","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KPN49epv/vDIY/FFUuLzoxuq4M150lIQsqiDOvsaSMvs9VbFRQiUNTMF99N0Of/9rzs+0SuJ1vfoUCXWed+syw==","shasum":"0c6b3a3677211556f220c657f246cef29f2612c0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.update-print.20180830210428.tgz","fileCount":850,"unpackedSize":1826090,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiFx3CRA9TVsSAnZWagAA6EgP/1cy/kOZYj6iv2VNvKRx\n98yYxvAhOttk72nyAT3+zcmRiGJyb+eC4E5UKpBRgbs4KZyE1M7lAwJyRxVN\ncN6SmsCOakm0XoHWrHVPh97aNPn81T9dIzFLyt12RXrkjmao7j/H+f/3LlXo\nD245RpAhiCk58ER2SW6Nfj8K58tV3SK7iM/k/0e2LYzQ/wHJR6hP47x+EDHH\neqTD0/SlvgFJnPxNp8zhIGGjXFpsSxaWgY7NzlcHMqz07RMIda84wOrQulpf\ndHuSZvpqlyW2Gy4SugG3OGcQ7djwNG8EV1Z0BtHpMxSqxBtS6xKeIPsaWuys\nvP/Wh5pCPwSbOAy296ZGeoYayom+N7gft6OVKEE0x9J3D5nj2XWvM8GSSLiI\nZGueU6t95LU9FSamBDvpSMm4ffK7Vqc6z0PBUM4f8xRbC3g+AxB80DXbCe1W\nM1EFvxHBLp7D9xcXTUYLDARbk80XHBFpQrYSAlvV/Co7ReyrVm6mt6NVvZv3\n1K8XFyjUdunTrKC+tAoSmJ4Hq9WXK0wrEoJu/3YG8Ebz24enLWTmZY0DY3fH\nFjXByYamN2wyylEUsbW6nyy0GtLGjRRdR5Ywqy1pcHq2qhP5lBzxD/UHx5Xz\nFiID8oZHL6txpMKvCrbePXxbunjB8EJ5hVZLnM0qLjvNoI9RxzM/Z9RUxVQ4\n9pva\r\n=lMIX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCz5YO4yRdYLMVveLE9ZdO4J3bliZUhAOFIYRAG4YEEugIgNoU5lSiqlifIOJfJ4hxRfqiGNY7NKMJrGmOk+7mSBNQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.update-print.20180830210428_1535663222893_0.2561807696844782"},"_hasShrinkwrap":false},"1.0.0-nortissej.update-print.20180830211058":{"name":"@atomist/sdm","version":"1.0.0-nortissej.update-print.20180830211058","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"793d454811ab69348f326f3736ebd373a876cd20","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.update-print.20180830211058","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4pVsPoHCIipKnmctTFsURxW2Js62Onb92d9B0LZ24dQoP/xJid6XWulSoFO/ssaJD/DqsgvZ58y8qqq2pn4yzA==","shasum":"1764475ae37db8a3d0ad44326b48711828e33eb0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.update-print.20180830211058.tgz","fileCount":850,"unpackedSize":1826139,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiF4ACRA9TVsSAnZWagAAdiAP/R4hk5irNpYBx3G7ywcn\nDYzKg2uW8besBjeDiAhP0MY7KwdbgAM6fBHgq+RJFodFKZs9FB1n1PbdtuHj\nZOVtp9KA086IHx27tUrJxMWSVKmJu/Lu+NSRIW8ecDf+4Rlm+fHcZYpuVlRh\n8X84UQigNrBYrPXI9s9PYJ9jipK+NG2gXPNX4blO8haKsewZl5PHpWW65tjE\nGgQ4PLw4XIbiOQjFW7x8EMR1SM5XuioCKO89x/wO4YadaEI/da5jX+yJgGZd\nQaFpo/F8LoIwyEG1/KELtdtZfk729L1bY2ccEu5taqoKK4Kc4URCK8kc3y3p\nvSpstJmYYJWooPFZ9Nx0sAA46gP2j/B0VdUzmmzdCp/Qb+0ZQdj73ch0O149\n8pC3IcEZtxaW7WyWSF/dD4WbJaQnx31C5bDLav4YTTqHNe33hJh0btxX9q3m\nKT4fidcVYwRhX5B/dlKEbeHJQ0YCx/d0798uWil5aQ703aemMehXKw8fQwoy\n9Vu2PbAw4U63oRs7GG1/B20e68Te779ZAXpp1j5/xayWmmmtyQU9lazfcNQt\n0FoyZsexXq8L+xEL4yFL+DsTkGPVz12Dk5j27Zb888BmHFixXvBTeGqQHqi9\nKPrl/4eVOIUm1B1I6lofVi9rU+4OiwYKVLMaOHD/UhpgC9sBP3d1bN/ANMjX\n7oSz\r\n=rktB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHTBu6GRUr0sVIZNUDrDbwg+6t3OElMU/0Y2JwCiesv+AiA8QrFvA+hCSjeArqqfi86SMCrz5PN/kM9D+GCflYn20A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.update-print.20180830211058_1535663615758_0.3928124583231072"},"_hasShrinkwrap":false},"1.0.0-master.20180830211714":{"name":"@atomist/sdm","version":"1.0.0-master.20180830211714","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/node":"^10.7.1","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3676fbfd7fb7b6f92e970179ba9034015fdb98ac","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830211714","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ekd8ayonhL7sDY+KJ9cSZTQzUl3YN+V8jXhoRloftC7VAQB+vvQpqWkumzVvk2+7vjc8uWRIAlzjEO8Aiqikhw==","shasum":"b7a88d2cedcc02a4f1b1ff91b9a14d752aaea2e8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830211714.tgz","fileCount":850,"unpackedSize":1826123,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiF+ECRA9TVsSAnZWagAAkbkP/j7kZ7qx7yXoUywmA31F\nSAvAgvI1WA5WPXWV+ARDwgXiB6s5bS8q6kNQslkTOrFSP1mgDzcQVCx61xTt\nFC2KB/5MFy/csbFv9S3C4gfa3sP743fqOZ7uNftGjIcxfpki+G0Avkgtvsp7\nbb4Rd5DZg0gV2/zhgEj0tkTOwZWyXD3rxF3Q1aIOqRavt4+GppNp88VVRSfV\nooxGyjUf03duF+YwIcGflScW35FhRKRd20vOw5cBZZllwt1XgBeO2q8Tq85r\n3JLzqnpyX6ZFiyvHLTT4PaJAsac92y83ML/zjxCoD1IFctrE2CJQKa7hoHvO\nTY5qj46qsVePyjbAf7ZnmhKkPz/k60XmPXZZfwpWTcI5APfbJgLm4vYTmfhg\nzLriXbXavOqAgLcNt3ftTiFHbmCtPi7oSkNck7YQa9qDH6QNvt3qd5NVc/aX\nRmcfJPIWvKHbgDT4Kr4znsVNCR6j5jKBi0Eh6EZGh6OsW7DGh2FykYJSdj3/\njo3WWrI1KWe0dB3bkKM3nclQPIBTeOzsu+CD50cqUlqqkiMictm5kIW6XFvd\nODw0oKnOfCfMt+2MiK475v/EbVDM8sbsqrQYeHjeyUAISbATD4uSyVg3oySi\nS1936/sj7mTfs2ye+boitneqt8EDv41yStaiykjaEdECpv0NUXarC73mOCRG\nO58O\r\n=FA1l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSOvEgYTSLPGZlpGOWY8Jur/3PJVfVhs0XCN74gd88DAIgIQRJTo1QVR+xousbG7mYL5gKpLrXcJXOKBbyM1dznms="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830211714_1535664003832_0.4411932392800111"},"_hasShrinkwrap":false},"1.0.0-push-pull-496.20180830214549":{"name":"@atomist/sdm","version":"1.0.0-push-pull-496.20180830214549","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6d9713a8b3cf1ad63df61a044ed084f6b94accfd","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-push-pull-496.20180830214549","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oBGlw6QUQZxDYmVKOkcycRzykwQIK5Xh7FMHHzlUnJrLg5U83sllnM8KV1+zRw9eHvSvWAWSJDANpypOrCh2oA==","shasum":"d57e695655cb31233e0232986a83aa8a4b7a444c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-push-pull-496.20180830214549.tgz","fileCount":851,"unpackedSize":1829489,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiGYiCRA9TVsSAnZWagAApIEP+QF9/sgSw9whyHQE24BC\nRl5O/xDrE6YBN59O4FzPWX5xR7kcbEx2MnUrS42Ctc1PVOYF7lAQ/NYv4lgT\ng0MeJBqFiq5UWCX5S361SGkUPJ1B4MJlkgbNZvTfRF8EIvEQHJblLU6/k1lS\n43cAwhUf6/i3BonpzcSlhi5IROsSlcWN7zKRXstlFcSYeindd/dJa7vw6EM4\nXXVP6o5ACIoWop7XLGEsomxZTsATkbxquTqrIHljyAkoPAr/FKnhK1yUPQw3\nXYEjuIcSVX5gWZ5GQ+egcsc+3nkbCEOkrdBBS6nZWknzzsch/+3jVXQh9kAg\n81iD/LRVlEHG6cf1EJ7eczzpwjnTPR+5Py9Dt5lpC88/ZXGrhujs2b+mMG+o\nCTehI0NovqL1Qd02WXrLd1KX3tPNRhiHHsmkxr3YTEWcrPenMESlovEceQbs\n72xyyQzmvq2/LWgM6i4nvKv8zDWdS+6YwS6KNek6HNarv46I7Ptq93vjckQH\nWCjd+PgqiKZYvmpE7ivq1XjnvoEhIM7gfvULOwwuczv0/LbFGvG+heDU65Ll\nJRyWj2G9WWT6mO338q1hRzXj2Suxijab8C7HeALFx3gohjvyoW2mNr8gE6so\nRiWM/LZQ9Wue2V7dLctvdHr3UcIhlzWED3Kro4kI+nKNyduHC8hnolr5r/XU\nb4x8\r\n=YRU5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCMDPlNAMMgfHEaZ4iJjzfQUy1eSr1UoDEUiZ/FT0kZsQIhANJZVlMN2xPLaHtgwTSiprBVUnA9d3CF+1BGQxS08ym/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-push-pull-496.20180830214549_1535665697149_0.8729908321360573"},"_hasShrinkwrap":false},"1.0.0-master.20180830215540":{"name":"@atomist/sdm","version":"1.0.0-master.20180830215540","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"d1f13d5fd3b6e7d11e92ecc774a7b0bd8030e3aa","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180830215540","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R936fLnVTQo560wNeMYkqPRVgjvXsknEBQOkvfpGUBYypVNiZwW5Y24ADIa3dozpX1+0nXhP6fo+zEBi28yz5w==","shasum":"de993cc2646ee903060e2dd8988e2bab64814e4b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180830215540.tgz","fileCount":851,"unpackedSize":1829482,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiGiGCRA9TVsSAnZWagAAD2MP/ijguG58D17mYOYNKbgU\n7ET4vmDkLr+x1s669JmvJ4jJjL1l2ON2WvsBvwpBmDrDtNOc9t/dYxGQF9c0\ny3I4DYcDzAu6k/KzI8UcD+2KvG/sAozH4Z9mU66sj3vgdptUFONiUOTynrM1\nBgpMK0InldKsgUKq6ENvoIXzvXKSeZKmCHExoVAojiXh7y9+O6faW1PJrMaQ\nW7oZ7Pl3w3/U9vFNeBfjM9wyksD2RoK9/4WntjDHlBcQMdvtxzOc1DhT3uxV\nxpacomDE+E6ntgsDl71/UcObukqYfgWznq5pfa+bSOWYJPIwT+NbcO1jes4S\nc/HrKxH3OywpFaPb746e6ZkItawiW0A+xuCnghT9h0FvN6ahCBHHOhQT3NM3\nsrDnob5A8GIj5pMmwz8KEGWwrG9LkxjOzjgXNbAOfgZ/53n6MWNJJWbNQ1iF\n2RQbCU7n5RCZRPNclq/e8JYKmIa2S6tmURdb2Lq123NCUZiWDVOncM7svR+I\nZkVlXL0UaL3NSSfgocnXezYK4MkxaKBiEWjKKPNttdJRXFoOgSjCBdNapKDq\ntKKulSyV255PHq9sw/bXcj/+tnj9GJmpMVBkyrnRT3aMlGxnlrVQpK79qWJ3\n45pdqvPilMGNru4Q9VTJM7zYFUCdmfSjCmu10cDwXLrcge4bokSFdHkn7q81\nnuVF\r\n=trTj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC23U4ddIaxgiWG1Cxa+8zyeerH5O7HguZsbKQh00BEGgIhAP1PbzlhdTXWa0jkx475bVGI3aONf92WYFKlbxKbL3Uj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180830215540_1535666309600_0.6647050075941725"},"_hasShrinkwrap":false},"1.0.0-nortissej.print-stack.20180831045646":{"name":"@atomist/sdm","version":"1.0.0-nortissej.print-stack.20180831045646","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6dcdc89034ea46b099e52c00d0ba38617398c574","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.print-stack.20180831045646","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Lxn8sNebi84ZBYs3TrigiqrgolzceGglnYNYIKFJMrDcPFLVJdGpMNQxFCqipHI8X3INEo/wUqCTD+VuOjDGAA==","shasum":"9db2952bcf63daf085b134967df0b5da5fb295bf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.print-stack.20180831045646.tgz","fileCount":851,"unpackedSize":1829785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiMsoCRA9TVsSAnZWagAAwicP/Ar+TeiN1ehUMHYYkCT0\np//XVkJ+kSjvxpaLI7PUTLtPNNovOJOaBxq7Z7Zot+CyjGGCa2QfKcTA5pZn\nbgVvrTeW3sr36Ud3q1hQ20/moaEkdI31hfhta8iyNJ3V/6gwTmcij6Qz9vio\nIykVZyDGZPvm5+MrTX6GtWu4JSpwLL1RpYZ/vf7WU0DN6IRi49apPc8IqlBb\nLxltYJkM7Y9Cr1HX80dAakBO7csL369G5XQDODyHaWMsk7i5gxxmJjT+rEzX\nbHO6ZUXhiBITt0yNvRevtu3zWH80kXyUVJ9nsjZprh7t3pAYH9oLOFQLEoJQ\nbxq1Krlbu2vWzzlvSGoT0IQ5MNkboG/jEtkJok7T+ti/Xvy52XQZInWM5g3h\ntVJQYJaQN1XAUgmDJyMkjZffxxqGptIJfdx521TgUIH7scVVqM08AjDx3xwx\nhGmNiV9ao/eUI0vojv2zypSDGUB7aKyeWQJ0AO639nYyWhZ61qSkpiXdnF9d\nSAcF1h2VZ9Xy3+2cusXdOJHfJLHfp37ofcGX75435FDQUkAd0LrXFiaWDrMi\nQR+erhtwyQipqg0s0R9V2VPCQlagmpuYLmMlqDTXN7ChCnAYrCFTnCY2LuVV\nrrpZnv7Zc+eOXLCU50T8BchmaegkArII++bURz6/ltzKo2DIw9rJdk8kEkvJ\nKTtS\r\n=AG4r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRE/KzqWHzAY5ysO3BLzM2AuiY9OmGYj9j1DOlkQ3MBwIhAORJys7ylQ7DVHmiJH2nEVwqj2klY/ibcLmjb7n014JU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.print-stack.20180831045646_1535691560165_0.05729121344441723"},"_hasShrinkwrap":false},"1.0.0-nortissej.print-stack.20180831051129":{"name":"@atomist/sdm","version":"1.0.0-nortissej.print-stack.20180831051129","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1af52f85ba00de5b5f0bf44aabbc03b7149005fe","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.print-stack.20180831051129","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vMzXBdxh36f4PBBpNBViXc2XtLmUjaXEkDKUt9x+7G9d1nexC/FhYXPJ4HIcCVu2Q8fpnY0/fizpqI0NudzglQ==","shasum":"7ff8ddad784fb63c7e5c030faea5f6f6faca65a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.print-stack.20180831051129.tgz","fileCount":851,"unpackedSize":1829890,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiM6YCRA9TVsSAnZWagAADlcP/iqwaGWbH878OE9qLAQz\niB4We9Sa3ESJLvHHcBGeDz21x9zIzCD30jQBMFvBv07oftBfTkD7reBbQhuT\nVg3IkUHzEKYh1tq3km/B082qxIbrkj4UIy48QkU5PzIbzKFPUZx1ruF6bRow\nC+iQr/5zK9Cok7kftMk4b1syTE2ZtXIlGvRAS8CIKSYSH6C1cRiNZA2nLT8K\ntRT4cgHzWQBeViG485ZIMmybEYaEnPrQ/R9ENLh9Tp7X3TAYBus0fNqY7KvI\nCzaEElcRRCSKk+eJad3wpCiNRTEYrjSu2CVEle+FVPHk0SfYSQwbBFMe2rh8\nyrfToX49wN9yHhl1FKk1KOl0am3D181Lt6KUc2/sr+QjdXZgp2ODS2zqvTXK\ngy+8m/0/VphxhZNyYSsi2MRyyFvhjlClhl0vb/Rv2+on+e+F868cjgG/BejO\nbfF3XVeeYHjzMtwF0bysRk0GGmUJPQixjnViwqIX3dtRPgIYMSjP7GDDsj/1\nn++YJ2giCZIDSuDXD2ABj2oWvLNFQ9aIq9EWkISavxjryfr3BfAmxoeUl15u\ncJGIEtAeLtOE/UEvmEKIw8ss3AatNj95J0ZCOri56gEBBYo4erMF5aBEfQ7y\nnUgMoB4TCH54Ik3C5mgJmDNjKR2XpyynqWsy3dWzZSHRR/+SaP2Ff6fN603x\nrLj8\r\n=r0MX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC6Yk0Rd+rMvcw7Ov2Go/fgyr57Pn+zYwB7xOXnCvxMNwIgKqyX2jufMp6b/dYQJaqXZ+h2NPTzqhvYg3MVxmfeR+E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.print-stack.20180831051129_1535692439536_0.07250035769118024"},"_hasShrinkwrap":false},"1.0.0-master.20180831051425":{"name":"@atomist/sdm","version":"1.0.0-master.20180831051425","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8226d5a264cf6b17f449d43a6043cbf6921e7bbc","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180831051425","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6sTjvQoM7FRpL7+TYPT+xaak8YfkwDn2EFEjlCXYd83X7VGDmS/zplcCeht85hE8RjsIAdXlmC2sgyW3PbwOIw==","shasum":"c4c0c5899bd9189c5110706e1db64d5fa900c4cf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180831051425.tgz","fileCount":851,"unpackedSize":1829875,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiM9ZCRA9TVsSAnZWagAA4RMP/1Znq1OvKtucNrYRTqbu\nLudJwXW6j54OYqPTbucvlb76OcVb/bo12ZaFosZOy1oVEvjZ/8tPmKGcqG+D\n3g7mrDt65c5mZETh82M/0e8ziFFAovLeYsf6Cl91eT0Dw4X8fRqp7RuGsmdO\n/6Ba1eTiraUicsWD5S8qYPiXY7+Y2E+45J1w9b8k7/jlkN0El2AvfTUmkMOX\n1wxfE+8qqKgEGv7NdAf9GEclTwvjA/V6tL4Jt2LF4bTZajFAI60hMAFIwEVa\n4pwuMeAguC1jC8roEGhCCNZUhhPrb5dnfJIF0UQEds52CoP0lBDewjP/wz2V\n9zg085ETeE+NnK3IRdh0dzrAT+QnCi+V6n6Y2Y9lyoJEa85QBPA/WN1LctJI\nZQgg8XF5Q8BYpShfAhTgsX7BgDBhyY7w1Ge3/dfvlNz3lFl4cVzAKoawOrUd\n0kdAx0I2367QIfCqvRCZp+cZZZ/6tKJ/Jn3QB3QwCYavw2LZ/twp8KPMblUS\nYd/ZFsCyIC5LmAujzKUvBluqjlENl81EynPJqdWg+7GlvrH2Ow8QafWzeb4H\nlYkFIiQjOh0Cz1mor+4IKUfuZ6SS+ksMvT9Y2fcIj7nOcJmUrQtSAEtlDVad\nN9c6kbzFiV36yxpwTq9V0lZyBOpxoKHu5gVzmWtlodXrLrkeq/xyoBtTeDsy\nNjtT\r\n=xfeF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOWdGlfm7XI6PBmj+BCHVr6oBB153mjhzW/mCXOc3P5wIgV/StkySxG3Oz9MO36XcUTyatFPW7L0/KM388ZVaNfxw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180831051425_1535692632751_0.9439976682237778"},"_hasShrinkwrap":false},"1.0.0-master.20180831092458":{"name":"@atomist/sdm","version":"1.0.0-master.20180831092458","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"567fae96f19a4beddd10c9ffe537b911b7e2f10a","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180831092458","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wOYXPxA+BPWAvg1DBanNo+m7Lfvk1baHs80UBGFah0zR3arlIcqBr4t/+/0gdj5uIhf/H1sESqPSu6BZnxCChQ==","shasum":"424a6a452df2f70f97d435c6088cb0eca8a12e92","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180831092458.tgz","fileCount":855,"unpackedSize":1831484,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiQoVCRA9TVsSAnZWagAA7wYP/3byKBOWf7MrrPIl3kCr\ntNPdkNJXFUTcgym6IgxMT/FUL0S90vGIaUTDBjPVUGgyyz4Ak7ZnNOWKmeQH\nuTR5SQElVo66rVxFPdljRf/4VClTVn3fkfhgNFpNpcjGiwPTH6RqSOsbVyr7\nEtvUJ35AYgNc5lC9l5zSDzQ/aUms+iYPISD3AfyBcp2HB3YqByBj28sSQhaj\nBy13CYdxnLQGvwCJ6UBYAbJMyIqWBOGfeC4s7IKU4wks5wytWMi+nbJl0y2g\nwByTOs+3/qBc8F4rFnBXgUF+Wamw8rj2Lj2zcE7/T8BPnNfr+TyvAUoaceCw\nycmqhuMvUML+IsNZN+7AegvuqUxYMhg0YVAOk2X5s4hpd2pzrO80ITBkA1eI\n/JB1C4g8GqpExNsL+pvuOv+Zu/ybbH8VoMwEERRvolL+tcmEGZvxWmE9qVI0\nL4ctRpfkupuPUjNm8EtoPB2RfUJgGzu/DY74LoaTe2DyZiIIv9XLz89v7Q2e\nYXmrqF50o2phfqz0VY1TZQKQ4YuXUQ24Dukj/E+PLRlxawGJsTT9ZpYc+btr\nz2wxsqaOWoY1Juar+D1Ze7yinPaXU3jRA26SzEgkGrW7Fepy1jW0F1ihTUQu\nVY73cWxRJDg+BscUTMKeKLCpTK8LrrOJPp2mCl+gEJbxDB+Gdn4yBC2fm2NR\nLhji\r\n=19Il\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICOmRSXMjRoXlz7HatMP3DaeTfoOJeRvrkQpxzWwcGD9AiEAki+KuCmn5bw+XFofIeQVYAQiFjO+v3WBtKCmpaHzCng="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180831092458_1535707668946_0.7363511230724276"},"_hasShrinkwrap":false},"1.0.0-master.20180831123943":{"name":"@atomist/sdm","version":"1.0.0-master.20180831123943","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"eaf2f60cecc51f2b8c057307397721f7d3c0cb96","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180831123943","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kIyvu4FjHv7BuexAj6UnBfmt8iemoDuaLRigPU9CNRhYYq/+GmL958a2BkSMYE4CPaTHHfafmMG7kw5XKjP0gA==","shasum":"0e0e58fc1c738e58a3c01c9b1eee431d4bd5a8c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180831123943.tgz","fileCount":855,"unpackedSize":1828036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiTfLCRA9TVsSAnZWagAAlhIQAKCGQp75NRlEpkFkIBz7\npd2QOeam7vfiqTGnEg2esMGJFTXTo+283OAqAn1eejN9QrbCqFhuwkdZlaaX\nePw3gpSIeSuLhs86MXbbX2O7gC71jr6c+tJQDo3TJdvWyQRmp3BTFuzGe+xk\nGj5oiq/aA3lTcKOjVL+78aXttzjx/Hw4Mu8Iae13xXybN9rRanX86nasiMPv\nKpKZNdYhFNpw5bVwk1Ioe9FomHo2d3gBgo0tZoIwpFSttx16/YDMyYdbfZxN\nGn72/3tNRc0T1e3HS9lDonXUF7ixmQGkKFQ5FteDSozi5PyQEECK5IEiawIR\nP3Ia5Al3i7iRET8nquiURXpOL5o8hzXN9jJ3iGIFpw4OT0er+l1k9JERTxFr\nfxYqq41pdBN0L2oT4kdtR4ErvpGsw2bwPAa7+E7TGaUd+R4hKa5XA6yur25c\nd5HZUxomKGXg3y2kQ8QT3vX2VWiWhWB5n/AGrW993rqgnUQka3nMT7rbGMCx\npu9TAQqm3IG4Md/imuTilUDJPP79kJZ5Lf9tqam5TmlNDeVEsZ2HJJxbu8QT\nbUs1YIXf+wkUwfOgab6jWxx1vn7GFDvFvFEBIwjyGy5htlMEyqNHzTHBUm9w\nyGjFQzN182az8rbIHeNvZm/o+Ie1oFnAu5k9vbyL1zGpxMNjCaKyzNYEyoq3\n6khU\r\n=Qi56\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmn5dSTt2KH4hSj/5Jer8MQlVRj6dy2NXISK1Y6MzDnQIgXXZlDFEnrFhoIuR/3zdNFQqb360XBkk6fo2p0tCmqs0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180831123943_1535719370608_0.5488076074432529"},"_hasShrinkwrap":false},"1.0.0-nortissej.first-push.20180831205527":{"name":"@atomist/sdm","version":"1.0.0-nortissej.first-push.20180831205527","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8b04e7736bcc177854a03d9c5b51a0b3c424dca2","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.first-push.20180831205527","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C13trqs3Fo6vUH++btOwtqZ9H3GvcR5Meaw0WB8CiJpFeMJm5g4qjW5JWj7PRf7/bwir3uGymtHzzkJA67YR9Q==","shasum":"2b073ade56424afe0920be20947885612006ae16","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.first-push.20180831205527.tgz","fileCount":855,"unpackedSize":1828206,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiavaCRA9TVsSAnZWagAA61AP/j91givETsGmbAJh9yM9\nNvgiyWknBlr/cshRbWs4azqNBJ7TMtmRhBXLryXekwUlZv+GoXca3nw0CQBk\nQuq3EteMM6v3heuuf6rfoDoud+SPutEM2rPAaAdGZXLMjgNAWORCAqbV+u9+\nrB2/LSnaGX64YdQH57HOjlq+GMBKjViA5TKxZUrqnjADZ7t9NdQUildDdb1+\nUnH47jtjzpBCAuHvdtxKFRJoGKzJm69bGNLgusBoPjLlkE9+1cA4R6q7BBgt\nYxtn7riI0yQXNmnNAnToSjA3q4b7KqSVDbJYwKsMkLAzSKL1iq6jRrylWTei\nToK0TUKOUlPn4eKJDmWb7wnqDbikzT+LSMbMJrGlb/S/STRwsxDzoglWDxX1\nyYmWkIqZFR/GwDYIzT5kw5SGSB4s8ZW80ihoXeYIHdOgkrLDhIwG+wMxPlIN\nP3Cz8wtfx4SAKk8M8XpjdmFuTnww2nf+8wTmakB4sZPLC+icQ0FufQn34NyH\nJ8h1quVHewgvQ4Cgy2t28sGGyz1dYhSjdbIr33CVriVUF9ve+6sh2QXcgwM2\n/0GA+7KGoqRtXeUFZLIdkFmwCThp1BISOSCZWi0wkIkYQLU/QBn4B8nMd5j+\ngDRh4ZEca03urd7a7LBQM35pU8LDJKGqhJMe70erzpew/09WS16/D9PGm50t\nZhqK\r\n=LE8B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsIfTaSoJoomq8wxImnJj/ucVdrUBtNIsD4QDMAO5CBAIgJRcjoL06JkHQD7XPR6dfbra5o4OEdQJuff6EivOZEwM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.first-push.20180831205527_1535749081457_0.022045757117099107"},"_hasShrinkwrap":false},"1.0.0-nortissej.observesOnly.20180831220937":{"name":"@atomist/sdm","version":"1.0.0-nortissej.observesOnly.20180831220937","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9c775165e3c711b89ea72b4545d14e739dfa9757","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.observesOnly.20180831220937","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sz6He+gtqZplF+7EO28aXiFeBMPCfbaKo0X+AATAx9+VThK9z5v1+aXd9EKt4M0VSfXjijAzXBs6spgJInFWYA==","shasum":"a6a1bd5e3e3ecfd13d9e1dc026bf1f9f4b4bad91","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.observesOnly.20180831220937.tgz","fileCount":855,"unpackedSize":1826715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbib0xCRA9TVsSAnZWagAACwIP/jGBIDYpTH7YduKJbaVO\nb2ZRp3qlpvr9gUWgcgk3019Ear5Mre+8zIRP3WLK85xMu+hRE3xWeqyKCVtg\naUDHLfnTK21rjONQFFHe3FCxZPI96T6ocVkg38K9Rz4VkdsxsTed6i46BSO2\nX1bWhDK9l1fu1Hs/AOkuKxLFCrCgebK82rD23Spy10xMPZvtTcWePrV/XbAd\nmmwX8ulGCgKg44tKcoc4hPGIwf6E+STeFBwQE2jxKaGp7hS3H7kkZ64N1r3m\n+0zt2WGmSUGTLJVwIrQjSsK5glFf6mjLz83z3z1MldU+yJHfZBvZnF8G3K86\nEbFk6Sj0YP09F2XKbAgUPXmgn/LciI2+Rn9z16UH+i2+WGziWsf4KqXQhVVm\nO0SHMuoJ1DE79VsdaEUOsj+l8D8td99x9mwkiZ06Xo/Ymrg4qDw/6E7oBZ3O\nZhG9tMYHd3M2u47tCosVj7h/Szw8heJrxJqFrbdMjF1J/fq3YTUdb3rg3tYG\nqUU12tpAr38h6XsGlzOEHnkxouCjS43EOCY+DE5fbyXem7l7btm4nbS0IXFR\nW5GZdMg+X01mDrBFGPpgiGuSB5HoaM+GAxWbEFOAG2MRM5gCAL7MvWJ5OaR4\nZapxhaywWkPma+zJZIwmloCMEMARlUs53tUkPIwp7PP8YOPN6iE4UEmNSzzM\nFBPR\r\n=cz9X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDy/pv8Dmj2jdi0yeU90yQtSPkXjVpI3oy2dmD5eKo/UAIgClfCanMTdAq2miVRPmABfQxdlAoqRxU5IFqgbcPj6Ao="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.observesOnly.20180831220937_1535753520499_0.9634539709642991"},"_hasShrinkwrap":false},"1.0.0-nortissej.first-push.20180831225158":{"name":"@atomist/sdm","version":"1.0.0-nortissej.first-push.20180831225158","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3c420a9b2443947bf3005b19b151fb8aeef84f57","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.first-push.20180831225158","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2VOEO0megpsauuAsVCVmL1FHUjzZVHwrQGpvYuMPiMIVceMVhN3UQq2K9Rec9ighbJUjCX0xGiLI7W/UMIyY0w==","shasum":"322666c8b48a2cf1643121bc8a1593af062eb218","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.first-push.20180831225158.tgz","fileCount":855,"unpackedSize":1826738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiccjCRA9TVsSAnZWagAAp98P/jvIZslV+PUTIg7P1Bj6\nXLztTMUNyCV7moX5fDFWxE77uJsSkgZRKA34HkW9WC5UsHfyX3lPLkVhBKNB\nUYS7w3z1eFlpdjENmNzhKCLeKGcrr/NTlRRpUYrWRSs2Eh8jIcmjrwU3NHjk\nUAS6U+LBlTupUP0Jy2rKTdrhoZCItvJuvHTRXMZqfFLcVeMpvOhzGuSsZgsX\n06iTD+2iH4Ni7NtPwPe5ob8jgyDJe9bLBkOyey2JEYeilYIstOG6es1ESUFj\nvmXhGU6hs+wFizD41ZpmnfEB9skKrn1m0Lt8J4CqZ4/Utxh0HzAv+VCKsogU\nv7NAR1E/fn0LkriepcpEk5KOmrU9Tnvar0zBiywylJhrXChVVafPufDkgnA5\n8cgeSQIs96asKSx99tmC1C2kSa9WsdCrjZdF3dIVRf197gkYe4uArdJ6F8jg\n+ZWuJHlAxXYtikGvy6UlGhJ7JGIJC109Ua9e5p7kEv1iWL9ywwYWdh+5BiPB\nROgkTHdWRLFb6k/vR3UCWQk2vd9B8A34S5VQDC24BFYyMhZBLurf92UHzgen\n/YQ1X/kE+cpVmMzwZs/5WMcP0L7i9+86tz27BKhxiBf/g9r0004yHcglFKg8\niYjCevGAAT3PJgoRutbdzUni5UWUKdfj32kjuWiiMEHIxQwcQH1NU6xONZUC\nRblZ\r\n=VXNt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDdtV8cELT5KHCrL6lBzBw8ELmb7J6/JuZTxwZ84o0u+AIhAMXNISz/pHv3QOHt4ShxGhl8Pa3BMnGVRUuzd6LZq94O"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.first-push.20180831225158_1535756066422_0.39093638566766886"},"_hasShrinkwrap":false},"1.0.0-master.20180901020009":{"name":"@atomist/sdm","version":"1.0.0-master.20180901020009","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180831124448","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"80231ad455cd072e7c8d06b46195bf10012583a6","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180901020009","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oCeMoCTmabXMvD5peP8ZfLkod6+jwcURk6G1iZt4BJQf1Q4A4EvNTKZxhztYRuNf2xRliyPGyvFxtFZZqnVHgQ==","shasum":"b94ef65d3a17dfb45eb923bed544beea1e3cbf6e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180901020009.tgz","fileCount":855,"unpackedSize":1829966,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbifNBCRA9TVsSAnZWagAAKdcP/3/AUc2RFJ4hLBHqMmaB\n1Ky6vey0kTGfp3QciBJ3wBywyL3Z6i10Zq55q00Pk7KxEwIc515A3A6ib/iG\ncI/UUmQDdZTkWFcWrECWmTHkBCBssVkfM24NdQQxBS1ADPyDKnSaaY7Gmnvs\nOMaEqAWM8+e4dtBtyPxzqoYHEsEoRjrcrHASCAORfoFxG5CBQcjXmVWkpBTa\nraXZvzXPj7qwNk531kxuAazGSmKBzzSFMeQk6QkfaRNw8BbhbxGYLcUzuiQS\n/z36cdTesXPR9SuxJhc0aFzTU65NSARd6g886jOhyyi7rDK1gH3/lK8dXMIQ\nKr2+UbaED0BmVvvACLxbjSqyXMS34zJYy8RGGvHJ/8sfQULzFSUsy2zlpfHT\ntD/N+Hd5wLQ2iQNlrwIdEOXHZ1y0/6wXV1oQozDubcIdbPpUGrOjLPlYkpJ5\niB8ArsI1fLIc7cVxMA1ZYk2b88Yh/AkU7rUF/yoIGFwvBuW1kXo6EORqLj3N\nk3I8M4OmC1NjnLx1QFDZntnlhym9apCu3r7XhKFQCdGb+IhpChpYaXBliGan\nS71q+dT67Xeb3osgmvyTS8kZGqVtLH1hweuPcI6OPOGULtMw1dX2SATzkPPN\ne7/b3kJhE886/0yXgaKToHjT75gblgXilkGVRgdQ+rZWGXHWpatuZ5drbK5M\nhWn0\r\n=E8Wb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJouAgzAbm2NTgtM9xG89Udx/x4J9jb+NS+Oc36rPCJgIgQZ4kbT255t3I+/t3gBz8SySvCaNGGUfMV9801Hpajw8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180901020009_1535767360192_0.2480792922908721"},"_hasShrinkwrap":false},"1.0.0-transform-transform-testNaming-1535773767577.20180901034944":{"name":"@atomist/sdm","version":"1.0.0-transform-transform-testNaming-1535773767577.20180901034944","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180831124448","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"afc2fdb055f1cfc33781bc743051154a28ff1459","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-transform-transform-testNaming-1535773767577.20180901034944","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F4lecdAkoin3DK1TUMq1hziOSFQMxK/71UxZs2oPj5qw0qq6kA3r+CTPvGcAVY3+anEv5cE+6poFvTYeg5Q7/w==","shasum":"6d4b729dad904acc4e1c095e01a1cec08dcfb5a0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-transform-transform-testNaming-1535773767577.20180901034944.tgz","fileCount":855,"unpackedSize":1830004,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbigzzCRA9TVsSAnZWagAAeY8P/09bJmy7lCyRgg6RQG8n\njYiStTTjAZgwhpY8wGxvva9AyTCgKsPrSqPMXa4qgfz3NjfkC3HkAOdrPfzj\norgu5C5wQgDpdmyScXJDUZzs/n02ziykLq/6DbvneGNOJYrhdgD+RODl6M0P\nY5eE5kJ9Typn+cooNhpClnQxBRoaMEwMfqhDGcOWW/QDGbtvF2Bt3YZi9zJS\nGcRn86CipZGBN/Hn+32zaOW6AFw+C9k71ewa2F/yy9ewtPkslIqNKCOcvBv/\nm79FovticrCVSjmMjPkD/HcqgI9f5N+x/9vZ2UHuHoiO9589BdRETNx9flbw\nxifBhhtWLPbtieJy9VjN2yRkmTpXxfDClfrdh7Pym4QawpVOryJBGfdDAftU\nOCWZWtp+rtA0HpaSmv1P5lfs8LEjVSCdU+NkXZL2o7H/qrGwWYYosBpq1uM4\n5NGFRODZL041jdKrXAUOIdB9qI2gfsmcbldCtEYqaPqM+RxDMp2aC0/4ZC0D\n+n5syuMrhp0LEMtWfA3J+rEm7rkVQ4rjY1/Dj49OXztpUcDjz3EZpQSGoVeX\n3yHYh3Xzwy70H6uFfgp54C550+F3a7HHZBqG4y1acKU0v4ZULGPT7IhNVndp\nJGhW5HbY2+3Tdx8wBFnY6dwWwhEbED809Cvt9jviE84S1NiupO/vwSsJSY4v\n38Ls\r\n=evmU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAr9oUnLnqPjgSDaG4vWfk7A/mcdPF50UoyXjdypv/RjAiAHGUgctXfIUAgoXPa9hHWZFkZ3tlJpBWvtVOi5IT1C/g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-transform-transform-testNaming-1535773767577.20180901034944_1535773938292_0.17001061875925472"},"_hasShrinkwrap":false},"1.0.0-blog2.20180901042134":{"name":"@atomist/sdm","version":"1.0.0-blog2.20180901042134","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180831124448","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0be8f83bfaa4abb83dc9d081914e6917e3b4a172","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-blog2.20180901042134","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cAGDrkQiv6F/1XYRCZTkaJpaZTFLEqRfgyTT+HC5pURhl9KmZ46YGr5mC8VhqYg31kRlDAeW4yxJSqy21VDDlg==","shasum":"e8025e2938ac732a46602c54bfc2e7efb3c73a1f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-blog2.20180901042134.tgz","fileCount":855,"unpackedSize":1829965,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbihR9CRA9TVsSAnZWagAApFsP/jqvPKbu+bXJx8Hhh0Tj\n36PZ6oubAuesGf8YorBiVknjwh2S0PdvdagnMJYg0B7EbY/jmWKnLEZDxeGO\niHoGTas+OOjHxpNAe4GcG43kIMS24fafYvx4JOTQwUUy7sShh4wLPm0GROE4\nnk+WOxbPM/sTxNHpDm/dhKNxr2Q5ggUyPAaT3kIzjWj7zO6uX6hpw9acmBWy\nCiAtX0H7z9WnX5HSBE/8zCCp/8yzG+acFunoWv5eko66cMJb0ttAmU3ZoxvV\ncxbreeKMoNhWZmEqbxTBEZK16W5baArixrISZ2rRsavf4It4dBuRAvXpXtyG\nd1QsrWvuapIX56ZkvpMNu3xgpvg95sg6qzKnEZlO539ztdDrX8o4CzCvLZ7f\nzWpz7Sp7fC7Vnljx/hB9aM5UFxrqvc0loNQ1Z1DjgBFkfUnMoY7cO+CM7WDw\nT3YwDl7nn1J6NrgI0kUI76fMvMU6SdfqaPyMsuL/+1UMHM6vyqRzJWXFRoVi\nDE7Li6u83J0ALEzeCLGqFHqiBSRyS84mfl9Pq2K8zsiEb4/lZYIZxVWWleqC\nzUMltboXnzIPWjW7zcO3hmYVCfXIBYK3Owy/Q5olirkZ/njRl1H75k+f5dQW\nI+oVGDoJP5IzBnH4CSwntUgCtFtioGkff4WlbCj1IUiUOL7rOPkK22Y395nu\nIlui\r\n=gvoY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE0UpwYh85K8BX6Z4C4+73GwjZt7dv0AvrUqidhjDa06AiEAgTpnp6+YUtI3ZcN5qGN3GUL8owyJqo91BYCdnrVj1fI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-blog2.20180901042134_1535775868394_0.6739684621705371"},"_hasShrinkwrap":false},"1.0.0-blog2.20180901042248":{"name":"@atomist/sdm","version":"1.0.0-blog2.20180901042248","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180831124448","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1c060fa4a39cbb4fdd153a5893108d7ef26e2670","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-blog2.20180901042248","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-etUUzHGtDWj740+JFJCs6oN0jubXTVfPkHwR0N+UKjjBMkwn9tmwTCvIhoqDccSP9laN6B88+3jkHJvfLMUxjw==","shasum":"57c9423f1bdd375efdc857fb2c31161ad603e74f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-blog2.20180901042248.tgz","fileCount":855,"unpackedSize":1829965,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbihS+CRA9TVsSAnZWagAAuywP/3FobFYlP9zCMjBoLcWk\n6Jx5Nts/OXEB8MFZc0IC1Exp4SyYiuRKpckywPdOxk5+V1xUjexccbh6bxbA\nV44eq+Xai4TCjvQ8uTmpOl0/8G+x9kKWkZu+uhUfEGKHTomcJUqquh6a77bq\n/25KFjkAGo6vvLWY8JZqe9wdBuxekIx8L078vf5tGmCZVOogRXdaLcLBrEhM\n3NKuIovuzZGMoR9dBluTF0wprG73NgVgP/OR1nyKJBsVYjkFop/Lf/Lrn6FM\nu9WnVGlaqiDZmQr3OdBZkQeH8gf+FK/mU+qiHR1Yt7KfUwSO1vDNKy5kmzNa\nPdb9I4mpTD0GHCCkrUY8cNtaVhRJL4+MKONVqaa+vIEmVRXXsDpX+lDgIAjF\niIYOczZm13HsN4kHK066yNCmbRxdcs9VRtxcZAAdWiSwE20Xb6llm143W/Rf\nkZ9xFJohsv9oO0C02oJ7VsTXLEc816KD+w0XOaIsL1zlx/8MqnvYXt+jucce\nztNngwyDDoHGCCAx1LR7aUiHutZmt1PoZZUgpbdRwtgccA+bXK5HwgiW+1oU\nlQWVSLVpXsHoAGWwhL6lSRF3TpQR1oXD6dKvLUOhO7OWPLT36nLowgQolmTG\nO+t0Ibn6UdKzdmkr4jXG1qCrXHkilBJTJgWVp/aoyU0Hcq66GYU3pQo7MreP\nQF+9\r\n=/krk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBg2Kweh5OSVb3LzjPQ8r2FystIl+jKCoaB7laHBBt9ZAiBRuKraBRyt/Vr4ybXi8EXaNMbT631R3WOCuo/bjztGSg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-blog2.20180901042248_1535775933929_0.5848619050674575"},"_hasShrinkwrap":false},"1.0.0-master.20180901092113":{"name":"@atomist/sdm","version":"1.0.0-master.20180901092113","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180831124448","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e185b4998677921baf946a69e2f72a2f8e1abcc9","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180901092113","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XjcuUIdkeDiKsptBKpe6O6hJrNgaTddtUm0PsP4UifsM3MXpg7ZisX0YGXTJXgd/Tvan7YjH1LVJ3f30AqtpLA==","shasum":"b6eae1a4c141946224e43cb923b3034021a17691","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180901092113.tgz","fileCount":855,"unpackedSize":1829966,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbilqxCRA9TVsSAnZWagAAZyQP/2Q53EwUXV1YddbAT9Fn\nVIgsiBinxvhCdmX0n6HLGEcUXVg6WIv1lcUa0026nN8Am672tlCzMesXuI6J\nNmU95VzK/FCVuVs5p5GjuSSBsyjQBWZlBylsFEJDBDOxj3AREflvIU3MICEK\nz8ugOaJn2QfLzbgV/w0JI/aC/3q/E4lnbO0/lbklVMFjdwwYiCGlmval5oFq\n3QChC0iOgNEyL8BL/LBnWUt2nH9z/SvDr2QIMP4NgtKLUq1jYhkdE/zV5N7z\n0r1spTB2yGfdjNAj1jHrSuofwYE/e+ZS4pltC4nsQHpjDx8eEQXX7Fy7LTl8\nGoUxBenyjTQKUqhrR5vfrwMLGyuiN9QhCcPw2pBg3JJHcALOTSsJxrEtfTEF\nF9TGfsrif+QzwlCHaSn0xOfaUKwaPcWf1J+k9gBrRolXupgm5s27TwEqONUs\nCUmjNqCONSyb/ZgZfMdnft2tgSm4dmwaBGvmkDpfChIh5TjAZHnOY0z4Povg\nEBMFYjVR5cujl3MXR3NicF5myTV8cA17ZEDDEEAtlYZ/XNCt8oM1WniBFtWB\n9j2ru4j4rguzV3+K4zS55pzce6TreXekPib6bt5B3wDlmPSml4SdtzGYPDei\nDJH2NUP2hW4khKQIx8VEh4B+MczDTI/NS/bfixOxhq1lmQELYAvc8nsV1hx0\ng5nR\r\n=cQMv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJgsnbOoou+D88Ds7D7zcuhjBEt46gT/2Y8X2Tx8Z/1gIgPWzo0pxF/xvRATskQoVjbRMcPf7UjYjYdgCH9kL3KXY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180901092113_1535793841002_0.840034140704087"},"_hasShrinkwrap":false},"1.0.0-master.20180901121655":{"name":"@atomist/sdm","version":"1.0.0-master.20180901121655","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/1288405181","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4755f48c0765b89406c689c4655074350b2fe2a7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180901121655","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F3omqG9i848SfjXXNawLYa9xV2LB4xKdq+n/kgGbBPuZQSIwKOKGGhWrb998lYMMNeFHM8v/RrBWlMWcT7JOhw==","shasum":"194753717985643012b64a14c96397286f453d98","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180901121655.tgz","fileCount":855,"unpackedSize":1829456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbitiyCRA9TVsSAnZWagAAP2IP/i5shCw0EV58xvM/qBFM\n6RnfEHhZWrjP/ChKNTFLdrI3bLkBLaoPOb3NMT7+Hp3ZgYI9n4oLXKqRP89P\nGjrz50yYsG2dfRxJhERi2Vngnq8C5MH1bypmzsJCdeHkAO4NmrJwKkMuqzqL\nodJCj0tlbvsMFhN/DRQPH1O6mXQ3XcbEZf6lPvb6oJ66kjLnDHPgB/wd4Mau\n7qZgzN+uKyjOF9R+hOcc9bTumCS4elzImbPTO1wy41zzX6QqFDeDVj3J97Ig\nAJuKttoi/X6UU19LrCVCv5jPP/wh0Vx/r4zE7XtcyChchK6T9ODwDfe2W+yh\nRhQhB/Bgqx0C37Od26/iK8DBlp4xzkv3wjpwTQq8qZ5CmW83tVnGzcvwL9q/\nCVN9PO0Mk6jDlqZFf8Fiq83bc5+0MDlU1UIk2dt9YkuHnyFqmmPD9aJ8z7BW\nGftLOO6PYPop5blWEXfeRDiBqgtODfgaL+wRZUCEctDQz5Fk9FXWfDBDa189\nK/Ssmv1mdvB0K2qKi34MQyVm8j9b/tX9jfhpFxH0eatyZIXPu6Y6mfVdlcyN\n+hqS7Nx4q437UiYej6sXrxKmdoxTCK73PJvVHia7T1jW4JkBg7dvSqqKGR6V\nNAKEB9aKPtekLAIGUbMTWUCZbCGp+iuuZehXbojTnqxVNvCqPikXchhRBOJu\nNyPz\r\n=TxHF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmRWRmMgahAtZvXdIe5AkbTg1H78b8/w2x3P1Gb27R/gIhAKbpynwVIxS442+OAX9uD9wlEtXv67LX4FMP+RQWw6ej"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180901121655_1535826097829_0.1394086093500999"},"_hasShrinkwrap":false},"1.0.0-nortissej.observesOnly.20180901184855":{"name":"@atomist/sdm","version":"1.0.0-nortissej.observesOnly.20180901184855","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/1288405181","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e4a80249de9d379d7282814e951a40b9c0d12597","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.observesOnly.20180901184855","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vuC3fnCWq+v1afAUUsP4QiJxwt64kQLUN32iwQWe6wH2XzA21NbFRLmoYPo7cv4Nk+ZvI3s8/0F3fsfs46nBmA==","shasum":"a22eb4902afe4a82cd5be9dd32eec7e43f16a6cf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.observesOnly.20180901184855.tgz","fileCount":855,"unpackedSize":1828070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbit+uCRA9TVsSAnZWagAAu9AP/it6qDFrno7v7w56e/DO\n2rS23vOk/EqD1Sl3gqHGu2whfG4WXzoBy2Xvgn8feerC0SsHE7oC6e9IrxYT\nnJic9y8boDRSxIt1hEG0vIQqrB2UGj+tq8BMZNx+aCQidKAnil2HO/O78OX8\nvSqPyCVQUfH00skpz56MwV3YhSvqxHNeuuBUSYxMAgL0483APb43SkdDrJhj\n0zMxqUDSBgYafUtvD4LQpFpQwtTL2xDZgYM5tI0d7XuQrGSyqbO4jxDPYzpo\n/j67DyzQeR9YMZmVWUYnOK4jJ0L2UQP8tWi/NWINjBzR7Nv2LShHjBOwDQyI\nByc9oyDttTLhVy69YSasFoMS2qTYlIZ6yGFuI1XXki5ry37G7EL7vmuUD7Fm\njAdYMRXcGVIMZlSl6lBBHcboMZGBjMHGBwtPbhcOh2/wP5Iqa6jIUmlnKzb7\nl04bgn1JNos0KOW3J/ki+wuo63c0bu6AnNZWWvelRgvVRInzaFhPxWiaEnsd\nlM2YVWP8aitMqLxKwkqhsPYM7hnfkz3emlL07E/6q92fe/MrH86nF9+0WFtl\n4QJxU5u/Ko8VUNLIT96sBZKnGDpD4uiZ7dqw4bpln1DzNUDT8CctY+DlgBhu\nkzzJfpAqta6m5kCQqcbQnwzynjwAx+BRptPqDCA2WO0AQNOo9w0bZ0nR5cKd\nkkFG\r\n=rg7J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgyfsDTm6ehqDAmHOiYGhls+bKTjF2EIUj+6zcuxnB8AIhAIYtaBnBheP/Jt9bHgDAYZLw9ece2E83nh8z5Gvn52km"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.observesOnly.20180901184855_1535827885242_0.010480804170535762"},"_hasShrinkwrap":false},"1.0.0-master.20180901185236":{"name":"@atomist/sdm","version":"1.0.0-master.20180901185236","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/1288405181","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e9058eb6a7098c7cd6f7be6d59f87868484317d0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180901185236","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tGDaD9PFN/AmNIn8f7dOJaEBHbq2xjx5Q6YLqgeXfie9HQWRJsOcu0gVdRGjPH6kYCD5wkpXSXilZMO8d7AIDg==","shasum":"389a07bd0adaa9766d6f7e66bfec77f1f30dc3e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180901185236.tgz","fileCount":855,"unpackedSize":1828169,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiuCHCRA9TVsSAnZWagAAYeUP/1zUlMEzqBHmX6FKkDSY\nIY7TP65xi9rX04ZSJbH+F5WjIGyVcs39022AB+jCo3+APhFWNEDE6Xhf97tk\n8FPUGvbfazF5RgsHJbkdLG133a1447UDRAUTRL2K3w3w3YN3SxV2WywgezX/\nH80bojikTzwTCcfMZ5BOGlkwaOTVXBnkwqxEyIOmrufnaU/3VFjma0aAL0aU\nYasvAjl96xhW7E/LLeHSrr0IHDp0HtcTrm3ZSA+SXm6xZ61DqYPNFIeGIXhh\nthwR+T7KXo3cftiDKiORXOrMor/kyVlgp64nSo4hHl74BqPH+bZfspGH44nL\nKxDGgrTs+3et6Xb8aoS4BKYX0Hgr2GsasVw2sptIt/qDfmbkCQgiUlVkLBkr\n1sQwhF2LBCG9mc2rztKWN5HK6/tYw780jnvmTDC/9HzqskT8cjTJOV2tSJC7\nkDz1/HCokO9Qkt3oQul3S9ngbaQ25gd2Smva4yfzWQ+tEJUMEb1O9tpZXsJC\naldSszZ/WHZy9vF3TUhKFmL6r2NuSNWruK1HftHxp00W3NV/X3RzlSVhtg3T\n2AlnIMZ1RSi+Q94kSL+8Zu2Rbo5UxdFen7G15Lb2DihV2plKazsSMIUvuUlR\nWs5afce77sO7wX1wWz8Azie+aFO2wTdozTKgBxeMZG8rYdMG+FvN243ZxADY\neoN9\r\n=fxqi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB55EikaZb0+Py8NqhITEUnEernbpfOK9Pd96rx9b9P/AiBqmOhYPJs0j3rWtGC9wDvqjtTdNCtAC87FjSyNyNZWVw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180901185236_1535828102776_0.22155571081923187"},"_hasShrinkwrap":false},"1.0.0-nortissej.remove-deprecation-from-client.20180901190705":{"name":"@atomist/sdm","version":"1.0.0-nortissej.remove-deprecation-from-client.20180901190705","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bf658a56ea6e3913ac3f2361ec2c0c760bc333ea","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.remove-deprecation-from-client.20180901190705","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qtMWSzz3ptRslllU1Cr5yODTnmv99HFNKJ7u9J32wBImoST3/8YcW4VfysYwCoUz/iM6Xb8/hGhmITyxFubbIw==","shasum":"77813a72c4e6b9d3255420a77d71ac1e107db81f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.remove-deprecation-from-client.20180901190705.tgz","fileCount":855,"unpackedSize":1829278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiuPZCRA9TVsSAnZWagAADmYP/RuXrIxogq5Hl+GGEyoO\nmXEMiY56o+8GtZdIbqun7njZT57TsW8eozWUKSeSpdf933JtsBhJDAaBd2TA\n+avq/BJzDHLsNaBVlHzezh8ycFFC7OaOjPJ0g4s2wJG/X5UnqWmcqOPUkOGo\nkSHFaYWTkKovIpF0o5PBijLfMQXV5tF4kuezUZzH4+HT/EUkH7GpP4PRqP5X\nYTtExv23fypXco6OtwNWUr1nCyjeIQ3+jpCpWVHphG5SbkIquigYykdDsWQI\nUDON3LGmbbOsU3b6F2OWHRAM042hATvQ94/TVlFeeZopUAcUt2ise3B9R7k/\nHmFPrM5iz1aDxHo8OSexWpnNSWKQ/kCUFykVktpsui3KAGQuFwIbkg3CBBJ1\n+MJUGIzR4SizbT9EAGxmUUafofjsOWmgPzNtFEhOu+4WOPDChmFHKeY7QC15\nmdIX+4irkUfzXyVhHLTVPGZ80a+uidUbzfDjIA9wjiPaIKPSt3o2DEbVvHP8\n2O+S6Lm6aMUzZDeXpt5fND8K/KuUA+ORcjAkqbHokm5m4yxWulOmk233TW5B\nu4pL9XB+a9oOchhxR1BkIt9DVtk2L3rLmDyG6OCyMsduO1+JUIH4YSBSWfiM\nKFxQH8ZRLoWowSC+mSOMPV/vfpJVCM/pgaOHyNvU3SQwKUtrh1zED75z2AEZ\nRkUO\r\n=ITbx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/PbPJwxHBFzVgP3hAC1yMrsuCSt7OMdSj6vpS/qBFVAIgaNznTs6VPpZ3wjBhsyWtm8x7m8kl6Vb7A96l9Aj927k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.remove-deprecation-from-client.20180901190705_1535828953113_0.7640647704052965"},"_hasShrinkwrap":false},"1.0.0-master.20180901214816":{"name":"@atomist/sdm","version":"1.0.0-master.20180901214816","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"60f4094061a3fd91356545985f21ac7d4b2e3f66","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180901214816","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-11n4zRNA9l15kQzAbIELKtOpH122KoZgBhWVWE/D8wxOOFd8ebBGZp1z1ZEep+XcU/oYOxVU7+q5oopWN7+JOA==","shasum":"2df69ebaacf2e4d1694d69c3640c597d08e5a87b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180901214816.tgz","fileCount":855,"unpackedSize":1829244,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiwmxCRA9TVsSAnZWagAAnl0QAIiAd8uLWqfttlfxUokM\ng4mZK74f4S9A//I9mIY86+fodVPo/8MkUtKWbwn3s9qXHD19Cp68P6YiV9F2\nLdf2uUYQB7D5+TR1zYExPLJmxRspDy5R+EwKS/6ZOdkEsw8IrwN2PuOGAjLe\nug2+1EIloyvcpEjzP0BJQrnqijz1NT4Jr7eI/PD5F9NbG3Y+2E4k11Uc4EkX\niUYUbQyTRZfLaSUp7Wimr+rX/LtwGO0b1egunyEr+gguR0BwliLdMO4Hf8VJ\nbAk0EXhcjFCa3Mak09zsNfqEbyyjiu+dcB8u1Vw5BQrOJgFfLDITCC6A+7HS\nI+2GSdco6GcujrzafY4WPiH8mJ6K3LGqV0ugQadjSR9IWw/3A0vZKOvDjnna\nIiH/TY+MfGZwM9EMVNgQYYA4mbqjaWXCLmfQhHWJfjvcrJKdY5j+cQA8UpTp\ncV3S2xxY9YEEQQVQ984HzBFvGBWZvI4iBoY6IF7sy2cxJs+cVzUdF8oWiSXF\nkHT00yEBX10f5Ue/rdSPJhjdq9BItwID3Z24MyKMmGb2W/DE34Ny9OINoSvy\n85wneBePCKs8RuWstINkKzi2AH/KTPFh+o1dyO+rb2xsZsLYUp0eRCkqzmTC\n4RIBAzowoz+GCTF53Co6OA1h1CuCDvkAtiAN6V5f2VMSg9Pz5o61enLH/3yU\nfpwP\r\n=6x6B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDUql4ZQBl0oFpU26It0IaI3M4eLSjwijD5leXT1NiGmAiBn1WgV54pIyamBR/FicDq8wS7an0qAiVZ0tBOUS99j4w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180901214816_1535838641022_0.018596043935227735"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902005722":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902005722","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a7ea85046bf17241012a04e77e143a4e60ab924d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902005722","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-urmmB8DWkyHCrXXqb7LtW9v+DnchjvdyaqiRkGk9vz7USdb6BXRR8mtKLyMMmt4spOoiEC8T+Pn2IRVW1fj8Dw==","shasum":"3b86e87a581294ac9a6c12a25ca0979b881474bf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902005722.tgz","fileCount":895,"unpackedSize":1874229,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbizYHCRA9TVsSAnZWagAA7+UP/RqbLCBuFVN/VqIr4qgg\ng65hdTX5gpZhK4HB2yyaeZsbpGJc5NgSjfrXZm2bi1rZHSFBmuY/eldCs6Aw\nTCk//PtOU9kwUcFGvyNNlF30SeIlb3DO2ibvtCaUPNjQH7ISI+55FZ+g9W/F\nI+l8Pcavo5Kq+AWYdaTWEhQoywEEAhpizYn32PCk7C4JIV1IQvBYNB08y6Fy\nSrHsHzrTmHbwMpGfENKMEFO4U5KjrIcPHUoqFaUx3efWH/3tkN24mtDfYKE3\nc0KiBsAw/1nbLY25wgGySv9LZajI/hbyp3XwCM6TJpuYL2k3IBtOfSaxCcHS\nxr9rp8H+rzN+VuxH4p9IQ340fjZvtMCco1pRzk8pLblYmW54KDMwzfRM9dLx\naAd5AcEFfFQqD6UMH69fXhxYRJ0ib6gx1plaKWvW5+AByTjfx8acqbwO3lXS\nXdSo+OuSW55JOqdGf7IZbiSUsh9nRCqpmOnE3q1At7muXs2DcGhfLsOgTiro\nEciscFGVIbptmEBn7GzyT2OxOJEwvXoqECTrH9IXa6xZW3WeSS/FcHPrPPWo\nXdpljc/hs1z5PU5DL8N2McWsSL99HuOUhUpFVIDKOuaBOetUnmVMISfvlVcl\nC7fUN9n4j9RCb3TN4suG6ssOrgli5GT6Lzix+hE4GtEys8l+HjdSohI86RxH\nFvFU\r\n=QSyc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNVzmJt0NT0v+aK74Itv4GRinNfsdh6zcPO0scADFY4wIhAMUBjYGGLER5ab0JVT1QOWAK1XIYEz1Yu1GAGJ4D93md"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902005722_1535849990701_0.460586699584854"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902013119":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902013119","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dadd8cd9c1ae0ebd0ff8384a2c78436bca8a1e69","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902013119","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nMFJzp7LynHf3nMZ8ukANXgMcA8uk8pJ3fXGZvhzECEvxKxs+gWrnviXR2dJ1vyRivgEoZoF9ci3T4DqJZ2q5A==","shasum":"70f523e747c6ac31ff397dd3a4779ac23fdb367e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902013119.tgz","fileCount":895,"unpackedSize":1874123,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbiz4HCRA9TVsSAnZWagAAxxEQAIgL4h0JI6CX0x2DtnRO\nd0s3ssvn4Deyp5YIAFarUjyMylteSGYWDpSjUjHvHmkjw6My7QfRCSimBhuQ\ncOREK8LcaZioYALLsnd0BdvF58vzv9usieXLicbwDoOOYYMDFyBdgUw3Sd/L\nvT3wFcPvTgsrEpS2cJA8Ho8vOWPNtlqLiUjoJqJ1jzWjyEJJDD3KKJiKpAkc\nuWQi7f1qTMbMP2BAhGqSHCpB+UC34CtMUohjoLCMhcylNVmQF3P7W6He5agu\ndf13btoYL2bnE3W9Q8VFHolaSfpkQKQfCKyQBcnMbaRbIrTWLGrLg0JHGoCB\nzFzF5VdFPjh4gzsnOqA50NDNJcuUPyAJ5MHdxQB7WDV01992FaDB5aq9o82g\nTxUofX2UISxN5MwGOf4Yj9t7oVm+/FfXl9yhlDviis5Z9EjuAylj4wBMJvvC\n10yjeJrp63CDAeXSoYcmWxyHNBMRW+sfoKCOjGLW/EOkQPXfKHsV/GdyJEDb\nEO/Dv1gbTlzwUUFaDAKzQre8oxS1qExanLaQyrEiLqA5AoAm/uE14ebRHW2l\nP+/ImhZVoc7slo/9hT04YaVSHqcZjJiydnRefwUEp328xcpg7jq05dVM5E2+\nQtR2Kpks8tWnS+38h+qHk5Zr21sUnjcZASpiH7GQ09id/fA4QvgCqS7/AZ3k\nOWp9\r\n=nSQ1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIERNdshFDwoQZ2956ZvE4ni9j2VrNC9ogK16p33nrfv/AiB4rcaziQyZ6udhtKkasTtxF8FCy0srpdikZoX5naQdlQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902013119_1535852038827_0.27366697758085046"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902103109":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902103109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"32b80f4688aca279cb060a4fab930820deaaaa58","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902103109","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WnDTVhxwrT2eWP55Uh3uo7dCafI5eE0Dza/z4edRlL26ABA9bl2y6dxG7hzfZe7zm1cEdX4AD6PksTc6QtB59Q==","shasum":"40a9f9279854b7a652dbd4152f0d25a15b16de90","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902103109.tgz","fileCount":899,"unpackedSize":1875923,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbi7xqCRA9TVsSAnZWagAAE4MP/1Rju8U6sm9QQaXwbOOY\n13SkzSGaeufHeoCyl2Fe55V8M24atnyMla9u/5qbNeZs2aNpjMsI9k/soQWV\nTp0ei5e4CCPNyL5JBaFgjR+APgkO0JI7nvP7XybQqh8Rb2U/UngknlLT39GR\nqu/epROjssaGjyvc4cmW7gNd+ABIJXLDOmXh5BdyahgZZGHdNnSroPXqCEfq\n7vvWKOchK9AXGH0kEAq8UC1mJ6SgUZr+/TZ7l1JevP+YoWYu3VDKcknPCvx+\ng4qeVw7dDbITEfpFPAuzmJxfB+Fx9AnAFRH6EHlc12ixHgDU6doOQMUR7kaj\nK6lXmJsVrjSTgRVgeEIkfhhd+qHMsMfVZG6xwCHSEox4ZX49AixfQmo7cFFS\nJEFgHxetCdRiyI7pVJweLRPbB4JS59RPRWDYFiXEsBTeRX998j3OLdRd5xJk\noJzCiDgbO3RJY4PkiWfAoi5xlGSgll/HYTLA7xjZ0J/BVenOc+V3H8uE32Mj\nwJL8uNezTMf6pPH+rdCfJ0E6blBkJVnKMnCTFAJske5bgd1VQyDRygYX3QWB\nJ4fSEzH3uQcOVS6lSbFZ4kVQfyz5Rs9WAqQaep35K/BiTX7E08BZq7ucuTPb\n2FJRrPJYbFgKBRB6ZDrFwYaDTa3/G6ES3x1AGrVyeRJzaAmL2CGtgf7lrbvm\nSC5l\r\n=NdbV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB2f7tSrJURdLtU4TRZwPA56EZ2ShyRuASjv5dtGCtBFAiEA9xEyhfFzJNpOCkcdl43qoPeCcrcnkNqwEirXafOj+sg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902103109_1535884393080_0.6536170151284395"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902110454":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902110454","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"31ab9135af844ce9b5040f8a36caec6d210b2f27","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902110454","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s0KQlJFuGBo6kFiLSM8U6RsgrXVrAtdORcDZy9yYQ5BonukeKvzjBDBWoHX2z70kGI9M+JRRZb1IkpoOkmdxKw==","shasum":"b0dfb43ac5814dc1a3402ec63bc0bd6c460cdc5b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902110454.tgz","fileCount":899,"unpackedSize":1876918,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbi8RdCRA9TVsSAnZWagAAM+gP/364sqFP19GPfUinYpRn\nIiV8eiNWb4hPFdTUdzfj+4h5SFb2BJ4KmutZdjTnoasaajd4wvf8GwnlVMt5\nT/3/wn0t7F5DyfsLeZv8+rvaHF81z2/CkftHa/1AG7XAtcIaMtsE3G+2h2b1\ng7xLLyrdtGSiyfVgzwKQYvGEm/XObkw81YUTCqZ84uBx1gQ/AJ/ksav1WF3G\n2R/1muCuPLE9Z4oEVV4RjvNTAhXz3CwNd2cLbbX1i24n0sMMczdfxThvioUc\nRXFEOex58sF4sZMKxzvoI4D+LoMPrxkjfBOOOloMJKvrxQ4DqkKYT9xNOWt5\naWUDiYRV2TThBAwIfSGk4S/l2Fe+DU8OOiSd8CYu8BTW7x5d3bSQjFfACogN\nuTGlgi95SDnUDcDb+D4ACHkHoS23gvbyKLygd8ewsVufRaKbo7da/r+jnZCq\nFgbd4Xfpu87diy3IGosfI8UtOyj8lUMKxJ1WatRodku0tWt4TOIrRmDiOpOi\ndiS4Mj/WNy8S1Oaw519lOyn6hP3Ty5DVooeP+ynCW5SWrzL6rbGJw0tmaC7U\ntFNTq6QYmdGZ3x4WZ0VreznKg7moAmZHChc3aDlnOzghm/YXe1um/MYnMJqZ\nfz2IBm8N3Wm/Rz/ptlAIlwgLrDcE1fFLYRWF5NYXTxLH6Un5dcZu8cg0dl3J\nqrsF\r\n=dpbC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCB5Yik1UxD1voGWpar739+u/qo1RcR02EmDNp4KJGyDwIhANcSFsOzvOOKRhcw6F2XPqnALFEkgmlhwBLcC1K9anLT"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902110454_1535886428509_0.528623434167117"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902112126":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902112126","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5820eceaeb2ecdbb0f37fc7a3bf1f238920e7d3d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902112126","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kRP4Nq5iP2qHB7T/mytf82NgNJpwam5ETEYPD2gqQgua/32+GqgH0OY+TlAqC3iYL9tTJSYAcE9FgTHeKqK0gQ==","shasum":"5f45f001fbe9a96272048d600db74d16a9ad1c77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902112126.tgz","fileCount":899,"unpackedSize":1877127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbi8g1CRA9TVsSAnZWagAAyQoP/i8IXsxT4x0I3HdHEw6B\nocv7GL8E8ts8Xy5K/dO4ESJ2C0qL0zVlk2uqmqcUyOwWPL14vhBwusTeLp6S\nXeBLmrfu4S8J/s5dMomn8o5i2ooy8TvJwt0LI3ZEoVbNoUYQn6Y7dM6wVQHO\nSoArA+Z17/XuOZ09Y/hNrTw2cJPi6xxuFl1cIbXQ5QUW9OhrcLpfcOHS//gb\nvUTSRCeL4Pus/EzH7hLDuh2EWyQyDoz0//E1cmGFluHdWGrnjMcYeVk21Uxu\nEnCCynZjG5EH771aQ7ylfgG13N+QapFeEPdjPQy4LqNOmo46J3y5TuQrxvKz\n6FmW+7vuxS9ftXCbmsGy00oYKPGRUGhxXdW0kh7XoywCG5HfEwsmL0vISvWM\ni43llvc3vD1nrwe3YuwSBibPVwjPWX810F2GfcaiYiAqR0xF5tG78WHqziWo\nXYGylAHL663zLfVJv5sNDYdu+sf6DJWq23NlGNpgf17XdlNkGG6cfiFsYazE\nIEdzDbrkoZ9aRA8AUYEF6Uud3tZHJJEYi+QevqCge2bl8eNG9bNOGrSnMxt8\nCazYcDmi+GTB0SAUfCYrCkB0w0bFg0IrDusnDDBWWwxLy4HKGiajtwNrNleW\nekv25/gX48YsUftC/PCnHd6k5AbbTM7DR3mFs6CvCzd1RLN8PUwOLqOZKva2\nu0pU\r\n=WkUZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCC4sQANxwrG2KrN0VH9BTHZJoflnMJZlwbfl+Kfmj9rQIhAODFQUlQ2Gzpl52hLA+vJnZKP2pevs41U73kFIeMzpw2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902112126_1535887413072_0.7958562619513265"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902113407":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902113407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"685c9cfc681bb48fb65706525aa82445fec925b1","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902113407","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oFoug5fqMNNw2cqD9S8N/S0CIsz0pPRiwf6IGkYRbgqpZV4Wfm01DMBqGPPDU7TyjnIOTSZSy0QrzhITm2tARA==","shasum":"164fcb00aee133d5efe445abede2b8f827710bd4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902113407.tgz","fileCount":899,"unpackedSize":1877432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbi8sjCRA9TVsSAnZWagAAezAP/iJXi06ewPm6HKvEvaSK\ndnYLhDRZ9xosZ4aRW2rFgm/ocRkUs2XoCPI4SPtb0u8Q88NOKREeDZdgHmPw\nL2keNdSh16kKDWh6ip7uE2B5PZe6jgtVrN3ccTaBvtxl1TbYjE0yFNNUxU28\nRTG3TdvYeFhKt13iTbRoj4C+8rd6yDAT4vW0Kk2oUGRD7IJu8T22pIfCKjXz\nT/s7ot+wMw8HQ/K7mDX8w/kTogPVmjUCD72oHkXPGl08Ut0izRDeMVjMUbLw\nSnkIuc5F3LuCtHvs8+Bg6VBMK8byIUT+1Ya1m7/RBz8O5j3XV1YAAYoTQI8o\nderZ+xldRa3D25GHxEnkRol2MQe5HjxFv7eWFuNW4xpQJTO5s8VfBtFclmkF\nB5OvB5cbFo/e6jR334439DX5j8XqZz3R1Hddd+u3OmS9vVjixWsht+imPohm\n2mM3r4nXAgt04GXXfv2PnduSJr53g1628D+YNio01xduOggRiK53FVixjHzN\nfLJoVbiTtp+TH4ArmS2P+9C0M8hcsF20NQIVlt4bR/8dUut32DMFqEnYYJFy\n7GIoq/m65rVqL0Z4xteEEvdvGyXAqlL4spY0PJbWHfzz2CxKMTCvfbKb+TR+\n3xyxQIcyEeQHjvQIJ9wlQkj4XPFM5phbSZSk1ZGOU1lYwkQzvNGGMPPB9HUI\n2/Ck\r\n=8WIe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG1JalyXi3O2B4ZCwJOPdlZK8LxkAlnoFZJ0XyLN+3FBAiB9+rql+laWeomT6uHmGuLlQVYuf7TazkBnsi4RpRRXqw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902113407_1535888162147_0.31665325115624166"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902121350":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902121350","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9ed4636efd7fe7d5b8cac83ffbc0251ecac304ba","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902121350","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vg2YMgDQPeqLOmrP3NNO5TRkibWUAkIhSed8kwgFrvc6QFPSOjvQX9P54Kys8LRXCwQjNgUobX+J7eVCVNJh7A==","shasum":"d113334ee4baafb0ff776550d8559af269b9a960","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902121350.tgz","fileCount":899,"unpackedSize":1877348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbi9SbCRA9TVsSAnZWagAAp2kQAJl4kIfeclEQEZhzgnac\nUWDA5z/BpnsAe6x+tQIB4Lz4KUFpqYtSb+Lb8d0U9Knl9IKnMcz0DTLcm8Dd\nds0qJZxUQi+R4eN8wnk3OrYbtv47QBPzWd7+R5P2lQA/APqDWMmwk1EN8m6L\nkSHUOsJTqFhI2YanEVcT66O+nrdOJ+wQfXFNWprrBBHsk1n29Xk9mMha3IhB\nmfH+o49wVHal3q+bh/ZUXUcPIt90Vdakf3ttTiuel2R0ljak5gsTU294qSvH\nK8kSn2c68hbRJ0QNjxcdW+KCM2PxJr8Vr4TM6ah3f9lDDC8hltHxLqvEHsw3\ntM1w6ky1V7tRZcjG+2E3v3FZ3+zgZ9s1UsaoHyDOTBuc4rpxW7huyUtYr9M9\nFhXPavd5tlro2xoQk555AartCGJSm4PzO334MA/g4znv9/zGQDuw0uORGVA8\nDJm3DL40+z9D9TCzr+aKAZDMnVf5QblCoCEb90KwPRo0m9VavowyxcKT2PSy\nRGIZJGYH5g7GSgJQBDlIOzCWxfbbLCT4PnXRJA+K3ChuVu1JPAJ93C8rGk0Q\nYT1c/fkaHLOuV/vVStdlx2vzbOIy/7a5EKFbVnhy9SKDyR2edLzqJ6NP7Zlw\nzCYdmOFSK/wJayqv3/01TfdlUXJsX+LMNe0qS+xvDmQv3D53hnyFxHvFScC4\nkuZe\r\n=vbqv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBHa5WaJcLjHl6/DtwvR/WZiVSgoDQ3KR+ujZFw9G29GAiAQXvfHh9TUAKisfkcS9Qc889VjVW5mL7SoHbuPIF8g3g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902121350_1535890586959_0.8735240874920771"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902165419":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902165419","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3ae7609b2513b73af626826cfcdd6155dac580fb","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902165419","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6kmTS6VgTeXVt74YfgxwJ/g53kWQrMdcKxG1Ttnlh2dPNC4LyiJTG29BvNp92kPcVwtduzFjZ/KesYoKgkJEjw==","shasum":"4e662f4a173bcc9a994c9bb2b880c1ac45128721","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902165419.tgz","fileCount":899,"unpackedSize":1877348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjBY/CRA9TVsSAnZWagAASm0QAIqNO7OcqEInJ+/P7H9H\nep1rstQR7VATMzhcjIJZ7N0NykTV6Jl/tBjRvV8GwThe+LQFp6ExVxPNQk8p\nSS+frSnh0TWdKbB2bHxlD4ucaIQAjAXZ+t+QRsG1BqtrSJa/JoqXTNhpzxwl\nG03JchaKZUf8PpxmyV7uSs4QlYbeqBfeudU7hBSiRVuZm1L9uRbj9xGiK9SR\n/IMW6zfK07hXxzaBZmoWd7lVI4Gz241DrEM94ZjrxlpwWn5DBrwRLA/pvlCx\nvSPsPhKvnT0CTdn2WXTYz631MnTsJ2O1gTW0If11Y4tI/AakOh3933d+YlD+\nwcui9E+v9+bASrMOflsYod3M3TZ5dEBJGx0dGjPaXDBCaiNn+N8dOAuNnl9i\nOkAvv0aoQJi6vKSGY/0qhPFGVw+2QfY4nbFFR2ljWNNOqeRKk3JJ2bOf/z3a\nNjWP5IZBjPf4vRmEqp/sBk76gyW21IRZkCDGhS6f8nE85v2xDKpiZtEXSJxx\nkO7G9ud//CPJa+GIs3OilLvXG1H63G7ijkcklV7cN9v+0CHi1944bCic9Etd\nSJa1o2FhGvQrjheRPXQTfGYiHYbVSsICvhfzzIC+HDtIeHcVxhSaOQeBEh+b\n9dZuEbqZvN677YEAXAjenzc6OUFyiqNj4x8htpDDcQvd6o7qy4Li39tfRGNW\nDDsr\r\n=2lIi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG7xCWJKEOmA/oA6PWnw4ovKi5rIc5Zt/js3kgbO0gQEAiEA5Z0CiggYWuQ04aoGGHECcHOV6ZD2YEzJaKYkpuAO5o0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902165419_1535907391082_0.4319689409605356"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902181556":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902181556","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"43d581b813deac7c4dc5544ea82152d1be729267","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902181556","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZkN+stapAeSUZ+K7uAd1GiQQJlYA9F7e23cZ3BDt7kIe7lrBrd0lRzWHI3JAIs104R0l1Tb31844ndzQ5FYUow==","shasum":"fb9054029903cf603102eeb519c2da64f6741483","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902181556.tgz","fileCount":899,"unpackedSize":1877428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjClwCRA9TVsSAnZWagAAPB8QAKR9dUE7UjvPQpI+yKqF\n1d7IrXsUZ/3gW3HwZ6BI+e2iBGEe3ZLaXWyPWiiUAxi6Mvo8UAh72+xwRvD/\nqhYDoRpRXmhMzEHNmReJGtP+9Wq/19IWW2mlH7CHTy17zERRluixCe0REfb7\ncUoLfPCCbVYsSsHmLElRxhnhI3lnF0tD2vET1wDjtdork6W5Uuc5B4msPo63\n613wFIYtxWV9QM+NmP1J6iXBjIIx8lpjnXZ4/gjyihYNResFaxNzTkHxPRe7\nwTs6+bzEn8LAgc45DOAyCHzGbveK2wp5LyR353fXDYOHJ9zNgULdxj31H9s0\neKiBzij+XHjsO0tfQQZEW9HryouL+fmnxULyILhUlezj8l+AX6opWe/+cTZ3\nKN8YJT9sjfh4TzLoxpPDK4Ki+rQEyaQpdNiGJ5+TleOmroIgodfa1E1pHIF/\nmN8ZRfvUClXPxmpji9RtVTorLPwktQD4jevHJKP6Z3Z+hbSZOW6ZMGjZZmS1\nLJQdS42I37coahvyhPu0wc8pZnii5F+Ulky9pJ9fK6D1McfounSjyokhOBFJ\neVLMPLIRqCHNr02W5Mgjxlq4QIEboeGrDNgP+YoZ7Ao9jYaRPjdxvNnnS8bW\nOAukEqTyeTN9HwwYnRQDBfnhE4fNDbAef3Sj6SC1QjnaBTyAMVXqxq9ehniG\nuy8p\r\n=K6F+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBlFOFXlJimuXxjEbjt4ULzfHHYlX5KaQmN1QgmumG45AiAx/PbCJcXKG3cUSh276JyxSUjVSLoDNvOGtmFyPl4feg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902181556_1535912303830_0.3486539027213884"},"_hasShrinkwrap":false},"1.0.0-nortissej.first-push.20180902192959":{"name":"@atomist/sdm","version":"1.0.0-nortissej.first-push.20180902192959","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a5db637c54e0a00c89a4abd9aa9e2eeae5b786f9","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.first-push.20180902192959","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+1Ld3aY5iBTCvIvwUoChivB7t9zrb93WoQXNEgEf4QB78DjQxmrQZZ7XAZEm7SzmiAYZWAWpXkK7W/fD3ktRdQ==","shasum":"b32ce0d34c1c03ed5693442fb49ae64bdc79323f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.first-push.20180902192959.tgz","fileCount":855,"unpackedSize":1829283,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjDrRCRA9TVsSAnZWagAA+N8P/iZTLPsSc+455YppA8Kf\naYG29UG6SW1p4C5Vk6eU59660l0aB3E9pizgJdCrembZQ12vTzTQRiGBP8AD\nIzmmlJvei0iWbnLMrrnQsT04mBukWukcxBHViDo5rIio+E1EzetcRRkeCpno\nPdnB27Nknc/9OoW0aftQahnx2xNQOczEg1AdG0O6tTkAgyD13gP8bNackimX\nD3oGmbVpQzJr0G2BGOWPh3GpRHddgiKINjdkwBBJ7Alk+EzrFlZIwK89YjKY\nw+MaIv5LuZJB9Q8jYhnpsx4CzbdbIkMMYQfEn5MytEzpavH627eOr2t7gGOh\n6pA3eGxuJaH/nxegIMxVjLLIsVD/cxVDlS5osu4g9UP43TgKQ5lyewyCNOQ1\n/c1FSdp1E1MQjbpwwL/2kZeHXg8Nb8pEBnijgFoFEePxhY+FP+dZU2oj6cFc\nyuuugktiNA1xwtuSjKupCV0ae8s4k0sqaksVdFxl9/2/0pMVcQZ0PQXl83nE\nVSLMD5KGa4LPZlmswYvFoQPz9eHMKff2hdxMM+BJVjVA1CSR8HP0V6zNwMkY\nNxNJcCqzrpm+cFsegsSGmKE3vE9H8Miph074mcqt4sARSI1adXHM2hwnGMJh\nvZDJiSo6rwTbuRTccrh26rZLT8eKzsUuc6Cwd10yE7bTzAMyhMmqyeLbJO06\ncEWZ\r\n=kvXV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDYuuJk8oIrnNNQhtzjMYwdHZ5+W10TTtG5GfXeS/vusAIhAKXp8duiWuKO4O+YjTICLGqPkxFtpqTjySGqYi2W80z7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.first-push.20180902192959_1535916752730_0.35628233496642703"},"_hasShrinkwrap":false},"1.0.0-richer-goals.20180902193803":{"name":"@atomist/sdm","version":"1.0.0-richer-goals.20180902193803","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4b36f70d905be7b905bc88f0d7b723531b23b1d7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-richer-goals.20180902193803","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-38XacFkFC3h95FDxvb4VAheKpzD/Tz+gAl6dKnTaSKvu/AR7GEWvUtMKhH/NOWP399Kl19ByAXoQG/38LBv2/w==","shasum":"2b62e3824365bb07d3e1733b03b4f6cff49763db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-richer-goals.20180902193803.tgz","fileCount":899,"unpackedSize":1877429,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjDyjCRA9TVsSAnZWagAA62MQAIzmsh2SkBv3jayrAGJB\n5V4YbrLc06BiT1nVS7a8ao+3jWglbjiyXx0xPgMEGRsf7SgfnE7qSAHn5qQ7\nG2lAk8MPHJ4dS5U4m4aVvRvnXe9iLxj/sscCepx+HtsHyArRFHljY1U72aA9\nWD+cnKahTvD2LsnPD+Ep2MDGX96C9scZB/cHODi5/EGTwLWEEAvs5bT6TOGP\nwcsNYpDA02qYJ/nxrAnVRbfQ2JOMRXP43VQOntP9qvpQVUbtpY4T0bA/2tQQ\nni27jgernFzAdmMTYdR0132dnzcO/40QdFlsGzgEXS5Tz0Ef5ui2VZoU81qm\nS8uf9t5lmWN6g7jhLvETaJVC54jiqMlarSxgJYXaHCsHvnm6oTAXuhHiUFeL\n9OfU2IjbRw3mf2wks8fB/77ojjQcTBcMncw8M/QHHuKFyFkUO/xGS0Rl8sUN\nnEYKhT1IcvTyGR/DgUTsDjP69jHnl8CJCr0XqIKXG6W01Riio93V/FEmAmEV\nHReFEmfZiGGU7PZULCs/SCCL7UvchUFYlkUeP/3CjeHRFg0QVSmvBHu+Npfh\np2J+Fe4eBbx0lVzW1D1lJVasLdtKx8DO9iiG3A4VXrdiuNybJdr4XA6NHQS/\nay3KC6lFcQY7f/J6yn37fxaKV2V+sTKKJ6zB2PtVHxkULQ7vOw+2vT+bgClM\nP9IJ\r\n=zzhb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQMf72q+id4KRfZsWyCK4HYL1ZGzKErDYACVjglCej2wIhALSoIfore83fjJZgvg16aEsWiEI40rGuOua6Fanwulzy"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-richer-goals.20180902193803_1535917219017_0.4164984732646164"},"_hasShrinkwrap":false},"1.0.0-master.20180902203942":{"name":"@atomist/sdm","version":"1.0.0-master.20180902203942","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"68016eae22ccf44fe06c940881c2834070d6b6ac","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180902203942","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-imz6WX1E9zRdnhm1q7aFIFY2x3ZfdiCxglQhe6ev1yIrxACrOz8MXeDRAGogDzzbud0EaztwwoVut8KEnRIXYw==","shasum":"385aa28cba55e7360f47c1c85534b2a9fb2fab82","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180902203942.tgz","fileCount":855,"unpackedSize":1829269,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjEsvCRA9TVsSAnZWagAAFyAP/RZSSKUQrJnA0R9Cdq76\nKIag5e9QC93yPbb0qDXB6tiKM65eay7YxCRtbJ7t1Uu4sPxRKiluecUkgTeb\nKYAbkLET3CdYX082qo51KFTg/KtibxtWoHETaTJIMytaz2578fOmvZfOH6bD\nrM5FY5kdHnlqSnQW7qZupGMrPC4OKPBM0rF2vPAt+SXYD9Srmpw+eg6f7NBp\nt2dW0/RzsvQT1Aeoyo6OzcnHWbYP8Ro/ahtG/2V5fE/zN6y2u/Lj+Bw+EAFl\ntbAGqc/K6hqIWnvCl/38A3yUVKY63CH4Z5tqZEm4yCZrWsayEMSOYuqec1aI\nzRsnaDFQeq8Qz+v3DkKT3RTy5uCvdT/k0KdOj5kPbaPgzW24H7x1KV9f/Hzw\nnkVGEjF+HoW/n9TkeVdihN7pLe2uN71bpP/cE5t+WnO1WOadFgMp3ZTopUPt\njlPHPaWBtruPaX9y7Bxde3aMZWLq7ibiSz3GcekIM/yS9CMcdbjMQSLregVA\nnzmPYW8MG1eN641PXJHOqNpFCi5670sDI4TfvTif5h3YmXjptjyXTzA8X1Jc\nClfqONCallSLJAZjvfNiywugigAppHKeYpDq8TMFwiaOhzxJ1xHmIW2HAqrg\nnUFC0XIHfw9sEIkmhqu7t5xkWtjqWWNc7rUmxygSC8vGazPUnxtLQnVcdIK2\nh/G5\r\n=Vwz0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHebfac3D5n5pJ+M7ve2dkdmMu/dGZ+qq8xoGGL6M6PCAiArsJMqlhGK7971lY3VnszRrv4hL/bPlPwDgs56Uljl2g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180902203942_1535920942323_0.7928167413240983"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180902214700":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180902214700","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c4dc898aac1cd7bb492ed33aa1de7d55d6b56d43","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180902214700","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-X3/8Bs3OdlkioEm1dul7QIXvAK3EnHPx/Ug5rd4G/cZJ5xRnRLablZZq1IRd/B9EPTMenLKCfmwTLJaGMQ6tTw==","shasum":"77e67c126077dbce9126abafa88d66c5da9a7481","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180902214700.tgz","fileCount":855,"unpackedSize":1830283,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjFrQCRA9TVsSAnZWagAAT88P/3+xM5W5rdGl/mtr8JMV\n4jUTN/W8VLGI/egH9kDJiNwYNgi6mbcqK6hqtFTyUuuPZZq+IPd/oAjweMoM\n+8LiHl0QJ/DXnchPOhpHLwYBRrElQebjl96D608NHpEj0KuOLR3/TIDSu6dj\nxDx+A4TlBzCmnbBgzSSGIUdZVYsxT7i2ow9PHF41ctxUH8Vb/qLqDm7qQ+oP\nm49nyZdlUkXvR6ZtIINdBNIvpnqV3/+glZeXHVYruhW8/7X39VUIbHWLeHlm\nTO5i15j2a2UAFRS701ykPbZIT4Yoe5elHG7yCZOyo7roJiE+IHtFGyLpgUMT\niypM8Jql2WeEjYJTbABgWYpIrTvTZGyKAmrEaTxoZfVVQR9tk5wEyIygifxc\nkJlvDuIePdp3hlNg/sR1eOImHX89W9AZZJOJjLt1C+cT0WJmRdxL3TwYpBbH\nsCnAnnO66syLeAyCuE89OZvt5yS6fOtgvhdYcYx7GBO46GnL3jDrccivKH0h\nqknTtp2XrVm034l5QoZFAYVkwbN2ly+A4rBcuC6ytYuV7scuqJiWbA5UnWsZ\n5be2lpejN9JaEVb6aPSnzH01AMMLUjN9+f8tA8II/Ieapx9CWxESQfNm2LK4\nZRC4dit29/ulqUlKx+8efX9yPiUquJ0Oyb5F4/mTIfKhju7GkvtZPIoc/bDl\nqdG5\r\n=SOwR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDsP745jKXmtfBahJL3gFEzBtRtfbJT0IMLlZ79L2JehwIgDBDhUpv38TOlGv++mNvdSy3zirzyoW9OQJXsi2P7H1Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180902214700_1535924943177_0.9623311499398612"},"_hasShrinkwrap":false},"1.0.0-master.20180902224739":{"name":"@atomist/sdm","version":"1.0.0-master.20180902224739","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"64d4512f4105272de351190627720fa0c82cd18f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180902224739","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GCOV6Ayt8NcrJNfHKbiWcI51VJ1VQ8UpXpvJmRYm/DjXWIubWas3oKNAqFeDFUDKmXuqL53HpV9EgcsDROKh9w==","shasum":"bcb3e32fbbdc9d9c9df547603a3338f7f721d5d5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180902224739.tgz","fileCount":899,"unpackedSize":1877556,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjGkYCRA9TVsSAnZWagAApioP+wZwnPTjpL5RTPfBLrzj\n76nyS4ez1j8N3hFQbXpV/566LOfb4zXu7qvAnLcKFHJRBc6e2kTA7fT0KOBU\ny0JK9C7SAO1wGgWCACKSH3DDfr6bLt2gncqFHXAqnP8eeMPR8UJ2WkbRdNEC\ntWob7M9iA9cYqu5VcULwssaZN0xkN8k8Ju1XJ//jXgKMb/qCbWdf4gRvQCHq\nEt9xGEpNC9t7SEMADPcht0vUignPUd61Nx8kgCrJ91WVnJwPetG9fTuAw4oR\nmGHfpiTr1qX4PocMnVaaBUs1+hFo1KSNfa6Xa/MQ1auhULL9buZB3V30jUMj\nOhtJawJ89hPBAsGmch7ul86kyFiLvTVFUpjDD+LeDkKU83FkN2RI8Lhy1WF4\naIsZ5qp+y0uudtWO7XGsE7QFiR3Nh80XRxhVzEHvaMg++rjUn7x3BbXUmNux\nvi1bsNMIkQkgdrF2i8dfQ3bgSzqiuFkaHq/JTTjnR+KSkf3rMR5nWEXq+Glo\nvDox7bLjqacKzRTZuSkxLUXSzwo/FM8yUsJD4mMAYW22Ntc3H6ut84Wr9B8e\nsJcKaEExIm9NemSYiW9DkdH6J0zE/Cv4OthxWSlX9Hm8/T4zAWIIDKJyMbHc\nlXxHWUU3RAOVCBaw14fEmqeTjhhUgEnG5Pf+d1eut+nV0/vMCvvmINUL0Je1\nJl2e\r\n=x/QO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDW4Qq0DgssU5y52BbRMPKu+gVtEmiiPMYWMy/F4jUtFAiABa6k6x3aNJ0OlS9m84Y2wYlL4Q1G+W16mgE/akfCpeQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180902224739_1535928599674_0.6101067202649513"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180902225703":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180902225703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c6923640bc0d08147e50db6fc4897208eb15b1f8","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180902225703","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gQrLpIBvVTE+/v/jaarwlm+3svf1k1V2IEuyPtfHV4iD7Q84AQ5tuKmoRf5Ls8S0nPdFNjFK/YvK97Zl/l21qQ==","shasum":"9bee867b9e1fbff7f07d192c29673de73c401f79","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180902225703.tgz","fileCount":855,"unpackedSize":1829972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjGtOCRA9TVsSAnZWagAAhToP/2DP4eC65yuUIOvZwo3e\n4DF1QVzsxjf52eNqRGgK7/T4ReP1KSJLLbzBN1CVZPgmxGk4dYYb/5CzH6s0\nWMTb+TncYOiBD58l9xD8iTMO695cHySFWsp8F7AxKAXCCJFddiRPRl42Mvhb\n3QMGn7y3QlKym1Bbcjx6NSKr/wtepWVOWd8Y+uyuHzIUL1SNQDlQSHEnSf4K\n0IQ2G/7PZxCt9VeMZXPcRDhrM9WBYdzSvMIggJA83a1h1Xz38A5gu4ouDBTn\nqVzhVRFLS/v/dmCwP98EsHKRrB3YqeJ8bWYF6YGTIOX6msmE4oaNuaYYd1b9\n8r/2IxWRMkkwkH+Al30w4AGkCQneDRlKiYvLyhRri3cviW6lCp4NHqJT2RKn\nOACOKT4GHPsCsoVqSq7kLFPC+j4cCVMtHRpVbCh+VRq9cjA1/S1vwfe0VxiU\n956/pQy0FH+l/2/xjAMgrA/JM4r8WkDjIYok7deWZULZeaSmsWdzSRhwL24l\n5BM6hjYY3N9vsMP/+5Kj0D0+YN9LB8GMCHcwsycXoFS8P+WZkhuI9o6i88xZ\nzdBWAELFA65GE+ZEPI1DahYFocN8Xbr9wZ5Ia4PMY4CNAjhjxJkBS2PXyJOz\nalFJ6h7HGcsCxvOh1u9rcHBu7ZDUgbBR5bdMd73EkNAsIaCNPwWWZ+TzMd00\nS315\r\n=qLXI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZoM7gbHSHzd6+NiSZj7NsV1WhqUmJG1f2A9HgRF3ovwIgI+lrsD3c4T49tswJmvmLPWSOz40zZdBfNcYbV7xkNoM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180902225703_1535929166200_0.9653048694281428"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180902225953":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180902225953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180901185845","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"42a0122a8a999e888447bfca919315c544b24e7d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180902225953","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0ycwPbBdPnvqNid37/PZhpQEPzUK3g3bVWOtSLKNrmuIaEa9ynXeayUXFQqKo4fI6CJFY22lOMJjJgM7A7RnKg==","shasum":"b64192291517384ef61d2e8f00f99af207c7a7f4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180902225953.tgz","fileCount":855,"unpackedSize":1829994,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjGvrCRA9TVsSAnZWagAAeDAQAJ0fmlj/cYf0zn+a3JEu\nBe25oY9tq+XTl7Dim/buWiIEaSh4RX7akyemeB+y4UXNUgsaZkYzUMSK1dve\nUgqTRAISSEmZwrbUpodPAeOh01l7R0D/r5L3UI00iySprmZBKiVdbZGj9y42\nmRGrWX/2047wfDWE9PCwtKPU9VHEZ3ZsSYjOrv3SBs2qVGhuTfCC5CJG5djk\nKh1yyGWYtvHLwbfXzSLNmsBtLQhnGnui1yChqtz4v+TNHa19YG6nu7IVZfa3\nStxs/C3g/6HUuXVL9v1uIqU8PpQ4YYZ6/6r42I9wgtuJzHCexVQb60HSWpVO\nhq2zv4KqIFaKvjBz5LvKzMi8CJG1hKCKh97B0io1zDeJePIfb9ZFin+lE3YQ\nixvbRRuX606nmdPY3sRFI5MXrXIJH0jsFudaA5Z4OXv7Hy7SSx0QL4yDnctL\nsIyCHnLPDoUmm+MRPI8RjF1ta04sxwZ4aAIlG9KWafPRzRZ1u4K6oDgRNNgI\nd2qIWQU3XGWiZBWo+FBFPwGKy2pcR8Rr3gIjuiNG8JXwe0oYIpw49oXrda5N\nXVpE9cUL1Cw1jQ9j3LamtVAs7Zt4jVA59oi9OBBsKxYtUS8jdTzApivFIsl/\n++Xnjf1PpNc4+VvULRTjgv4ay0gD1KIEpRWe1UhKWjcUN94augB0aCb+bYal\nk0ZR\r\n=FbB2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHI/jbCPueIgF3gj+y8os8KC/Xzj7i0PO6+EbPsxWVIbAiBtWsONA2uaAmnASFZ9O5lMx530lJyoY55FVtC+aHg7BQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180902225953_1535929322587_0.4491784191874235"},"_hasShrinkwrap":false},"1.0.0-spring-initializr-generator.20180902230714":{"name":"@atomist/sdm","version":"1.0.0-spring-initializr-generator.20180902230714","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f66251175d0190ea738a7b7aa8184ce8f3f9b60e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-spring-initializr-generator.20180902230714","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5Wc5boXxTXvfXJxPY0f2VUoQL76nETiAbW8HRbOzzol0Y5D1fRRnPFY7xF+LnlrscbxiV6WRMBb2Y8LDYkNrrA==","shasum":"9cc04ab0a5f4228aae188dfecda61408015d67c1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-spring-initializr-generator.20180902230714.tgz","fileCount":899,"unpackedSize":1878281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjG3GCRA9TVsSAnZWagAANhkP/3XSIgPbasmjGLyzqNln\nDQWQkzYbHlW9iCHuM12LPwiaR+BxjF6b9gCGhs6I6mq+HUfJX2JY1GGQPyhA\ngcTpBmCUi/+9o4ZgAp8rhAdpyrPSfZxB8XOyARc18Od1z+dIKuwy9ELxlONk\niH3aHzMM+YmEYkv5/vYweKxUS0VaFX9SHXhE9/it0j5K+/TxBtvbfwNiUUs0\nQr/nc/b9LEiad0AUYRjPFbTWk4LVmSSNjRtv3YuoDev+l/W4Nnv1P9cTJf2Y\nMCqQvFNgOxhBztkf7HaJMudmvCtHZkC+90w/X9F27AMNbGF7dIq+sHxNoLPK\nlodz/oMy7id5yKhFrydoiNUvBoarQK7Qd1exOlu4jX3RY0IBjebLBaCKuR4W\nz3vdigDPPDMsZVfaBFWR7c4pus6EYjJES7NFueoRYjgEihMlOmPt+JBlrppB\nU+RpLXJTRFQ5MyVsvSexC39kK/7XisZZyELpUWaJcPGTFpvtfWltAVFIYkYl\ntmmBaV6tFyaYnd/uXT93tEShhSxSJ3twm/3fJFwIFTwktjTR3UEx57ksySpo\n9p7uRnBM7UoEFoOQG0sywaMaQaCmxV7C6xLYmnUIRTA3SDbzfX76AQMDpCEU\n1XFdefPBdhUslYEZFQ8tjXi4xAMAttYNSBW0pZjlSwMQPMII5tsjVjgIUeNd\nnVFU\r\n=cnG9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFTWdlZq1LI7flPxpVRpnJDIgbR5yElVnDnuBiaO9ls0AiEA0gFFLme7kxh5bhOhMp+CIIBs1l5ob3TwXTlKpfVT9Lg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-spring-initializr-generator.20180902230714_1535929797693_0.6665042387483566"},"_hasShrinkwrap":false},"1.0.0-master.20180902231058":{"name":"@atomist/sdm","version":"1.0.0-master.20180902231058","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"4f5507788de27370d66652a6346f1917daa76999","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180902231058","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/DBQy+ygiKHllHW5SM1ZNQf0hxoQywSRCX5D5bnlDhsvVFX8MiSgrMkkgXZmNvj7acJzwuSyuXAJ2BWTqxOVsg==","shasum":"4e5def3002a49bad7a9c72d5f74eadeda90b0a64","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180902231058.tgz","fileCount":899,"unpackedSize":1878260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjG6vCRA9TVsSAnZWagAAWNsP/Aq4rkf4T+dIMxWh2D5K\nEFPDP67itpNYmiPLg3iT3SsW23qUFGsytRa0MH2esL9NtnOFWhwQsJhs76oc\nvQugvhBLHqOfnc2fqJFmyQr0MFxQ1iZdcH5xyrk3PsrIvAo83a/xwED71nVO\nS23pRcqvgpQkP+aJaZVU3mVBKg5mdlNHDbvDt9dFg8uvFY1gkmBztGtf7CVi\nl4NJT9xAPjXYsfF6SH+E4rsoZWOqcWvHbniFH49+ewTajktawZMGbsza/5gG\nOLmfOziPo4O94FretShTJd+dqi62UdWkbkhbTyp3nBJVitEcqImCVIIWTyBb\nYHe/dKvJXLFWbymKY8ao4sEtgu3eAO9R1hv4svZu02WmQ7bnrAQTG0HjcPK0\n8LhgeDKA4tzKEMs9lH+E00cCZ+cJv6g9t7xdthR1KKjpXfg1l7RnSC/IjDSW\nhxqNOaWzDCqRTJ/yCrajjnj/Rxke4S52FbSXBNnVxpNREtDzB4ycoOlFAkEF\n6TdriyZQDs47B+liJxsFphkzYLUpUyvWyFaXthGRf7ofnbLoXWQKRWNVxbtV\n64js+8eG6H0/kcgqjRP1yMBiVeQt7+O13N7r7d1FcDfcRhCJXIDMC6Yox2Qd\nI0aA6CtkZXMp/Vb562BGxnMs+IwT1Sg3J4vFzfITB2S2Yp4NStg9ALHE8RpY\nRmKf\r\n=Oq0V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDiB4Sc32zIigFl5s9dtkFb4pfq8s6jwOCNp4BIzrtKqgIhAOupHGkn0d/q6AHfzJsmRFBeDmahmnWvbNL4piR8DGOU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180902231058_1535930030683_0.2072116072903878"},"_hasShrinkwrap":false},"1.0.0-master.20180902232137":{"name":"@atomist/sdm","version":"1.0.0-master.20180902232137","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"77b4a482c7b4466c1e4c46bf8f3b11a5c9f4ab95","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180902232137","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Y81aur6xtWgCwg5k194eMNrJ9dF6pBQxWsoaauAFFnPF4uF4lbIr/qzyr/ryCle7Z6L4qs9fgyVSHwWRKXgqOQ==","shasum":"60ed36f6c71681cb9852fde29e643693ca78d706","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180902232137.tgz","fileCount":899,"unpackedSize":1878335,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjHEGCRA9TVsSAnZWagAA6EMP+wWHSq/jhLAi04A6rAsB\nqTFisvU3+5sGWbe42/35nj1gaeKivmBnauxWeXMR+8xC/B9dh8mupT36T7K9\nt0jc4EV5IccZYFELr115+6u8yyInbv0V3tLBq1+QfydKGXbpMnhMfNtuj+zv\nmwb7syneYUff1D3kDA9z/mmjIjOT9EvM0DiJZkixV115mqZJuf4J8O40qjSM\n86sFkJCdW+R93JY4FzbYeQWKJ3kc3UvJkjRwc9qu0RtUbSFBIj0jM1BR14H1\nlZZojNs1Q+xDYbKHBQV9hr7TCoJSDss7bs9JzK/oD8WhSuFrAW+xYeaD//Ia\nrJnuQFwZgVO/2CTQMfy4q7sRjOYxMB6QhHDfxhLsm7phsFLt+UPxQyTVSGN6\njiKOLuxhICxKetOWfMxygxvTsD+AvOzLuMONKvMQfKAbnrYXbnW6xaOITzGk\nqBguCAnc2ktbkL6gQvEFIT/Q9FfIU7dXdvH9bDop9Mz/1kJxADPpHvRetcqZ\nNqtLvc8U/sE7tpiRkACQd82KCt1LY6ceCqWos2c+WIN7M6i0fiHs1ttpzwjG\nFGiHy0DILivAbIygjyH4nmuRfe5aJL4G/ExL+azE2n7zDyjyPfS0/aJ3l55n\ns4vittcju9k7bCi9W1UveQn1PgOV2J3NbkB5eo7/ZhO/U5843gv9OhcBaucB\nSs5e\r\n=ZuEP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDuZRb5ivdSQ1X0+BxP5uaBWkIPv4z2X0N6jneLYC1dmQIhALM5KOaGeOlfWJ2SyCBZ2eLZTcLVkf2t+yK0uZ3kxCFB"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180902232137_1535930629392_0.4518586702562606"},"_hasShrinkwrap":false},"1.0.0-master.20180902233103":{"name":"@atomist/sdm","version":"1.0.0-master.20180902233103","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f99726021cb6c94645eaba85b700174a70248e2b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180902233103","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4AKbmNJw+B+0R4pvC7R5BnaoyJtdrwdKH6xPsAdmyASzS9JOLV5v64yM95CHo6nSFfWcQ1kAcPK0fdl9YWHjmA==","shasum":"432d4b6a6c6d50909321859cf8cada935437f7dc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180902233103.tgz","fileCount":899,"unpackedSize":1878810,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjHNgCRA9TVsSAnZWagAAry0P/0s10wxqruy7EaHIhUzr\nzHpRMziECdpXh7v6VcdzwUBz8n9a6cNFymqAcaqBm4NaKsw+N9JU7vOGSSDu\nOTSaMsmS3y2trtd3PHVmtR3jBlf6XZS6GapOLj3QLfQX5bvNzDcPTyWjj0bx\ncSlV1Ol6nUVGF/B0pRTWTZJgKWbILnNgczrbpMXQeLl3pRNWNr2KzHp9gKkY\nuj0JW3lNQV9IeULB2NKqfxgiOdcf7diVZkJgJADdWSpASBm1bX1a45fviliJ\nET7np4Yrg42+tb4WfhCgpi/cYQYKclpMcQpZyMCTaznIh41/BYFkZKroK9Qx\ndwDUAy7oR3q2svLXdMu0nUVzFGEWQL0+Evgy765lCFLQSj69RTp+7uETi8NB\nrhMNLfHdACfJT2VG8FbUpVXi/37s+d4Dplznlu+UdpD7vWKNm6Z1xcSdb7mr\nrzbZOkdsCb2XI5Jm2GsUZGpAg2TYzuF88RwkA8qF6t92NjoZrPN23LrSXXmV\ntEpGIyXswM7QVlWzuQpqcUorQyfw+T04d4GoCShoK/CUzSNnvQzxJyxcuWAn\n2WNlYFlBPQ5FXb43o4KXxSU7pA80xtWxXnEsAen2yCXN9CMIEbXgFPLn2wLr\n4IXqDBtoJF5PbttGFfR/h+RNvmqN/H+hcI92z8xsvxvZ04ds3HmwKn1Yv5dN\nt6lO\r\n=pTd7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICIZaz0fwVL2RcQYOsgi5Zl90JlME10kEV51Buif4rAjAiEA3sc5vaIi8muJOsMtcab8wXWbbLRy8FSr6Ehkt62lsmw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180902233103_1535931232054_0.8979341042680469"},"_hasShrinkwrap":false},"1.0.0-master.20180903050226":{"name":"@atomist/sdm","version":"1.0.0-master.20180903050226","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"94dafa04c1106db2ce91caac10859dd158def694","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903050226","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-j+XrjA5N01O/xkgEkvEHFqcwGhrN2CivcAA6n18TlpAHU8x5rKCTy69fluLj7OytkatTc05vWh1fjzZkhRXwOg==","shasum":"c8c4138e17a4e18f5cf5e366a5231c5528aa7cf3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903050226.tgz","fileCount":903,"unpackedSize":1883140,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjMEICRA9TVsSAnZWagAAW1IP/144cmGxkX3dyBThFB7T\n9GKyzzZESO63tIV9qDo5T8+5ZxEejjgpnFvwnywVaYbleNcsG5q+Z95oVpgo\nmw3xPhDKL2onp2TJDxVu0tMmxLvEfutyuAFGmPGiMVdPwCkOOoDgSNKY2Mw4\n9SohiL+tRpWAHGlc1UufRxHA2irA7S7OdiiYUMJr+pcueNMdfYVSnItu4RV2\nqVB5e1OjuFwfqOUcBgxM5BnMRYTac7RrY8dS5z6OuMrMfNpYe6SSvTFS0bpX\n98uZRYLTHMkBD/QSO7x5MnBTn7RJm24GSAverCwaRBl9uqX9X1w70ogdNB5Y\nBOjWyiYEJxXKE5NQ1HBhnBFxT/xswm+mxb9x/12HiBK2e8saFO+WsLqFAfcD\nu9oUP3XQzr7vBrlSuRMdNLDe3MnqOwF75w363hsSsv+KfNCc5akMXWAbQA6O\nvymmeokJOCD4WINwNllUC5wJySBGr3zNHbAnr6KlKZKpmKpW90yyPCiwsf55\nMYqnUxvWsL6fPuAG646ud8LKuzP08Q0rOlmSe8BpkYZqy6FSWuoi3vwI4e8p\n4mSVxEXjxKftRqXzFSSiIfYlHaKojqYRvS8oFqd1z6jdh9Sj2cQLyUgtA/Hc\nLe0Qu3d4oHxT9Awbf3YYac+z4ek18CMXde/FXhjEnSbm4r8W3IMHWMvCF+s2\nJwYZ\r\n=cc0D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8iR86cobrdlNuLThMZNdzFHTqgsaUzYYqYr1w9ivsRgIgZzLMthanq2tihIv17bd22PV0X6xDZ+q2fIjZcoeLJTo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903050226_1535951111122_0.13955812018090263"},"_hasShrinkwrap":false},"1.0.0-typed-build-goal.20180903073906":{"name":"@atomist/sdm","version":"1.0.0-typed-build-goal.20180903073906","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3654a92ddcccd6894ecef6a832ee15681b04107b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-typed-build-goal.20180903073906","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-O47S1PCpggHGitT1A6YRgudyC1QBPQPA/XfxSGhrxu3+/Qr8/yS7kEjylIdyvbovqNi9ux9pmajuedck301DpA==","shasum":"3e88c0fdb920ce16a468bcfb104734682d7e0abc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-typed-build-goal.20180903073906.tgz","fileCount":907,"unpackedSize":1887058,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjOWbCRA9TVsSAnZWagAAPHUP/2cJo3cZvsldnq1wUlJ5\n3FlRDH81xlj9kr5MvwTjH2Uj0wZAWQhhGPD0R+FiNxu6kocnDaDA1anpEFXF\nzvxRwiud5F3Bgay/ceDKhGEE5hDU4h3o7a7DuC38edu1kmFZeZFKuIGLK32l\n2R5IanjjKvmx3IO8NvpWGtFaxRYAYLMcKqbhdY+Cez+72JWZhlVpAyRAl4sW\net50iDtEliI1sr5LVyVyQlxQA0B56j5tmVxNjTKbBAw20SxJYEQv2D+RrBgg\n5L/aMHmpAmjWF13axNjo/cd6Ps4V1J0y/fkZxQV4LlcgifRJDkcVddUav+7a\noZNZkWGosQ0EvT1yK5OOsC3Ae1q5uJ8MxuTRUVoXyUgJl2qUBMyrdleeBq6P\nIA2PXbtXvBj9dcp60M4M5ZUqkaKfNU98abwjZPuKhzti53WELXjOp+3j0JTT\nLSFi6CvPbMMsfB8dM53DtzqVRSBun0P5oVQcSNzl0jXjvVSjEXrtO3x+16FW\nBV5SJFtDBR1FSqUx1Yifn8laOo/2bNBDjeZxHkSlAMLmEDW97CLKaIgQXceO\nBKzhs6u37ssk+h8kRcxDV55beHqlvje/CshmJZuUH8ymx3rWHBv8FQ72Qr1s\neR0/3O6n3JloKufeyTtP/h3inC5k5B/CfsleCYGKXg11CsykrnbwakJsWigX\nANMQ\r\n=k5nH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE6u90sN4fSoyw8V2mHpFeZi4TYWm75v5T8b8nakt3BGAiB9sb2QeMO4ZQspZn+9p/MljuXLMeNsHjvt/ign2mEOLg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-typed-build-goal.20180903073906_1535960474513_0.06439410809891855"},"_hasShrinkwrap":false},"1.0.0-master.20180903074915":{"name":"@atomist/sdm","version":"1.0.0-master.20180903074915","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"165861e7ab4dc49f7da0c34d9d885d034e4bb32f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903074915","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-38qyvFSDz328TRkr17vjCzj4+EDhC7c7o95f5j0BqCjyyTxRxeBwxbZFqtWd2+7F2D4LeuPSJOjCRccg76ECBQ==","shasum":"d9dbf8a671cadb14fc6aa813ca85f2f205e3971c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903074915.tgz","fileCount":907,"unpackedSize":1887048,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjOgfCRA9TVsSAnZWagAATmcP/0U1KHPXw+eRJ9YkwNge\nk3oNdKgPUigJTTd+4nfFtYdIc129DLEep1cvLOnEbL8NQfnIFFVz6DJT1XjJ\nHnTpKBYGUI7mZOuPeNVywlwV8BWzp0djYbN40nOqj/baMgvNAbE2VwA5nzLy\nbs4bqoV6QFOZ9tlaBILa64kxVtf+3J2OdOonsG7tymlYri6FxUPnJPgOmT5Q\nlSn49llSH0U/1Fh6QeI/rrLHIs/wdG/A2WCq7084KPeDH5qm94GEnICN4frB\ne6vIYhxEm/q2bMTx8YGI7WzDmU1wK2bsY6E0vYPTtp87AsuHM+jDn/21zJY6\ndWvgZS7KOLUcYDDoDPhaxwuFB6aN3lGaNEVI4njCMDdwZM53/bMGZy/1EqOg\nvV6ADfKOcHxSHgS2PhRt2eNEfM8LpdIcsOT5AJ7cOyXE77E2QF+fwQk1f+aF\ndCoXl7TRQyR2grwZjxcAJvmk5p20GWAhvgXizrMAh4z2v1KvIbTguo0Pqjxt\n6BcSICmBhzGvCi2m1AaAKxmh+mAHQrTJXn8BNW5I4SPtANI365kHVm0VKobV\nOf9qVz9ft1vvse6BVMINGCTHNv03fr8rmRtjtSg3/2aWWDZgQ28QA2w/8OgD\nDDSsgBVZ0tFsHHk4j22G+pKp6DbvzCVpO9g+4wKmj++aQXc0Lz0fhtDKeIGn\nWz3E\r\n=gJvh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF95/cYWHL1gQ4tZGHoyd55h02UjirM0JhhmRxHvOF12AiA7j5g8qxxBBuvwfUH1aQKRO528bU7ENOjfeXIqwfgrfw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903074915_1535961119116_0.8975653511613031"},"_hasShrinkwrap":false},"1.0.0-master.20180903095316":{"name":"@atomist/sdm","version":"1.0.0-master.20180903095316","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a84385dd4b1161907b2f09039cf8eff53837354d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903095316","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qSjsDhAhjNaXNLDyehDkTxULRntZIW9oMhT//SCQVAYmUKY2nTMlGXNCjiuZma+UkBaSRIU3oQrb6xx4SOwOsg==","shasum":"2915836d618a411a1f756ece394eb8828b857480","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903095316.tgz","fileCount":907,"unpackedSize":1885288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjQS2CRA9TVsSAnZWagAAOPsQAJ+nhnr8w+/YfvhmXkuA\nRTTYxOxwEmjBY4aC4zsgPd1PI3+Qg/LUtpyKlf2d8nEEL7NgjtzTMx8HL+hd\nzEL59zok2+xM32Mxg39MCXDSsXLGXnzHeqe36L26lRpVMKZ42FFyACPRuuWl\n+uxluTRrviPamOycXfUvz7CR5XBVV0XlWSDRI3qQiilyzN4fKlArcrzVtCtU\nWj0/hWBDi3BnvTudjcBNpsqQNGCvKoMn5HsjXRkDA6fXl4Hr5Wwsyofmz8h0\nJxSUTZp08L/LZi7cig9xBgL24eoaAb1VaGogYj0VWObWBSmwDBOaZWiI3XB2\nwHBtqwI5e8Ia2RWtOhubKmgrIPEVuZA1EELEckJnze94t8x0NMx9uIVLClwF\n9wm9peoBG1scqTE42xWC/1OwFFiO+yWaVzxlX01yeSDs2aRaRKzPpvjvzSnv\nPaDCgYo6ANI0HmgBNFQw7bRR1cbI5IaX/Lk+tK1xN5wKjMCyli0NtdXnGcA/\nSUxVDpIfhKroldsq3wjW5Sa5v2Gbv1QR1HFpa+B6cXmmBexSuMf5/n8i1pvG\nOaZhiWfTJ4DeRxwH2G3fCFwtJFpIXm5OD71tYWigm8+SGJpFWBZ/W0IqMLt1\n9B4L8K+ffTSEF8GL5jfoPZ4VEWhr/dZN7n0jFT8CsypiKyz0hj2guUTyOh/f\n69Lr\r\n=go3l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBhPD5iASEos2Eeq4jOxoq1EFpXe84WqyskHz1avZOsaAiBp0BxqyMjw05HerNYjkxD8DBDbzcbLUNbyzCfAM/N8Ew=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903095316_1535968437270_0.11177221931225123"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903112552":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903112552","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"177d2d7b257889c59f40dee511928d086146aa9d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903112552","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-r1ovkOvZ00snj8BtxVu3NjTqtnPphxEa27FwBvsz59ontBiruNu8fN6RQgexotb4VDOc1LRFKf/vwe8L/38hQw==","shasum":"45deff032a7e22295c85e4624d11c44e38595f31","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903112552.tgz","fileCount":911,"unpackedSize":1890547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjRqwCRA9TVsSAnZWagAA04IP/02mVHrdqVifsCjmBzIs\nbcTCfbYNvZQ+2JOQ5kfOB0eXuzw6g+zFD3gcbqjAll4TwI0KJCjiQI/lNm0f\nCLLy+VsK9dVPMzC2cmu/7gBSy7t9eaM0X3mIcLIe0Dgqd9MbKZ/7t/a6m1Fr\nYo9EQX8t7CRsZpdnzZ2eP3pWwVwkWvk/pL0lXqHQ3SztNvCiVOK+a8xjfohk\nNsG5MaN2h3ToMRe/JD1uPgbhgbErInrNemQZxWVIgNjxfDa1r/mniGhcJub4\nk1Rpd22LovO1qP91xtmUQX4yYlX02x27KH5haiHP2SO8x6NmxIuhkZsCxCFm\nVq4e+g4ket9x0pD7uChHqTC1XU0g0YxMLDVh4vxi5G6TV4f0hBaVnMsYf4vF\n2evHenGe3dEdbry/+dkv/ZoaG2cZRbFtuQHUPiQLek/Jjh+/NFKL+tSqG0Vm\n0fMfDcsYGrZvCzcWZUvDNQ2gVbtkcL+woQn4DX2rkX3DAJ1K+DDW5oCwTsBn\no7gz/GuWcyg9Y0cUrdzz2+ASiwI3E2CM9eHttzxcvj2m1VO8RmuNYWkTtQih\nXXUseKqGC/ZeXBRzgNVGjbSaQoFbScJQirM8nKmGW6rBiIDVTfqkCuQYXRSb\nqkOzarW6niUqvnt71i6ymt3FU/G1qq646Fx5ErGXtjAVkOZ/TurFbMLJBMwx\nxjaz\r\n=QYQh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDp4dokcG9TLWZqtpSzqPyIHuLVcfW25YS/imDjPzXYmgIhANhK2dwkzENb+7SNwq0nFspGRYRSRR5UKjNK0p1Cg1Or"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903112552_1535974063559_0.15615440537099134"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903124928":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903124928","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"39a4767015bb2034d2fffea2ab305f5605dc82ed","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903124928","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6MGbZnNRDxm4wWiKjJ+fXnzagbG/2Ux7Onb4WEIfyU1owv3xWSIfH2LTNYt3rtMKawyqXXF4ejwUD6YBX82Gwg==","shasum":"eeda0882d8a1ffe447c13ed2a6d6c767aafab1f8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903124928.tgz","fileCount":911,"unpackedSize":1890786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjS5OCRA9TVsSAnZWagAA3agP/RoXenR/0gXYTkW5znyg\nPfuP61GejON/s/hWvm0jixhK+YJ7FwQg//ywNh8d+gPE/vY9doDjwaYLvC25\n3BG0bzfH+/1Y3MZ2lX+1r0mwRw9ZBDiM4oi/KVUN4qPLj2csOM4BuhILA9Gw\nGrEgdt3gXSmt3OPQrLOVQQbhMBU3TX1EQTGbcsHfiUwNYiUu/tDN5ORt3FDE\n13T+jdBUgIhLXk4XH8tg3VP4re5lbAJJW7ZiBjbqcyu5F0s6uHkWS8Lykd6v\nlEdAcQ0IKzqbQG8OvjyfUS8su2RB3ic5UK8sjRlBf5/7rYETsyV2pl1rOQQe\nDNAsNSX7C+PrsTxuCwh1JGckkF+4hb5+V3yIbnj3S+kQkqgHL9UsF+64HEud\noR7hBTPhGW+lZWXb3nMutT5i1TS2B2aVCCqvAD7w0FMC1fhiaKtBSZI6QxIt\nzJvsjnsTloR2c3/dXx1r0cZJNzYmbYjdyQqNwUAPzmLSnwZcRPeXfGreOv7p\nJp2CoTaz0Ryi9HqQ2A/gPd6yULAFivCSde9FF19KQ2iHk1I2xdatgA83LSXm\nvbEYY3cxwWOp5Dc1j6E5bLhIqQFjHLMX/ijE96W4F9+RE61JVyyTmY97hG8T\nwaPpGZJOHQbbuDPPP6G1rfkFxlf98UUFXqmetEULsnB9q5ieMswRkUfoN/qh\ntTlU\r\n=JEZV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHDNHI0XoS3Z9m3uGbKX25lhdixECnXixxqLLKjdw+ruAiEA2Q9aQFkL8DO0MpYuRpCCJfgDgYcaUJ8H9/Yr28m0vT4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903124928_1535979085902_0.2919177514565032"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903130920":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903130920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5945d1ef89338e1704e201021ebbe5f65f5e6d03","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903130920","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-knmGonWi3dPIGqeE3Qn6A05FO6t1C5b9HTTzMsQJmxtDU35FaU2D3hldGWGCocsIMq2qqJqoQDGbzS9nIgaIBg==","shasum":"75120a9544559d3d8d3f27bbb02188639acbc656","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903130920.tgz","fileCount":911,"unpackedSize":1891145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjTLpCRA9TVsSAnZWagAA43oP/AscF29Vk6rLNZTyDFhh\n6KC+5RWQa7FUTcAVMv6jpTwXIIMDjGYvB+mIoKHQR7zH2spnyq2fppq7XW0h\nemuE6PttGYlBufO+PVlOBy6la55eRR9Y5c5bSq/P7WfvKskHbGhpf/9192Uw\ndsPv4JahfY7YvZWiG0DN8RrL/e5zs5U5/HQ41f3o3Cq8DEdToMl5lTQ9FEJn\n5dcQXwRl5RIRcwekfODJpIBHZ5zREnXS9UP3CAXAMnyVKJKcplperRcpjEA1\nU/GGWZgh7aAQ0Bq7OlDZn4TaApMsPyyH3yd3pkOzI3s1YjzOdFPsyHAc42Ib\n3sznEc0iuIV31kzi6JNBq22Rx/qPiiaj1ueNu6WHET6J0+l+vsq0mtbl4fd/\nFgJtoYWk18SAEuhabMhc4xIk3xcLKoZYTdotrl6DBeSxSM2osSsJtgzxg+bb\nDQMjkYW+hO6RSrk0NMjUsTDAmOpU0cz6sfj9LcWkbGYExVAUPxJ2A1uDPZQq\n38G+R0W0LD5dgtKxqsbGx3X+TohRk8AYlXoovqEZAiys2Bux8bvkPsn+2ZCE\n/M47pZMXk2Qr9gkNUylCxS43kSmrOCOUyY/6qWbscp6QX1aRNLVe9HwXLPJ1\nYYejWY6Rh3oNAGMS7OMSV21ZTEExK1dt975/AnLajMkfN5iSqojlyvUL6adg\nbF2O\r\n=nvfs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICEiN3mDflaFjleyC2OiQGWmdikNxXWUiaNEs1xte1KSAiEA8a2pwqyCDYi5mNTpsrLBkJ8XLPGYrPxcAzAxhoZIXmQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903130920_1535980264964_0.9107365455192862"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903133924":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903133924","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7c98bcd2a5ad2c6f85766b5eea8b067fb9375a50","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903133924","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vwVOaSnugQ+6j68J/+gYXPwInbBBGZTAumxh33BrmY+iOXvZ9WdL/9wR8jkToC0/JN+9ncxMH1PQHcmpfv6hDA==","shasum":"ce266d74c1dfc9e7bd2fcecee5dae3389a77a4b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903133924.tgz","fileCount":911,"unpackedSize":1891202,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjTpLCRA9TVsSAnZWagAAG2AQAIOg2sxaiD3/M4et4uYl\ngKmEa1SdMdy/plBupjD2ya0+j5VhJ0bbAHHPWd/x6JwRDNK/EuAHUmq3ftyd\n93Q1XoQPn7HMos61jbjbCPbl4T7OVr3KEHrhRtQ732B/wGRdbkz1YsAEJlrI\n0xz6uXl67E5BHSslg2X1eyDK4BUjB7GBl97C92opvnbRiOapLz+TZBl+BOt7\nURo+8aopyT4moqg679EhDSYqAHPnEX6PIWMLb6XCMxkZoyh09AqHIIgxm0Sv\ncGUQyRD9//Z9na4BRawKp0Aq1RnuUu1yvzg7XXKHHA4uWsJ5nM7My1HP52Xb\nnMrUfKKdxrlJSP5reGpiqHdcQO4ao3YY7mdWKrosT++xImQlcz6aB2RnmOsx\n1nB8jKmmD9Fe37gt7++diiW2Baexfjo5l+ATUTF3vpj49v81wSUSe5rEpM9P\nph3JsAkNNgXyyyBDvtPP8IrnRkKosarHxNvHtX0CrBXFS6X9zIbRzD37EJuY\nfj0KumscyisPk9lHzFPmGK+xjjgKZcU3Ae26Z6zBJgaRMevJsnyqNlGUqwvx\nuPvMM89QDdcNuEBucD3D6lk3GJcigvjE5ay8imP7cCaEXJh+lPxwA4tk+Rcc\nOwglilfddQeoNKCVx5dWY5bgmGcZ5oJuCICn/7oRqrEddGGzJ+f20Ovs855e\nSxiB\r\n=i2+V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbMJqoSSA6pON9NUfZLEJkBxtl0KkN/H91clEF52/bSQIgeTVtTUAGCzW8tq81tRLkO9Tm6uMTF9sk3Mw6xc7AaPo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903133924_1535982154592_0.6288350543476255"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903160605":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903160605","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"983125412ee6cdf452bb3b013619937f39bb1488","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903160605","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gdu5/Oaq87ntWxEWdh23w/IR12EYRAaARbhybVzf1XzZZnblD8rOU7h8wwVmAVpK8a47bQp+nIOskjKQgCWjvw==","shasum":"be66aa70649ac96b94b663970e73e81b3e2ac8b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903160605.tgz","fileCount":911,"unpackedSize":1891863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjWF9CRA9TVsSAnZWagAAbDUP/R9Qcq7Kzn55x62hDZZP\noIJsZrxXhwGmBWwtl/sMHsv3ScbvPq7K7OCGXgioszh/FrxiTSp9ZBUOji8o\nfN0V6dqmNI1u1yX+2gdVHap6wfI5FtxTsVz+nPLLK8HB8rGxDmx0BYdg6EpK\n0ojQiyhOSxMJnuYaX1E/NgcGqABG6vX/AMowDa9ew1MOH9lKjaJ4+n922QRF\nqH/rG7INAv9od9hH67xXVbmjdWCYJjJsNwsJNddf5FZoA1YjS+IjiPhYO7RU\n2Ux858gwLLnpDH0MYZLp6XiTdOFMRL92FHxyJAY/+yEAqCcMaaqrr5/rTpVF\ncYZCTl9SUSQ/62OSONZaGzf81FBlYaf8RjldHKWO00/wJrMBwAc8JAO6VYq6\nujzk2plWGLav6uLPz7nKAmPHhBQs6GHlngisc4hnvedBoRbloqDxg8wghG1O\nYguHu6E0aUNjPnqOsGMOtFsGlCoEMiilpKUxodKWP00Je6tGqxcm54WAXXg0\nY3/hhd9QlcwYQd4w88aV+5iPS6VA5VWkfXAVHA7bmvA/2VkNPrtbp48Ajhxz\ndMrLXIFMCEIZURMiP4UhWb0gFaEThCjrFsBjH16ovZgcOChq5deP4T+5gNje\nEXskSjzZjtVy8B8AWy7tbxF/bgEDEnteBsoAbdw/+t+IKxCmnLpSYcxGGacX\nCgQY\r\n=LO+g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGDARkJ7xvCeVIGEDR620+KAxAXjMlTwJCLffrLYyd9/AiAx86dcUEMLezHqdoL7Yn6JnwrBlF0VbO8mu+gf7KoaQA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903160605_1535992188459_0.8189461941938465"},"_hasShrinkwrap":false},"1.0.0-master.20180903162723":{"name":"@atomist/sdm","version":"1.0.0-master.20180903162723","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a84385dd4b1161907b2f09039cf8eff53837354d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903162723","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sZl59bsLRPgwarnL+RN4YP5HFe7wr/cjVB7VCVVhrWFVR6Es3MpDf8NwVAU2EGOqe6/YAraQ0gVGto7z37qsLg==","shasum":"7616e2e3f2880dd0407c352dad04f250993c408a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903162723.tgz","fileCount":907,"unpackedSize":1885288,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjWGXCRA9TVsSAnZWagAAa6IP/1dmTkxx2yPdVXB7UU8b\nl+I4CVYvmSndVGJ04vKOzcPPJb3Yix2xpFAziV8eM6hXSoJcqDJv8ggR/Ueh\nUY3g0Uk8Vodt89CkEZn1VpNrsPBpvysysdd0oz4rqtB7mAAMrEO69NV93ejP\nXVkY1dvARo3JdivpJ3a/sORwnsiWPoFMeaIkUwxntaf2Q5EBfJOzs7+2z0Jt\nLLlESNMx1/6MiIfKcSVh8Wr/UEIk8g/WGsB/++pxb/Hi01olxCo5POK+UbO1\njiq+zN0uUXPYZFmd09h7XpcfOQiQOHilXWGLqrUBDU5UIgrruhf5dXPAILp4\nf2CcAc9X3B+SqKTD5AU8PmiYBwD5+5pK1fkYNVlJKI9lc+QLWeLUSX/85OlF\nFjuRID85edbC0Kl1vuw7lP4UZYuP6ytSKub6kLwDIb/wDmw6q2IdaOzLqNEa\nnEBp1GYFIYtqELt8510nmqEYORoL8fNR572YzEWc6Y4seL2oGJfusB2csZFL\nyPftE+m7af4ag/a8m4q1wYBIah5nDxuuUDvMZkFPYCxExiE4nybbQ8CB3Fe6\n43ama0hhn6rbUUPxJstnU5fubsFmMwKwlp6C3MgFIVT07R+bOSmyRXKSlMjg\n/SCBkO8/Wp5BhlQ4hlYd7m9P7VbGic1NaxcUuCKgHxjfZZ7WSRj5Y5P3G34P\nw9iU\r\n=FFtB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDEEq68sAsYrPRNlg67Dv166W77CHO8cMfaEEGxMwAIWQIgODc4gl87gcbccQ8uCeeJ2YmhP63tHVcvZdYctxV9yAI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903162723_1535992214625_0.7508254499070548"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903172102":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903172102","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b1fc8ff875c3422526921eb7d77ced3a7b3eddc2","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903172102","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LpwoJlxKTr0h4/dCJ/fx8yTik87RffYQkC55yB5o6qgU4D3QkHjqkFNpqQHUonTm2mrvEoKYA+CWCebUKGLjoQ==","shasum":"c1f2263a87649390f5c8da7370c70349dce0f050","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903172102.tgz","fileCount":911,"unpackedSize":1892593,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjW6RCRA9TVsSAnZWagAAlNMP/RQVq9jinFdh0DHIHGx0\nGvgKw1u2ahWTpJHZoxQw8gRt/dV+f2i8fX1KpPHkYUbYA0MApoOez64usK4W\nCxM8agNpOLHdL13W12umAB/f0dEB5i2hpw1M9BS9tvp6MyrKQa2tMQNhzFDJ\nog2sMGgUMF7bj0QO48BohmJ/aa0PBqGPGWI/SNH4QoE4OI0GbJYqzJhopbBU\nVRKsKn37P/TgBxJbPNMYqZhHfnbkrsS5pYoaZ0U58ImotcmaLcybqVrNeLgo\nyFG1xBeZSHc2sInfJ5oUCiNpYbvTML0XK9MtnpZrsTwVMl4iKEXvTrkCVTq5\nkKa0Ek8u+y625+C7H+CaNuaM/RMr5qjbGqEQdCfXhqp09PgjZA8KNtKFXzzu\n4w9yFiFRGaP7X58elx+E9F/fmYriEmoK8xQryjuMypF2AIPSWHuAI9gfPWxv\nGyyGZRUtDT+duFj3+PTWG7j9GfZvMqlo7sSLM14A3z8kWWNBRsBNdeosuPWr\n9gfCBG2j6OUBCrD+8L1n2HJj/3I+BE/EiNhu6hbiv+7LyOkyefUfTDilzVeH\naiZ0TscjFEkP7kB427nLB3px4M5zp/ZS2Gzlr81Twsqmd0UTODXyBG4YmxHG\nW/x+2Lmwz1ZV+lIw6w61IftuRG+1DVa9rIGDurDYM7d9FI42YdiIQH3RJ+Q4\npdR2\r\n=FgOa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCNWb10r6ZUw098fIAzd+cZrRsPGRa6mSJDS0+MEoDmPQIgVLebFMeLAqbGd+t6ZLUTHC4f74j2ne62g/hF9e8G398="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903172102_1535995536781_0.42329703944623653"},"_hasShrinkwrap":false},"1.0.0-master.20180903173217":{"name":"@atomist/sdm","version":"1.0.0-master.20180903173217","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"00746a6ac902468dcfbf6635aa208f30f726d85a","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903173217","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-d8lXH0Ej9dYrI8R1IgvlCFkOaZzr57V34gFZXeIe5NQnl7jN5UdCpzSAoYRn57t74Ey3BHCEBRjZrFxzAhA7pg==","shasum":"58844001e398540058d9928a635481887a92e5ae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903173217.tgz","fileCount":911,"unpackedSize":1892589,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjXDjCRA9TVsSAnZWagAAwfsP/0+J9VUclw/VR0gRm200\nK/wXru3/xCn8s0eNfadQr6UbVo1/Icabz5gQgAuOz3i8TkNrNoqDfNOQolg2\n+QT+waqt8CmwQDEkpcWiDn7bfKdt4h8htZuygGyyGur3AS/UFwhdBevKZx4Z\nISyXBofJDKj/rUJgKy/J9kuFmNuxUFwiYDV3dw2kD3m/klsNEBmAi46Ug5/b\nozg8EwfYA1wbIk1Vu97e3Mmeegj3sa9G1atZ5E7qDdXdeLZnKp6s0Kyy8RFt\nFP9Yave0CYL8UvnjstUkeY51yWXztnc+loaf2jmfn7Y6n5x1WybKsjttNMaU\nH+UzDiz28EAY7Ul1Ff9xaWmFhDm9t/xb2Q8dLf/E33+m8ZqDlVQ5fkdXfqlE\nNY/0C3IGAKXims5011uD/g/iEfNpq5mQ+a8KbBR6Nzf63gEVvz/nNYN0LJQ8\nrPSDz6TT4BmWtO6dL+dAs2H6QkDSupJLMKYER9JVuzjzwnyHcAR7YJsTaeUO\ndNb0rznIat4CdGce5H/TysXrAalPpoHDxQ5FX47bL2aaetzbSIiY+y9PxlJg\nMfk4V9ZLNsYIL0lXsrssF+rWmbKzh0KPe8b6rez9nsqoQbsffRV621Ajn51C\nb3C0v78Li/Us8jxKgXl7D5C81Vak0FdI3jOnqa/RZz6DgUzjTsl7Yuu7pP4X\n6Lc0\r\n=b6i9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWlZKmvLnZq4leYzuOb1iVfzT/1wORcJg6cuMC/F+fXQIgTO73RyEooV5zKEdr3eYb4hMaIMCT3r8rXQ1vjLji9Bc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903173217_1535996130626_0.6839781991991789"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903175029":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903175029","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b1fc8ff875c3422526921eb7d77ced3a7b3eddc2","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903175029","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZGWJ4LFZsySoTvjp0GepLVebA6G/sA8Yl/qlCtkt6KA0uOtVV7G7A4IxYrSWfO13Qc0o5jEi+OwzRMjppNTXCg==","shasum":"c3a0ed3d3ee7df27f7e0a4a76baf355da4b33968","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903175029.tgz","fileCount":911,"unpackedSize":1892593,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjXT6CRA9TVsSAnZWagAASVsP/1kSCKzzri9Mc4dkAZ/O\nvfP0Yld+0EZfkWNNX09aaAehIVhDFLIuknUbS4kXfX2OTNKQdAPvSXIhKW69\n59NAahsYxbU8T02WyzjN2xrwxuD1lS7Me+Ekd2bwZWpezHhzQESz+be8vLfL\niM/zKL6Fgz7NDS3ZD8435N64nbyv2hLDLk1+KyGKJ570JV7eHMIPcVG2sGxe\nWB5IuFw6XdFzkbID4cNew6Y8yfhVILQyiujZuPe7c6CeSpyOeWjB1LItDve6\nrWQ82JspxcuxtenQFu40lnWVC3j7UC8pQi6bHD1qFD9G84+AbDcX7XL/GNaS\ny55o/XxTKnZ4hJYG6z1UFXdrI6qZ4Wkfc+fJr79rv8MgB/hmaRQUfL5zo2xS\nJ8VepYkjbmKOyxG69p3m4TwDNwF5uHTsbK/65cNIvuDPEac3F26elqjRqn4u\nqNzksXYQwezotYInIDMZwTIHidLeHCrS/6NKIJwoQ3+XzZfHrPxAXsGd+QqV\nYVNlTJ2pvgDN4Su3xe3TPeAweT8VpJIMxFTl/STV4o9Wnok0lUJQMMv/7Bbq\nYpaLxefIJpBxijA62+tYzSeLhMzjOboAmPfohlNktT4RPEK4wqXavs9hGd7S\n/FyRgR8mmMKn0N2HLDLS6sAjZaNPhM6V4ttwLC1+JnxxxM0OEOXQNHgqnKi8\nGDQT\r\n=zjWl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHcAX60pm0FZm5GWurTSrCYLc1EqevWAC4UgA2uuNy+GAiAuL+cwq71waqp/gSdXHOQKoegGO2aU9rNRvS43SJ8//Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903175029_1535997177350_0.4865789619933907"},"_hasShrinkwrap":false},"1.0.0-goal-names.20180903175118":{"name":"@atomist/sdm","version":"1.0.0-goal-names.20180903175118","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"22fde205ad545ebbd16cb96d1e655e0c24619b16","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-names.20180903175118","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GCL982Gy6/Ffx7oimkmd5/0fakz8C5BJC7pjBPse00B7FfYXpUVuu87i5D/Q4gDHunmklNAMOo7smVhoj+UWhA==","shasum":"f459109d33b6de72c67c9aef05de81c866f327c4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-names.20180903175118.tgz","fileCount":911,"unpackedSize":1892846,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjXd+CRA9TVsSAnZWagAAhuIP/R+YPM2mcPyAYDwdXMPl\nzP0Y9zAk708z0FaORQ9UV4EGkZ1jAjIagWiRTBfXh2BsY4mrFbSNnjxvSAGr\n8xKIA5i3U1AaFMKOinmMfrSEpIbzl0E0UOyY/WqVXMrlPDBSrGQuCLHEQOPU\nEJEI5CYQcNFGH8Mz+815FR11eKncbQBhKAH+ynjqdsdVMFX/IArkYHFbiABy\nGHOncdF0X2A9jT6s+0ninWmYoLkI72ZY6QV3UKUJIZDjeYrMW9Mkve2OtKFl\nOnZ/vIRfRCYUqXjo/G5bHW330o4jkOOzVuw3eBSQpyxOcUxv97jpFN6A0CZv\nhB3pJig614B1qrC5KaTBUY1uILYfkPHM+Io2SKIhJnU93tvuD/pw1jOXayzx\nBya8Lsw4HS3S8ITEFuQVxkjlHyxZs1avb3BfqrZ7CpZpX8//nKojSDX5ENmu\nORYH6fv/rFUIMUokQLz6WKtbAiU+eRmQscmkc5zQCmUVUp7vxMtmqBIDgw2n\nL0AxOcba+cN2OTPr/MHr/HBvba93F124yMypLWKmHW8Emfc8DP1bOhgbwzi6\nmiz0WWVTiZutfcqmNn5ayulPGac/3r7ZWG7H4kc69/Iuvd83y1qptOj/Fm4P\nbuugDleRliCiaJuXcBEBmc2fQJVN4AsiMmwZMNnf16sWa06RnzytQx0JKqdB\niuaV\r\n=7awT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD10MrblDsk1e8Ikg/NBEEIUln59Rd1M2+iE3icGj5VZgIhAPIE49ONEXB3E3qqTJBnVdUpZguQdR5W/xnHmH6jJU2q"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-names.20180903175118_1535997821325_0.9557423967440264"},"_hasShrinkwrap":false},"1.0.0-master.20180903201422":{"name":"@atomist/sdm","version":"1.0.0-master.20180903201422","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a6ee9158dc86e6fa0354db3bdaf1faa0a57f696c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903201422","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6CSJ3t1D/njVyCADNqpYi6gFzATfPPTweJ8Ik/1LB7DphqlIeUAhNa69M6tPrrhuOKylnMpFMgTXwoOUQSIX8w==","shasum":"e946a10dfe9c54ac2873946396adf60cf114fc02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903201422.tgz","fileCount":911,"unpackedSize":1893008,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjZeBCRA9TVsSAnZWagAAOxQP/Rj0PK5N/iMYLBfsNad8\nj00BlF10g/zJhbHJCvnOnnzaeDJG40dqiddUYvtBO/J7quT0EDWerNhpa3Jo\nd2W+I7neTPB7bwHt3rymDiE4zhJAVmHQtbt4LVogdGQkePauYkEMoUHwVGed\n7L3IoUJxl6o039mI4gvC2gHSkU2vBlZ3FP8tKrZNRe/c3vseMOXLNYyeZv4b\nm42zhW0DADbD91t9baWdbATZ49E3laZgWzzlQeTGoas3FxAFNBrCOb6mJBvj\nQcjXwzF3Bwb9Nr83La5jD9/JpW23WZye+bi21ksnRVjQj1o9UjSYEDa68tRU\nrnkE0XRvkWr8naCUkIwmUl3qq3oOfDeTmovudFQru4WhZGBzFOd5q+/OPgDN\n9x/21sEI2U0RdjfDdjZ1fnjDZQqnl1XbISQBwvn6T/t74IO9r+Q/gpe11338\nTxe+6e/j54mB5wQ2Aed3Xb3DBs3o6fAjYaAmnHZt3vikuqv+Ynrs5Oo5BsNq\nRlVSAPUn+1IKZ538t4kPApKLGq5jsGe65irwQsgsKMawEvFWzaI1/ImFX6Yd\n+yiwadxHnIbNqRrRR7YVRFugCOi8O5p7J0ZN5B4kSIbb8hsvweFjoeqyUJhQ\nugpRXdxJXAvomrhGaQGOnrS1bt/y8h3zWBazaWgZ0aCfBhUX30Qb8LVvwbo6\nxGQB\r\n=2Y0q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/s2TfWhkRb+ptQAP7LpSTwJsG8YY8Lr6EeYaX9WZHNwIgPTAISFKYn+oyug72/5kZtMaikOdnWMz/Yl1Ktyhyr34="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903201422_1536006016155_0.3797573212822869"},"_hasShrinkwrap":false},"1.0.0-master.20180903202920":{"name":"@atomist/sdm","version":"1.0.0-master.20180903202920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3c2613e08ce59796e71429db8054cfcf810a4906","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903202920","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TuOnRYK5anm/zdAwVJkrAW49fx25Z92/tAiAsnpafQNhRi99KnyPGxq58ylYWsbVRBKdTOmzWY4Plss/B5RRxg==","shasum":"79341797e02e08c709c524d252318d99710ff0ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903202920.tgz","fileCount":911,"unpackedSize":1893314,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjZp6CRA9TVsSAnZWagAAE1MP/R+BqfDT3EsnpqpRtMnm\nTX2AJ31/4+BQ6BoGH+KCwz68BlZW2sIV7U8JuR4G+RURRBhg8Vbh8dQ4dt5X\nq7m5hjmDUgz48NZcCsBC9tgD1kpRpcEBRtLz9XPFZamjAwQR71nbu5G/heaQ\nRiD3LNJt600vgRIRWxuE3a3sSS1R3NL7lA7MuqNSL9QFK7JeLyJpmW+dra8G\nkGujkp/57dc18HeEviDoHfqOe/jOpG5hevNLiM0JH7MklRuu2c0886rYHUAJ\n/BHvwUWgSAVnojxZ5iSdj9vIhQVro4sf4C0cuJ4KA0CGgcIvBI1MhQHMI8t6\nmvXqsXIc2lvaTVwcXSg1H66SSPA4zZpv5wMu+chptif/ywjLxU+b34wQ19CC\n2Pu4iRIAF59aqWVDADeCsSFcQ6dsbPdvHi/O8m9WvF7i0tnrozdmeBVaG8LC\nT/g+UDeDRIiO+8CPne/Y6IlbzIRdEV/J5G6qffLrjiMuqiM8IUJ5Ujc3Up6m\nG8wt3FlxsHtkutEt7LnPf9hKeUQkmriT3TxFfSQz/l7LKNxey3IWqpr9hwGV\nEgBYp8liO1fFq5nM9qc2WfUZgFYKe2muy5yoViGvsAiF5FMtBVS3JG1jKCIj\npCIwXKKtxplZcLxy2pfeMv9D1RNUKi9l+tR7cCxDebMXQdjPuRA545mlLM1B\n9PeQ\r\n=NhRU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDGJNk2IcCobdvT6cMgyhvD6CFaR7tHaGNi/s0KKIHGAiB3dHH3GA7BOrle9POCLquf0sN2s4KL1TaMbcRB372HNQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903202920_1536006778126_0.9027518356612854"},"_hasShrinkwrap":false},"1.0.0-master.20180903205421":{"name":"@atomist/sdm","version":"1.0.0-master.20180903205421","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a648c6e1a8b7ab1ade95faa741d648d92aa3d4e7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903205421","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7TzTGPVUEZJxwuCFYaYs+X3x7pXh8iueJoIOIE8ahITuxY0WjPxB98fRXn+zQSBwYXBPvYPH69PKWbe8CZ8zUg==","shasum":"7cbceb51336fa28f4d0c8948dc482b10f9083718","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903205421.tgz","fileCount":911,"unpackedSize":1893558,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjaIKCRA9TVsSAnZWagAA3aEP/3oa7rM0Ga0Ow/gYZC0b\nUa0ZYHDCfbOYqn/hcNBvSQ6hvn1K2J/mGes5GHzrEJHM3h+5Fm2/ItodwYk4\noOrZSonTIAbH5CsIgMwO9MZTwj0iaXxEX3uvh7npzN/qeZ0sQx33D0UmiNQ9\nbsG79kL73iP76zf25blGo+KX6/0U3g6KCMpX/hwvexkNnUSP6yGL9JNSdOem\neNaq6+Zo+Neb8nzYJ8sjAbasN55f2Gcn6p5MnoguTOoqMksdwXdakTLsywih\nRC15KeTVkHPGNfh+Fg3yiRM147TTLiauy0IE1IHS6nBDktm+reVpAxzcs+sO\nqc5ubCTYwJ2lGzxOYJLYf2DI3GR0cooZjsYLvQ7I+13dR3n2gcnQ/+IHNwoJ\nEge3JzkDAF8WnIfHVNfl1lY/pDi7lnXnQwGLv3YuSwq5hT2XqT5i2DnMfc50\nGkFxcx3Ns8LAk+xb6xk9OgUZhjSpr0biMoghOXlOFQE+AqU7ZWkgvDC8T5OW\n6iO4dPMqAw2eYe33ooXzQRGBR6y9/e0J2uoiva3V3gBIMFB6nyt2TNyCQmOP\nUOxK5c8u8qOf5ReDMhet+vd2zfY7A2DUeQ9hgDGJpdBxAerGMakdaXcwn5X4\n0sR+RHh/fwrU/1bRE874uuRr4isYBvgHR9tlOfF044I969id6hghY2zgWDlL\n3N9t\r\n=hbr7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD2MGjZSd7n6mGsck06VvohzubD4lp2g9ymWC7mzcia1QIgOOjeFXTSa62KSWwsG05e4ctegvNuBZvmETHegc1hIOM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903205421_1536008714231_0.004433338399239206"},"_hasShrinkwrap":false},"1.0.0-goal-cleanup.20180903214357":{"name":"@atomist/sdm","version":"1.0.0-goal-cleanup.20180903214357","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1f22ac449da8164ebd142c725473b49185fe1c06","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-cleanup.20180903214357","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9vVqGEUeKZT3ye6oiHc5rHA2ll33cE/AuIrQ5U1ioUY/tOUAq/VVsDyp3dZbVgNrHLfaR7wPJ8shCSbchl3lAA==","shasum":"7503f2f69a8dcbdc2b732a53e8df5b1456ebd7ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-cleanup.20180903214357.tgz","fileCount":911,"unpackedSize":1890942,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjavMCRA9TVsSAnZWagAA9GUP/jZ6diZ+W2ZdvZiCzzR1\nfUosFicAaRiw3MkAlC34+fhUfmuGIVLwbyo0VVINlm7PLNZgrZ9BjwnrTtkh\nR13oByrErbAd+dR/tFOojs6g4IGhg5tgpYV0324uneVkNvjM9DfKur7UHIld\njoMM/l4yTCXUthg9on0zl6msKnfkMCyrb83wa4s66xNAVrGt7JRoARWHwhgm\n4Mgaio+lfuS25/yLI3c9uubdpUv4rOU3mse+aDcMllhaQHnKwO9pEFVZNj0E\nRfVSllBiR+oXnuafQ0jjMpGFH+UC5uuqoQesnQRxSVuYe81oJFEykxxCOFBK\nfWgPR9Z2xVCtDsTxTKcpCrAA7DJ+uGPWgtRJLP63eS2KscRXdYpQKgI7sfig\nTyTJP5YqD+sLN1Ld5KHu8nE2OSmKdlBZGjoGpgHIbUo12q7GXkPi1nF+ZtK0\naBr2JQbVAey4QhlOGxDhzyWf6JdVpmThWnSeWgNaGucZWy5jOlPA5TGOLRtF\nWQDQkCqJYPNOPSz2A+/+isdkf1IkzGdqVyn5rCiBECSsJl90xK3P6UE6AuZr\nxx51arwssp0q8jEDkP9YM33u6YUJPqCbvybM0JT4VXYD4B/Am3VW3BCdgXM2\nTsWVYOPRHZtEYbhlSoOr7VSr88WULY/OxmYr7qpO5fJtM93Gsj+e5Rli3v+N\nW1Ee\r\n=sxd7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJxhHnTAYUaTQhWu7mQhOSmg/bhj8V2z56fOx+dZHDnQIgbFo2jHy+k3NJHyCc3H3qHZyUzYVZpkEP3S4eRJfaua8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-cleanup.20180903214357_1536011211428_0.886581040119689"},"_hasShrinkwrap":false},"1.0.0-goal-cleanup.20180903221226":{"name":"@atomist/sdm","version":"1.0.0-goal-cleanup.20180903221226","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"9aff2a0f61829d981b0b0cd44bbe51bb96a21eae","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-cleanup.20180903221226","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Cgs/zmXyGiL4W+xh7yJQrJuSfdzpbqS1yRkfl330yPJVa5KUpyrWVN9FfxQ8VU7mu+NNyq/NUKuESnCfy+jBOQ==","shasum":"39b10c828f3770bef8e24e5c7d21180251f4d662","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-cleanup.20180903221226.tgz","fileCount":911,"unpackedSize":1891391,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjbJvCRA9TVsSAnZWagAAVhsP/izCZeIeo7Pl1B7cZzXx\nWoQVh1HeD22Bi6GDXfk41Rw0gCpYMpzwZOVpyy0iS0Fj4EXKChGim44f+fAJ\nua/ylzEqe0VN1rrcu+HSxQk3SSWutmGcfPZY6QaTv8+POZ3AVfJS+/AFA3at\nCebugQ/gqxvbrZvskZaAscpmfyhgIc4AOk6WPug2I0p94H8S3hCUJoW3bqzQ\nap/C3Kg8FJJFZIm5N49ZQLj/QX0XJJQBlfjUk85yVNuMGN5mJET7yfLPFxYx\n+QSwvKbUbBIP6KwWmvKEtHXPp4GmmFqX8cmfh+2m8xGHyUNEUHp4zyt1UKjh\nTK7hglA2eafuZ7FW0iBkq4+EHW2jchcJd+zb7AP47M5GQp/8YFVHnHYEzf2O\nnjeOMgnIeXzvSw7Y82Lm/wfiI6RuDLkpMheg2m9BT5BFY/PyX3Ez+kyMib5Z\nQ2sEf/0AgI10e9lPUMMojWe/eBzMI4je641HZidgCYEEYJuPiFv1u2C023rQ\nYlctAmq4kj+tiNdqnx5iggIc+vPCMyCEIGqX/6sEyAhz3tcRsuxJNV3HsG/q\nbnWIzU/gEfVUsSlvl9awmhJ15B9TJWOWPu3NZnCIGya63s7YqvG+J8sXO0jf\nH9+88k18cjmuNsmRy7lCoCDIXkBB6InqXDThtbGnS9nBLmiA2lqiPgC+ddxK\nW8o9\r\n=9jsH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAcOh9soUVF43o5A+uieEF4tqYjLGc06v8WxcdKtdqPUAiEAlL8xSKloE/lSm5smPe1GMNhA2ycCnu8117m9jjEkIpM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-cleanup.20180903221226_1536012910503_0.813739201832409"},"_hasShrinkwrap":false},"1.0.0-goal-cleanup.20180903224934":{"name":"@atomist/sdm","version":"1.0.0-goal-cleanup.20180903224934","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"96e9e490354ecdf0303bbb3896ad7d06f64ef8e1","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-goal-cleanup.20180903224934","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/Pl+Y9oBDmPMNCaGw/FRcXFvYD69ncrbgoa9516XGe+ELMfNE/QHqt4iAB+wRFX+TRwQnqGkDFkJLdwCeL8Ocg==","shasum":"3ddfd3417f21b8fac9069cf46ccefa2a379e9b37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goal-cleanup.20180903224934.tgz","fileCount":911,"unpackedSize":1891791,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjbr+CRA9TVsSAnZWagAAfT0P/AjPmln3wXNPFIGkXOyZ\nW9DXvlrAd5p4ag0evw1rGpy1Q9JLb7Uz5QujR0C8BBspDOq+Awwj9GANGtPm\nQ+JiQbFreA/tJgY8mEwhaGrAlXbqNoeT+XMhbaTmo1XFRX57fu+FFFvKPxDi\n8gd0fUBVQnLKI1UvSph8z1R2a2Ecb/WUpavU1ohwRDZgQCUQWiaLZOzcNbqw\nuvn4erx9EDWQvNFFUDnr4pqRCc5az2NvNc8VP8/vvu9iKZt1K8v5Z31aXaZc\ntKpa2WsphJ4T5i920psUdS4EIqn/XnEcAnMBT+Dz6S03myjyiCN7JK0QFFQl\nCs+DFD4oHKgRkRxRxdymfkRFFH6n1mSu0AuL1K4LUkHBsJiAXQdPDXHesvbi\nb1Htfs0tMhjGptW4dIIdNyZwtfSNOJWkKae/7Rr74s1RcOavED2Zfq6/UmEW\nSivxrzfHnX9nZ/7haNN29iyVkEx9+i88/62m33Fpcuu+HBf3e3XO/sISlUm2\naAPEwtgRM5FcJ3LIqHL6Hq4hmZgUIS9Brv1XDyf08eD6L1YMdJwpEBbBs21j\nEOHCZ/96ZyOLmvOjA0FvdVveaZLMcN4kXW7Hap/iskDjNNVGCRpNuBbPJh/B\n7S5vO7n66Q+Wr+MUP7LHB0aekNPXgIVo+VXYpnhATXiCBo2pbGzAAQq6R/Ac\nqx5a\r\n=wQp7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCITJRYuy9zdYyUVzH1SSF3/ofysBMHttmpU4T4DwcqywIgTUNRQmDWrAd7PY/DcOBEWBbAgZRl7YJ+Rb5FJsI0yTI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goal-cleanup.20180903224934_1536015101309_0.3094828751543004"},"_hasShrinkwrap":false},"1.0.0-master.20180903225558":{"name":"@atomist/sdm","version":"1.0.0-master.20180903225558","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"6ec243f76606c3df0fa80577a6b88e0f13a8cd9b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903225558","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-88D0MuyiakHDIydujJPDgoQWY0YFttiPADaowJnULDAcB4Bjiyi0d3xK4hsFgDrJo2wQTUnRmGsKGMv6l/kK2w==","shasum":"2fdafe06e7d88f16739f9939c60b07287bd7f985","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903225558.tgz","fileCount":911,"unpackedSize":1891785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjby2CRA9TVsSAnZWagAA28kQAIxguABLVPYH15SJhLMo\ngPqiG37FoC3rYqqoqTR81ZRfXIa1cewC61sPq4xV9sErWmnheigKEXwmiXqq\npexQlX8AE2WzTQrikYA6oPxz0b3ZADTAxksbNZm4YGiz1a0NSWaaDBI2bTc9\ngF8/okkZOnXu+Q7by+C0lXToZVVsV2gD/yP/fHpW4AIToDtpdAfHroTzEYpJ\n7kBlywi+hvVBcq//ieZ2qrUqMGl+9gVVM5WovnCouauI8z6UEdp781fVkUAm\nTNK4Wlh+Mleqp1RpFYg/YfQcqaE9w5GyP2gp4avWEAVVULBYvYGRAHYpY7dy\nFynnepS+RToppuGuJ4et091Qt1J9obbd6yk7sXJMBDeTMxkca5ZzYpuuh28D\neZSwDzp/zPmM6qCr5ycLJgoG/yHn+OE2StJAQKn2MkJKeLfGfYsKazLSWEvh\nHEC+c2E8TO4DDZeKtup4FnO/J+0moH0PfNnDEDK+kePFFbo8Pp1zvUu4IwHR\n33e1Y6+OS+6aufrB6wjhYDAkOe5AcZtkKY8QC752pxTPMBJtzPCDS5cs46hw\n6x4W5dXUa4x00LTB581gCvXObyk6fbT7sfQ28EFCDhChq5/J1x0wow8HByJO\n7uU61nVDWCrhrjusO4S8exYBvrhAQIHpMFimgklk1KdkgRe0XrfR2vUDrOdy\n0ULl\r\n=Sdyb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkoUfeMndP6MHz4H2aRkcJBg+MDsENK3YiSDkegl9EbAIhAJDQ8U0md1vuwcsZOrZh2/E65H05vA88cmt4+VwsDEbM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903225558_1536015541971_0.3320989196632256"},"_hasShrinkwrap":false},"1.0.0-master.20180903235234":{"name":"@atomist/sdm","version":"1.0.0-master.20180903235234","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"64a8e14fcf005c56b032e192d786e17c79a283a5","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180903235234","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZBjtwTrhjbkoDzIBTX+RtCg/OQzb99x5VKxC7SidrbuvkAewwPZG+qIFNtwQTjYjhc5qzjvpRDVT1Pi8IY9YGw==","shasum":"1af7c71200f0b7c31b102adc586b30e3a27909a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180903235234.tgz","fileCount":911,"unpackedSize":1891624,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjcnsCRA9TVsSAnZWagAAUa4P/3TXIXKvhTlWxoF8ZrfI\npUD4vCwxviuSCsHBCsBKPYOKvjH1DyiA31PapEBh5eb0dyx9ubEIEB0YXZ8b\nOBINysfUUWKnGI+GDgdUC1wEr8dDOdfnT6u309BdqPMb/IBBSDD5h0MZgK+4\ntKFkF3hooxlbzfFTYdB19nnCrbmSTFut0jhg+wPzklkYnZaUoRjlOsPNiuao\nQGcJol7HVT48qhsunJvb5RikxluWZqeYXlDYMFF/XREzrPl9gCTWp2Ena1A6\nO+yalu+p4rlsBwJgAvGJ81InBphnm/l01vppW5NMbg1VzCJp1ANMCJCEjo9N\nKRhMIZxhUJUx2ClZ3PwG/6ncCU3FT+mJfpcqlxz3CiXradUkkzg4nrWLj8B0\n+1hH2tmvFbs8l9mk3husDA1jYn0qmqFzQyw6um5bD3iP8F2STTpZTUSIV5MR\n2xc4x/+OU1lVRTx3H9Xq3hCmMJjQkrAwT7Ad7TFKMZnpDW3LbY95vboGUiEP\nym0Fkcvh5+uR8mOJxU4vVXFksdiOopccBqb3TG3pL4TqOdNXRh8UfO7Kd+qP\nlVCKB4vIZA0NjMQVWHYXjbhHJiSGov6ksbEI3+3M/UIcaOYsnnduOn/bbpTI\n0eO1ZJb0w8XZyRtM91oV2Y/FDdgOQhVAU96sZ8LMX/FJ/IneKsv4/7+RRfYT\nQYYB\r\n=uN7j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFARbSPjc9Wa13jFq+L7mGJ4uka+FHW5UKoZIrcskhiXAiEApuUOFR6CtE2Cf0MYzRD+TgdFZ2YmBSM0+2Tz3fPG+KE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180903235234_1536018924070_0.6936990927349638"},"_hasShrinkwrap":false},"1.0.0-master.20180904000529":{"name":"@atomist/sdm","version":"1.0.0-master.20180904000529","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"07cd94a8560fc6c7f8e955c1d725e6c09a0f305b","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904000529","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IpGJ3aKSJIDI+1CuxN9a3AHm178Nl+nGKvJTcYiF/bar7nOpuu0u90aFIUVGmLytkyTBC8nLnZezX8trSX5Cvg==","shasum":"ee04cb8c20b14a672e248a2652c3344139b81960","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904000529.tgz","fileCount":911,"unpackedSize":1891679,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjczwCRA9TVsSAnZWagAAhNgP/2/FA6mqmJVIcxrzfoSC\nih/FYwJ3S8hal11ulJTq1j2EafMLw1OXCdpyK2kJ0e1H9ZgOCSJCiSkTc3XV\n2XhjH/yESNlCtGQKcEcjv27weTyMCHfK5KkbLcZSVqKG9+K5ZNbUpC4kHGVf\nru5BdVeFAv6aMCRaR54fRCweQM7ljTl9oCltTdiVoZlmO4Fn4qr5mHv7xKvF\nSeefKQHVx9OSr20/3kbgsC6oX4/QGIJhE8UAKVKNkUi2cA7V9disG+slYx3g\ncW6PJG0LQkEFUxD/A8uuwdaKKQV4uOoDDQtzZewEOL+EWMTsNpcSG+J4imYF\nEcNSFXmR1Oew+zij6R3bqA8mf49E/12E+hnF3TkGKDk7Jya/+Eo1C9sH1QYj\nuP/iopJcd2nW68135s63QJmUyVY8EA544qATLHm5Gwtd6hofWlLbC6T7I8kK\ntAs2TX0Rsv/TblI7dBoaYTvC/mfjlUcKE98W9BpUVLZa2Tv6wZzdwjGYctly\nAUIT/JfC4w2mm86jKN0iwBBUaXduLfZIxFBreYIlegmBCMTzFYreYO8pFyMF\nudTM5NpoDSlwtur2jHduZaIXkomYcccmKUR5Atx6TH8UqPhYjv3k1jjKjzhb\nevnvNHGy4FXldqYoKdsY15AgZlQS8EgrDIxcQ5NduD/5YfZ7Un0nFGdyAfr5\nDUeO\r\n=oGgb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDB/5BgUk7eesx8nizBitE+26+/vLBTh5FGptOlvzNluwIgEzw0cbJBVLQ82USdNkylz1K6fN96PK+Z1NzBgi3gRvk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904000529_1536019695712_0.7291451283452508"},"_hasShrinkwrap":false},"1.0.0-master.20180904005606":{"name":"@atomist/sdm","version":"1.0.0-master.20180904005606","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"739b14c0cbd8498dd1818c54602e47195e507d27","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904005606","_npmVersion":"6.4.0","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LCEhOSd7FZ5qst3d149RBNp1b4eJjMUcPF6nFDvEtROVsA52ivOt6iVh7Elc78AwUVbKOjyMqaGuz+BLd88Scw==","shasum":"980deff80be9faa79cd4d1cc3ff288a7157414a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904005606.tgz","fileCount":911,"unpackedSize":1891756,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjdkvCRA9TVsSAnZWagAA9LUQAJjIDwWRuuFiNeqXxGVh\n+mwSxuxa11kOYgOo/v4QsQi7dakROHlyoMY9x/yRcAtORp4K3QhqY28tsOP7\nktgBMDNSGj5gFy4wo5YDtqabpHZQG990/MYnlmpeJ85gP3vUZRbPCUfUjgAq\nFiVkGovlh35AlXEBtb2NdhvuATUrmdjJuXh+ByfbM+JTsz/OswvTk23apj4R\nkSew+WwppOWHF7Ru35odq3Nwc+LP4RXHzLvSRNKhJHiaQBTPJ18bjmec0z+n\nY3t0rhI2cLUDHkyrlc3QofQy9jiWVwxDu/zR4nR/INbRme7o7nL1J2OL5ilk\njQh+fpV62kDXlWbDzIV3WW2mNwIOcDo3awsk+L5bn1oQ6J0XGaEHBBzBkYii\n+AoR40DZJVDad1vBUdZHVlDSJzLgavGGeSYwgIXzNTaMieHviWK/08Qg+n+r\nZKD/ZqYHelzA5ps17/Y/Q+f4ySHZ3oCmMiMb0lxaQl3BVC+OUduMkETh6JBF\ncMxz23GRpeQs961H+Zx0qj5Pu+osrbSaHJnSZMX08dX/0XY+6dkIign03UGc\nKaGh22vhYBLbl0/ufwn40XCWN1TjZ6Pq+BLl2tRop+8qXUXZ2Zeq4Kk9gUKJ\n+ALHeeVPouvAZ75+yDTyj0x/FsaYjd7m4SoAtdS8OhPQLn/6DzUf5hY3/O39\nedXt\r\n=Yw9q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDbvTdeXJ3IiSVn2+QiXfR8ehxgGAMYYKUSNzZ0nNsQdAiEAkWMECs282WurpDGrwbU7EuimWhANZJKhtM8YEha/tkE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904005606_1536022830059_0.26523498445577265"},"_hasShrinkwrap":false},"1.0.0-master.20180904101721":{"name":"@atomist/sdm","version":"1.0.0-master.20180904101721","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"27cb8a0fd2c766617d0636f383dca9542396705e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904101721","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4EqKJo+5JqoyO/1cyzegjiJyE1HNvWclNBLAWb05MY2GvXVgChDti9aCko2pS2+eYh9tguegIOFm+CYx+u3mUg==","shasum":"3c747edb82d727abb5a079955f1dbbcda62fc6d7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904101721.tgz","fileCount":911,"unpackedSize":1895668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjlwrCRA9TVsSAnZWagAAEFcP/j7NA4rLMoXWfY6l/JXI\n+CBQteHb7CL5hwcNp96C6A2iwLUwRJRR62Gr/Z45oIIRzLXkMBLkErvLDnzj\nTokAgopOip+l8lgL1ZJgvRS8Yt/GfyWvB5Wsr3wBIJRx1p8jfHNlR/j4f0MP\neK165Ll1JrqMGUbsK9YawKR6XmTRuTF35hCZ8xfh8eEfnsfNNUg3xDLmSunx\nDFgq0KzjRkCFox6daQU+Bna8H87dX+RiGYCMDt4hYYys11iWRS17zziecjcL\noM0zYkRd+8bNc+VJAFxRE1yUdestp7eQb8j9v7ACRY17zb/9x0pYIgQqx1aO\nMQ+XcyInIAEx58MJEkEd2e1QUcZxqPKMTT1rU4iKwT1gvntUTRu0f78VFgIy\nn/jzj1Sq4toslmlMDlGYqldcEpQSg9GlGrUGKP51Y97iG2X7pyXrsv8i5Rqg\ndf+NFWcrVSNFexDV7IuoE2a6Ni2V+N/AqHTJfJlPiP7Py2WFR7F0IGTNqsNW\nR6zf2UMVhoSdjDocBTTelqAzCmeXpXBc16/+4zjJBinlGoAHOcTq7lWSe148\nk95B27b1jW+KRVl0/gmgDeZA0lG6HTn+PQIIVew24/9sZCprB33uyiKA2Nka\ntrwUMNoIvJEr5iMrPUQBFC0YfbsZ8SdspXiIf7DaurNNJJ31zAoNzIq/k4Ch\nBJkb\r\n=OJAB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHReCFrPodkQHpFl7vUCGs6v89v0QXyLeFnEotjR89YlAiEAhs2/XdvMXPObK/r2CV7AnMfN6ARfGDKtxsInF52snFI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904101721_1536056362941_0.9898235277683776"},"_hasShrinkwrap":false},"1.0.0-transform-result.20180904124741":{"name":"@atomist/sdm","version":"1.0.0-transform-result.20180904124741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"95cd6608e8e55e31b9233766a5aaf507b869ba3d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-transform-result.20180904124741","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2emSFP5Bc1VfExoeWwVOht+IR4SUfuE/ziNo5BTrDnEf4zqEz7DGSb0nP/oqEXGjHQsP+Wqr0A2ORikJBWt+PA==","shasum":"acee6faf9c3002f3ac8ba72b7287826f1ee9ba12","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-transform-result.20180904124741.tgz","fileCount":911,"unpackedSize":1896599,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjoJMCRA9TVsSAnZWagAAY9kP/2s59C28NtVoR/jizkL6\nRSnT/IYzT2OFJoaYYskLjuLm5uNZfZVLeVqjrDSSWpX8N2gwode9AfmDgDAv\nuSn5huAcemMdLCFcIEVDZ8GGx5n/yMPlf8d+uM4rnNexW9RzxPWuuM8okIOC\nR81JeIWkubpdArJTzWgzdTYYkbakYoc5DzaCNf7GxHupHSB4Ul1ieiPTlkB0\nwCQwmd6YC3w79YL3lngdeBiv2HHkhu6/XpDURaYuKaVMaUE+KkEkbscYR1DY\nMmhfzQ0P00fYCj1iG7j8wz+xDda+dtjcCw2dfSPhDwYSwFyaVmRGx85LU/n+\nsadCZ8yDNKvtEblBsEARHQ7RQ88/+2EFSesmIwEGo44tavyG8nwAoU2lhsJg\nQDdejIourug4UWgqPmDgXnE26GTkF0heAlYJw28Qf89Q+bdOLW+wZ1ugvkgn\ntzXEMm6BBNjtDftuHxDTgx656YhhZmfXjc+PydJCtRD+HqgV0Keok3bNggkc\nXQUD/VcNF4rGeNPiyZoFuXmNiq2lh9sw1PYzKZg5Nd8lshpyqaMj7TdEjOz1\nH68KWu8HX/N5JU0eqtpe/9XLOitBqWo3oULN7erDoRdZr9bl00Czd4vk/2wW\nNE3wJ/+va/agLH5JMS2WiuqfkP0yFyCYxAcwp6FGs8qf4LIkQtMHROkwnavm\nuEzQ\r\n=rV8T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTc/x+ArAhit6beFpgMlGDOxVX/PrHZsTGaMwrWXvEzwIhAJvql5+V9nUakj/VAma+er0OJJlAljWebqDXAPqNgYJO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-transform-result.20180904124741_1536066123818_0.5866682132730203"},"_hasShrinkwrap":false},"1.0.0-master.20180904134435":{"name":"@atomist/sdm","version":"1.0.0-master.20180904134435","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180902103822","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"41403b6149aea9dd8e506660e5abbfce49964596","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904134435","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TxRbWiOxudpT+Y8KPrU58XBd8aMIcutzc4GkECSPHuxELnh09WEU2c0NXf21rlKDPSBpB4FMk1RganNKqUpwGA==","shasum":"2f51936e71d0c0794b7bb664b04f525a7f44bb5a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904134435.tgz","fileCount":911,"unpackedSize":1896581,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjo8QCRA9TVsSAnZWagAAJDEQAKFK+1vQSf1UQPy14lXK\nkFZzE0pvlw298EX/YeMVS3cnB1/+Rj8+64Vt7ivWOckH8ffBnjnHAcbUGXv0\naDeJV/PaVJ/rAAghgPalxPOCpChgmdtzjmD2F46cXA+M4rHdayJkPbTpoZyC\n8w9+w9KSdzq547GkNhcaantlB7KutDnSwj6mMetgn5FaaMbrpmq4gSGItlrX\n5bWYjNGdJmBaw5yoYNRuzWZFjIXdiNR1ui13E6xFWY6Pc01ziuCpf4iwHwc6\nB9fL0mvb6hnL/F+ZD9oC1v7SqCXz9Mz800tKkqRy6OcRrQNKyJklPAXArwM7\n7nu+ttDvHlaDFqsCpAWymQR4+L9LB4KS0oXpKq+h9IBvu2ImG0SkNSqPcm48\nXxX02CvLha1Ep3UIgLisW81d74wVBJOr6cXniRU21V8PO2z32VGQZZys5rvB\njCgYQ8EJJcoh7SW1u9Q0SGFDDVL5MWYeQG5Pt7mTVzx5JEVyY5J9zqYBls9H\niQ1+ehXqQU90l/NaDh9UdpnUE0rWt7MHnuVU/4vUqgpC/NpENAgmMnNgU9WL\niV6WagXKw2YOgJ+C8Vd9coXYfTr4LveHe74wXBnOTLYpvq0eU0Do0ETy1la1\nd3yMc9WDzfoMJ1E8/Vp0TViXIAiNCSbbu5Voa1hpSHwcbplsRyqE054abwvY\nZGWt\r\n=+YsS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC6SITMT0YS/3TGWKxKwrli4bSxvM5WFOlbu3a8hlsxtQIgd6rGcNRFtq1lnaUwLD43HRPTJn58h4bY5OaBEFb92Iw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904134435_1536069391741_0.07218266999412726"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c511f95dcf7bd4b01584b98f6069ff6ac8d86bac","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TYqYIvLHyFeT7MsR0TRYyILbJFo84Jl+OJw5T/jZA1CJd28dJJN/BACoPpsbbTS/30UEU1XyFC7Sevk2+ETogQ==","shasum":"aa0a00ad64638a32bb790084d72d6121140eb506","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941.tgz","fileCount":911,"unpackedSize":1896595,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjp+hCRA9TVsSAnZWagAAWhwQAIAslix3y8E+HUr9ugqQ\ngq3bkC+Ed2FlchORNjfmL5cTLCspKjgilrjD81UelrnZ2Tdo5o4MDsmwlbAB\nUo7X412Bzby+03uSR3ZEucOwDvb5kYAoA05xv5WDF07sF4CmGN1wW/SyPDV1\nxbz1d45E4vFKkh+q5cXIMx1czK9ounkxVmNYgIeQ5aF8avjzdYM42vm9e6+m\nZRy5qoDwk04SpDprG/5ZvxT9ArPQCHprwiQ1436NHgvyfR/0Tn/Esm1P1bxb\ngIje+0bPYzyDFSAHGBgP6W2IpVvJduR9aRzLao07ixFXBPIvqdUdQxJVzF1x\nxSiPC7rK4U87ZDc+SVrbWHJJ6Nq3IkAYLuJxOgYCKCnXUnbz9iUxpYiE3npU\n9sCjHq9Q36qjifeGwXsF0ysvArxng4YI2zV+ZTGDdfK5yy4P0mIBzl6cciZC\nj48zXyuidfG5N1LKsyWf8YYPAUZ6ps25byeW3JB0xH/y7EIscAOYA+MCjMmJ\nA+yV/XPdSRd+dX2w7bvX3bcvbULSAm8bAXTpXtCdLBci7JWropNva/8HZQPS\nun9HdY9qVURQ9as6TLqB7uh6oTb073jh8bgX1OrQDJQ7KpXANiQ+r8PFf+tf\nYz1njtXUFBUmxHtLW7m132qbvngxhi4HHX+ONXlwXQVSXbvfD/4GEH+pTra9\n+ok3\r\n=PSEC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHtrXL68WSEsmCna3rP9zpLPJvE2GA10tXOOMoemb0XEAiEA/DlXjM3hw0PugtOxB0kj1dCXrWI2n4u7x7p04KAZrkU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941_1536073632470_0.7260607657109213"},"_hasShrinkwrap":false},"1.0.0-master.20180904150844":{"name":"@atomist/sdm","version":"1.0.0-master.20180904150844","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5362c5c2d019014c088945e84eeac473cabdc97c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904150844","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OiVHnsyt9z0Kuqu8weYDA0Za4Up60UK5OqQDY4aB+ljJPvgYVE+8b3QYbuBqAMeAY97yThsTCgyZ+k0D8LswVA==","shasum":"be25315bf47c4583d73c8e962fd0f6a909d20ee8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904150844.tgz","fileCount":911,"unpackedSize":1896545,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjqLvCRA9TVsSAnZWagAAa8cP/j28CUtaHt2ULfuOIh31\n4QPXzt//PfIdN3syuoC6eROUzuNgblHjg+u5mO7FDUG8e1elSsftHkGDXfGS\ntfeSDhwGBYwIhls34aCLVcsdZUL/s/29dWv/6gJ+Cxj5LP0aYVkyfJG+BBJ/\nRLFniGCMbK4THCwlE3wY+aw6l7oIpO2vAV9NWKTuXUecUMMSxbOE0eTPcZtL\nGmhAGzeopKeflMggHHbYoheEnofPKQXrZYHjUDY59cuiua6EXfPDE+dUV+9X\nVp+m7A6gEJLplnGhFr1UVaUO3aW6qV0CSnrzfRyWhSVZ74kJmI84fIvPRF7A\nd7UUPhNHiyRhbp1LXDO9Jnw0ezrO9XsFxpC+hrOGJ+5ToyxCQS/75UbMxEMd\nShU93QbUZ6Iq1zXog2JtbWN8btdJ/1ppWPTu/OK7rId9YEwUrtbr3FJRoPRq\nUXdPSh+53wrmHTT4ARpUNt1BMTNaC7KJK1VCWmR+aWzYu+3u9VakaHc8B7IA\naCcrzT+vOFXvu4lbVzQnljOvf9G8nqkOZ2Rv4SPe8/phJ78ka2U2gQJ3gTHG\n0sqNbyxGMLnOVyuSoV7bEgg5EjAp1/NQtP18//UFlxWuSuD7crNy5JixRwrl\nE1KkvlOYdG46yodH3wtu5SLzAxnAgn/LAt6baDE3f3IrIKj3AYleksCXKdGg\npT+C\r\n=kgrU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDAyTRsfa+lMQhC0kw8c6IMhFtBfEkLwezvRNzo48/CmAiBurhcvL53wbymuGb0rQImqBq8J/8kU6W+PNwTDDpV/aQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904150844_1536074479070_0.5862889240041003"},"_hasShrinkwrap":false},"1.0.0-M.3":{"name":"@atomist/sdm","version":"1.0.0-M.3","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5362c5c2d019014c088945e84eeac473cabdc97c","_id":"@atomist/sdm@1.0.0-M.3","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cBh2KiKqVEt90dGtP/v9bTJ1tq20gbPWNkHvsMzeINKuvCJjWat7vhtxLAKN/B7bTB/GnLjBU9Jn1biwfUvpEQ==","shasum":"955fc293aca0a7a76958e2070903d9a589842de9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-M.3.tgz","fileCount":911,"unpackedSize":1896527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjqN/CRA9TVsSAnZWagAA0ZAQAI88+3a2DKi9RblG/shm\nalhDuC0YDcu9d5aIOHWN1nvVl3WE88miNve72AqLHgDqbRaF1Isxa/SrH4XJ\n/fe8vRTjwsxSStWpTN0TJ6NvU3thH37bernRfynU4zZPaMUV0m5jjHw4x0+b\nC0r/W6p9rezj32jJY8alTg+IwDJWTPC5XCT3jNrNAAK1PG9KJe9G6zdcxXiP\nQs+buzpT9XYM4NaFJtJZE5JsdP88XlAAAk5qjDnUUeHuESAlOaOpHkIz8vHG\nuWejHxmBPjY1blZQ/L5kT926dNfkGs7k5T4SJvQOgRfsONNehjqekvN8t+DV\nhaPPYTiju9K4aSfrVyBoNs8NJz4LGjeRNW2IwHQQdZNHG4HPoO/K5+qzGJYO\nodu67nQa74qJxjZjW25VCbS7ilJdA3hLb/Ogs7a+fnxK9Og4mqEtaFTKMP7a\nkwCl7aO7w3fDqtH1EmQUfM/v+YvO1xJhBMB2NEjykdu1Z64hnvytyJhrBrgC\n+BuOYoB0ggLLnFhoCOCqfdbfuYVdYkwB4GGC5VP0FnsZhRWWFNWCZonncMVW\n8K1ADtSpkaGaWf/0LR74NeYNP/mkBrumVUrBj5QJN3fvaUiIJzlGK1rudfut\neUgMKlYqZ7XdJ5PjE93s7OcynbBCi/Spg6KkjCDvZoZ4q/JaRYhsg0/4fpo6\nPQVc\r\n=HD03\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA1U8XYxw+ZPUul3LKyRM/335AzWFbMWJgAiiWhRLyCSAiEA2XUPTL5qunzRv4hEyQyFAjpcyVLJFWqeuCIQWU0IEbE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-M.3_1536074622255_0.6886636230656167"},"_hasShrinkwrap":false},"1.0.0-master.20180904201909":{"name":"@atomist/sdm","version":"1.0.0-master.20180904201909","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a96b1a10092471ccdf645be6e30dcf7e293b8a3a","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904201909","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tKXd4RjgkoZQn9Jq3giR2Q9Wqfw/4hG6Ga0H0cs6AJWO9hGcFWGOjM85badoE3c5J07rsRaJYgHEWuo8aW+Kfg==","shasum":"63878518f2b459be903aecf61d784cbac59d2136","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904201909.tgz","fileCount":911,"unpackedSize":1896841,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjulyCRA9TVsSAnZWagAAwWEP/j8y7UIYlqbV4goB0Gi+\njY6jmKKKWE4RBAZ/2SJbI4tetxJFskZLTKGFqy+JWEkc25HJZS0HQVkoqqkL\nLLb5Z1uPpuDqE1xR52QzEc8FK2od33dvqsruZF7zXYFhNgqyohY9BiNGrINn\n3QsiJ+gTW/HaxQyC5zbI62YxRXKbyARYZzU0HDjXl6EiGH4GdgsOMMaKmocm\nA27QZ3fHRpr066YwXFoJ+LvkKDO/yA86QWBUsrWXwKDdtDN7Rw7QGgy2EExY\nFogonqyg4uF9INA0TIirGlyJgevSk9j2sJBgELXaHrMu3ibXU6z2UR5MxIqN\nduBgUqlmwc6R779C5H3S9feZLQskZLEYmp/5gFx2PMpKhwuowC/2Emu7mfNy\nMfbjllvuuXN2X8Po3hx3zeCNqrKxC/8KASyExBN64OtiUPkVKjvWv0eIx1Dx\n+jPw/JwtIcNJ971iQSupqNQhSZKo97IDbvXMdG/6Lns0i+zLy3G3vPTRh9gb\nUup910ppzmenxN3D3qJmsRW8TfKPkKfpq/WB7gHsSDIAtay0X3MF7QZttcCN\nszcJRIhngTKn88+Di9jbLCr+4dqSVXZyzB8JDRYq5uUSZLnbw0lU9DJWuUEI\n1VDkUCfPCeUZHZFDMrz7VFAYmPmuZ37fh+Rnf4Vt93L2VqUgyMveJqes0FEM\nQrDr\r\n=8PQn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFltXQ4ZqdoijU2F3rusdTwO5cN/Hpw356rHUrAs3m/aAiBm82mN+N0TExECucMBB10xEkwi/9odovD6vHXaUrtzMA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904201909_1536092529203_0.4814802733181216"},"_hasShrinkwrap":false},"1.0.0-master.20180904211628":{"name":"@atomist/sdm","version":"1.0.0-master.20180904211628","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f95a157ff2d20eb02e6af21b125a2e25dcf4835c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180904211628","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZmI6grEeIFtmh3AoIRAYEgbUl7wyvW4we6J1YBmZpTBuBadjsSAd1TXjyI/eO+eBVHvxHj9MHQnI85w0PPJzYw==","shasum":"02e41ebbd7731713b24e68e8b8940950032bbbd7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180904211628.tgz","fileCount":911,"unpackedSize":1896895,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjvkSCRA9TVsSAnZWagAA24wP/ikNZPXA0gvyJh8saXlx\n18y1tA9YGez/xz6KzSSKdqufx2R3xfvu2QzEEZ2d/WkNokkMfRMLBQVEMAiL\nxMiD12KyWaC9pd3M2ESnVMJyk96W547s7JNVlGNCs0WmBg1c3Forxp+2czNB\nZkRvaeuoVTfga12s0Ft94hlDLtY6tpXybq3udFwDq/JPfnQhBEp/35357anc\nJkR571FWiGvHUM6yrsaJ3zn6clPEhtt0Jg8Q1/mfKx9BKM8gNIUxO9MJfDl+\nFNVafEhajUWxMW15jCoPznbie7u8g7/TTojyZc5buxohIHipIMnDmVKfdoz0\nMtI15twm0LLbFF1HylKwOyiHSqoG/43aQEgbVCPH3vWuGkHaXJxUllso4K88\nlDbGsRY24tWdfUQQ354C2xmIO8QlqA8z1cmdJPmol0QGlmMRcWXIJHKimxJw\n4YhsHA6QytPEIChqPj/7deH8r1TsbcSKQsVmYK5iDnWoJ90M4GBlfaywseaC\ngLE2nhIskjpjq0Ande7eNo3Fb93tK6oYxVW5Cly7RwUL1HZnZTNkUfL1SCfq\n8dV3iXJvQcYAAArCDXKQHZvpwzWDEjU77p24pcNq2JZUKlpLd5SFVwG+gBZ0\nLU/QCG8yzQJfTQcla11xBvH7WCYL8ShTu5GKkO7VK6igc4HHEDhGUo7cQMwo\nTw9J\r\n=iC/b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFPqtJnFMGse55AotWAH/he6EZkHjMegK5bmIPnMxZdrAiEA5pszVeqbPa/v8eKgR+9Ma5LwbQ1BFE7VMZwuAHVt5zQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180904211628_1536096529727_0.045111816552412254"},"_hasShrinkwrap":false},"1.0.0-master.20180905004040":{"name":"@atomist/sdm","version":"1.0.0-master.20180905004040","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"23f90cb934466a9d8dd41b0fe06b359d078c5bf8","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180905004040","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w+nU+5D8U+uL/g1qzgt1Bxz+8BUnTp0YjWABrx/y1IWRemAtTwIpmwEsxtN77rVGVOAGP2ayJbhx58CGUzEFAg==","shasum":"7294170860c084d1c00e7300f550511ffbcb399f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180905004040.tgz","fileCount":911,"unpackedSize":1896870,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbjya9CRA9TVsSAnZWagAA2c8P+wZWpcGMEjQOUHiWWyrT\nk8TdthgZ1moC/6b/5ENRmFH4nhU43LslhbnDrAgqeuzIOlZxdBwMo/BIXhw3\ngL0gMiJSNyxNDmRWM4CiscucCQVk9ONZ9DMmBN6M67nsXLKrWRam3H1qEsQ5\nG+RZNHjaxfzTVe1sxh4FkaZgEZBFEx508rp1NA5yEUwcGislTqIIQdpqw2BO\njZckhEl2hU6d02v6l/DHjo9coT5bSjHF9DFaiAI3nC9jGzvG8G7+NY0MtWWl\neABSYsyR5GQyr0KGghR327DPp8OQv7oRxpoJFzNWXQXv02sD8mXv8GBd7XZK\nPaoZr/6s82uwk6npcsnEfO+by6GKg3lUMe5hzMuHC6wC56d1a+wbs4ubsnBp\nhSEPtmNqa3cAtSv57iuGhl/qv82DLSP9+Ycq4E2LpJP403kbBOa9OPJ03L9u\n6/Y1eAo9RQCLGRS1GMcenr7re4841AIFU7poZzKw+f20bOoxVLpSQdBrpGER\n8Js24HDws0KJUIKj2JOQF9LW/UKWSdezkuP0pqcwKfHEshN/vC1p2OTa4wqz\nffaOD6B2cGOsxUGjdaoSQlSQHZR2QphRNQSwdtcWFo2/dp/sOSNgekJdycp6\njVaBms05v9FyNcP4ONHnA85pb5aOaEBmnp9/ZrV12jQQ3rNmgUVJEvPjtSAW\nK65q\r\n=q0wx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAzvRmLsAYn1nEV4LuqAiW5qeJeijpj5Lc6ct3PPq9XWAiEAs9tEQwFt8OXuhDXwy1vNiIRsjUQ0LTKFp1d53D/uk9E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180905004040_1536108220157_0.9811207233221129"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180905084253":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180905084253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905082419","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cc951444772214469f68e2cc986560b9dbe36630","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-depth.20180905084253","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AclE7u8BuNiXj0m3gEqBARhK8+TMbwRGVNf9IbciFFENSjNHyyDRGYfhZgUQhNfJl7L8XYoIT4falJtByjFr4w==","shasum":"faf12a7f6abb92f35cada1892b8d6907b7345d22","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180905084253.tgz","fileCount":911,"unpackedSize":1894771,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbj5eLCRA9TVsSAnZWagAAicoQAJbNQ4eC3L6vo1k0AGgF\nSJoApeMlL6PBfpi4y0BQxo2VQVlUqhhj9MCpPwQRC38IoaaO0MUCypzvbZQh\nLQfuKOOnd77EovOjpQ6pm86qlaYnKI7b8gksuYzKMv25pLrkDDuubfKoV1Dk\n4fE36LnP/fhUfQE+med6IBPTlo+RBi73o7MhllgjataIjLZFnkuzHkPBtP1b\nDsXHlWRzcKlF5pmOVorQXekVGHQo8UYcZnb+dLZ7Thxd4Y781YQV22Ntlg2s\nwkmR0GHBI96ZJWmVHsyNYjhaFG45g/Ug8/3n/36DSR/aogvwELilcY8Wt9Bm\n+IlBv8QXgI3EoDy+cYp9KFq4E1iojCaJj9pt9w587Ah6JQEqw0SbHLmg4fS/\nwdGTF0efZebZ7ORaNFRXgPH/K/Nk7gi3WIry1T55dSg5uhON7o4lAfAtByve\nPjV6uXUKasbRr95FHr0G2hyDhw4uT/MXHHy90hdBkhzRRJstiWIR7JoFFfZE\nnsbdhPn6+KQ2b6iTyc/pg6WH3ZmibYNCWEdhXEXh0DQ6dtPaDAdZxdkRfrBZ\nqBi94YxFFURaL07aLR/FhO2AylnjKpjw3XIJR/uthkwv+TtSVXfIlm0eu4Ml\nxgMEpJ6u6wMRrN7Q9RUtZ7v1uBRE16jRAZjQe67fMcEoVaE94Oq0IbsEt9E/\n7Ohk\r\n=7w1U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC4MvFXDd1XrmxsZAbQoioT8/d4MQhWLWmEjn8se5XulAiB68GS9eq3sMafC4Yib0KEzdEX6P6Fmt41W8rppQrv6ww=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180905084253_1536137098441_0.7663684701711535"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180905205613":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180905205613","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"1f93f89ac1ee5896d701679d5aa3224ff9f43831","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-depth.20180905205613","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mpV/B4bWO8QIHAT68FLKLFGNu8WTuAb/7d5smY5JNKUMzNWCJ9qAwwfg12kWbf5A8jkNGHZE6qjX1DG38A8n8Q==","shasum":"bc936ecaaed0e058afd3d743fab10a6c1b11ea4b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180905205613.tgz","fileCount":911,"unpackedSize":1894646,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkEN4CRA9TVsSAnZWagAAvnwP/0OZqs2l4hHvLMNB4Npl\nVsB3hZ/2iUQocQyqZTMogMKo0FFI0KRRGFiHG0gn3HgOgqxGWqa2ILQ/w3+m\n/zntuiQsj76fiBOTYUz2JhVa9sKfntdOnYe2KBOEu2vg4nGb09s9qMVNiTnc\nCvT3ExXFqrBYWDPhWMPyIAREjqcBRDIqpk+on4Nkn5zFjgd/7BPGju9OlRor\neneT4kDqwm5DdZAN+7/SGBXlvzb5uTQ4JY2S8lxyoc0o9ehzIXfRZPByyQlk\nqxEEr9A7KaJ7c+rTEOR2BgizZ7kjKpQ1sP688gI94fM1mCZ1eQ2orlfuFxd3\nVKVcVQAnXyqtHknGUbWJPETPoeh6cxIupCyMB4kKuhhWTFbBR0ga4rgTtrQp\ngq1wqbJqXEK0CmIVclMD/7WAnKSyAWCG9lVVlX+yEbyLxRfkxrorfFTCH29x\npzlfeom8Br52pjhBQK0WQUMlY71I9kmGkim49Db4TxveXY0jWpLd+hgBt56A\n0ZasDOCI4ze01X1lnlMDLO5FydlwsirjC5vrRD//qq/IupnJful80QzViS/S\nGmKI5BnLPvspyfUFoDmMRcckGoOUp7ZllzvPi/VM2cQflo1VTHODy5SuUGh8\neFlMk47nBDkhbkyIq/YFRXw6b63ym2UPp17uA3QQKq3rdWtmmcqINXI0X88c\nfixl\r\n=6HOt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHZuyeQVqldt+M+pFpep13ZVy/vDLOKmkVPBL0gcMVjgIhAOxIWA4/aQNJpMxGNUxSZHmb+v7lMKPf2OaBoNL+G1VV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180905205613_1536181111334_0.6290316820294888"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180906155733":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180906155733","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"cf72734c5da7c080efeefdd463f92a3d01e54a51","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-depth.20180906155733","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hJV69zjLrrrvEYEdYjmbodUvGg/qqaVJtoEko5fWET+tjNGy5tUwNM5fQT6hD4HI+cgY1NTgDdQhpfOlywFp2g==","shasum":"7c651cebd8bfa69b30b0a35d5f43a2e8a55b4f4f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180906155733.tgz","fileCount":911,"unpackedSize":1894639,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkU8VCRA9TVsSAnZWagAAcR4P/jrq56zUP4OQo0LOFbg2\nnudIhRBLFwYeVOP3va6YqodhdOarMT/Qd/+TRPFGRRQZrZa08jElEw8AoDF+\nS0iIhNdPpwbkfcmoz7EKSX2Z0K9JCMXyPamIkt9aivSq3cwlgkSOMMQu+58o\nyAOaray3kacU+3haDoLUF8s8cqIrwXV3nR/W0H1xJlEW7ULqeGFzQNxzww7O\ntcv5XwjGXsL4OzYKCtVbRig5GUm9l+LAia1U3jyne3MDqZbL2iY5nnFNemkP\nvzJf0UeVcyWM39/2MWLfKcQvE89CG8kEL3Zt4h+b5b+ethWkATov+Pq2GILX\nLWRlLW5jrnKcyRcJqSeWNES1/4tgKazZTApDKpM0+kBViLhFTON2oj1H8zj8\n3uZE24X9kZj2XCRqpjftxxD6ZSP5nVJYm2BJ1uI145i2cmb6LKlPRxj4sKEK\n0rQuGeFP3OPvvFfqGz/eUSrczuwzdumDybOIK0M4qH/Q+KvfHODBTiP9MvfE\nC60ZC7DUB0IKLv/6LTOxg407UrNnBMte2NEs7IS1rhw8ERrYduiQdlK9xOVF\nrx7ZV7/4kZd+FnlsxJ10TXiQjLlQYmEBqvP0QUfgXrIKtNm3IX+cX9Bwqv1A\nzbFbI45Ip+t+XOY5v8arugC0qotIFer+pzCtD0BO/PKc493lpHuh1Re55w/s\nJy/1\r\n=lYml\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDpZ6WLFB0F/IFHxdDzpbqX57OIr5RNukLUfjuvPNSjbQIhALwYIJByKq3OdbmqPhJW5KtAjiK6x8S7jIP8aCJvkmvR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180906155733_1536249621069_0.2170890076444676"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180906165909":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180906165909","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"74a7e1da941b1f96c5951762286b07261fb62fbd","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-depth.20180906165909","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1ecKj6mnsvZmIC7S7OiZ3ZWQazQQg1L1W2jT0QHFIwcTjJzFM5rHXyY/x/got3ePud1Y3DvRyyGLlZC+4TFhpA==","shasum":"c5237597cb7835839a8d1e856281fbcfd3c2ee7a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180906165909.tgz","fileCount":911,"unpackedSize":1894933,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkV12CRA9TVsSAnZWagAApEYP+waMhLIq/mhUSs6aB6yw\n3AVz7dCLu+ISwfflwE81Jrha7rWVbsggVsNM+p/X9k9QSgBDDBBh2brTK023\nY6Lrfk6OXyGM+n/YYVYffi0M6jJ5kLXG+t3Kfjw5KcdXueGAcZGc0LAoaffe\nB0D4NqnYYrw40GuIfn/fDjQ1bo84FzlwpOfu18H7CWwTJx16OaohNaUdywYq\nA0qVNosXmF51jgEMXDyCTMcE+qSEmZZDuv9PinJ2x3na3oYNxdC4zUOT70Dw\n1iqWR6BHcbqF5OC+evR0L0/lHrI0mFQoYO0/wXV4zB50nCd87JZscQOu9uzN\nVgakG19Saeib0mEWsrj1EIqKpZCoVf1d7JRwXr3L5moNgd5VAiUOKKQuVG3y\nw92eR9AvBSPqkE7O2KO0O60OPGyzMgFFp1OYS7fZIzWXt+UNqVBAU5N31ENU\nia34zrYB8jgWYRF1d0l37u1p7Zz//qQVLLwvsZGz9rvBTBGXFdSr2tHO4F8A\nYUJRbLfT4xXrcMnmShvELEwXLCebwXlFQGkH82SraOGYi+Vd9Bp1cUhEX5+t\nmTYXPrrb01IIrCEYEzVgS6uIR3E0EW7wJzi2Hers3A1TsXOFLnqU7aagQNrt\nZZzPljxRVl0DthqMHohl/HPJhRNJF1Xfzs5fiVntC1YEXJFnO41Zvq01fUe8\nyxBa\r\n=HDXv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFl8YbpIDZqG2HSxVDqc6m9U4PMNPhGC88FFiVCu5GDPAiEAtTkiQ4Q5N+FTJqxDgJmesSzo+6EIVKKaZ04sUjU/NKI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180906165909_1536253301723_0.9695451428608732"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180906204847":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180906204847","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"fe9d604aaa2f7c790a5d9fbf89e7a010e9e4c914","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-depth.20180906204847","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-93wOtzT0d/shbqpEdR87YC8iyxGRi7j9gcUdOkgaH1GN2uPpGdIs1bQsLj6gqtbBt1zBwsjdrIpEp6EEW9Sk8A==","shasum":"b6ca0bd1db2cde5f7874821b0b67eda77d78da97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180906204847.tgz","fileCount":911,"unpackedSize":1895436,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkZNVCRA9TVsSAnZWagAAhUIP/Rf/tWbC96UDqRWO6t+K\nUzy58tZu23voJw45uBa/bkTetksdwfU7cTe3fr1KkYuu01xUFW6AQD87axab\nc6ySWWTxBs80DKrBt+0tqhAlE4NJxjX3P1m1e3L+gKqY6ubVai4SnXPNPWuI\nZEF++Bj+jtlgme1y4Pw2Xr+ayPmB1CvUMC4nvEZxRvKG+eY/zL21EwtYdzd/\nA6aC25NTBDW2g4eCx50iUnPC7i3ODZmM8ODIZXiwcHEAf1aSq7JQGg1nNXVN\nZhKaIgBHPy9diZf8iWHPWjD5t+B3n5j5pGZClhb9qcGCbe21DW7iemfuoQiv\n22L02T00stFPuETjk06d2Lt1KU//8ErLcrbdVuxDPWJUunfDaGElIwElCvy2\n3BIBh9H1SuTzXfXUegjh+fP16xDHz/yV2W0Ftd7ojnNsTMj3C6TR2xt59agC\noNMy76Z7G0wPhfvHwwYbvmXNwL+uO3+0nduk4KK2BBF24y63MFQl5EAdQi/7\npEmWZ6pnJsCiA5dUEojgx9B0CWnE4YP+b5zKPl2NaWMAIeZeI6J9dctcgWAg\nWYHR2eFJRTY2EcvePuU1Yfuwf7vpDPssq+Xqefj0MGQEgqDndf/xen0ekUU6\naXBptZ4feKdLajoz+ybauIlxU5TnEMhJ9jjcpzmfnEYyDVMtporfm2EV2ID2\nvO9h\r\n=ffoG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHIAVZzFoahm2Edb1AK/dJMxiZ9F6KWvksCHZxiwuJXfAiEA+xl2t8ZGxO2eeQhmx9qqZSrB0c6NaZcYkQwqkiIIKN0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180906204847_1536267092143_0.8066108750756562"},"_hasShrinkwrap":false},"1.0.0-clone-depth.20180906212149":{"name":"@atomist/sdm","version":"1.0.0-clone-depth.20180906212149","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1048e58d28f3b3039a7af5723eb119805bb1f89e","_id":"@atomist/sdm@1.0.0-clone-depth.20180906212149","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QnBUwhVK7UVt9sCNWeIn7h9mQM8SNFG4FRYOwFLilMRREao9+RoymsJz32/vmJl4wh8QwzJU20m1WqomLRjFOA==","shasum":"d94ac16569d18029b135da00fdca0bdcc3a131fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-depth.20180906212149.tgz","fileCount":911,"unpackedSize":1895995,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkZsICRA9TVsSAnZWagAAMVgP/01fVUb6YeJMg/4YVdk2\nZK0VxEqFSfZVEG635+8ewzvDV6jeKVDTVptrm2pc54xRz3BgY4EcoagJkbq7\n1m6+vxjwvbxlcUUxg7YeBWqGACzs/0JFOQrGuY8OniWwQhJbN9jSQjTk99ua\n8uKUXDqqmX/8bFiEcCumovc2bxQgaLHwn62kl/DbSPrZxB5YkMOPBjkYLquq\n3NEJnnd2S3riQ3ginbsSyMs/WVzLarfCsaadEoPT77R2A2AhRYoci4g22d42\nyof1dpVU7oEacglmXB4wNazVvbTe2Mz1qEDNDE14ln23ynyrbpnKXFByqTvB\nBL5LNXOcACOiAA+Reu9t8Oyt6gdZ7ryENZOgnah3zYhJfmXF3G0lqkuOO258\nlvjJG4Wrps8jzor19+HvTnSTkGnS6JiPIUHd3e0pMhOwIyJNwhuEPHZsdTmv\nrmZW21V2aEj15EFdEGXfbxdtA4Zq+IFfxpT8eyvHgo8nz7q3eXfc/qm2go2x\ngVO2DM/B4jeFgu8hC4/JuCRS8Bq4lD62S6gHGKJxjWIOGjzOiiuW/yQJbggn\nQbboPG0HqTrI25VbrOXjT3Z5bSK9/4m8Rtj9hjEGhGmXrl9M7VXCPLyJVSGr\nlNBF5p5LpjIRYSUjIslrEjKZEAJKgKiJSvRJ5gx+qLKs5bdURNWNfQ0sHXLw\nDkHO\r\n=DvBb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD9NThjq4eGx+PjlX5/cc3ksqiL/RU7V5lwdhsCfzNFQgIhAIRjp79nyWleqJW0C1gv83rVLCzfPWxbq0x/4HmxpUsv"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-depth.20180906212149_1536269063651_0.8908986670651025"},"_hasShrinkwrap":false},"1.0.0-master.20180906213037":{"name":"@atomist/sdm","version":"1.0.0-master.20180906213037","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f92d6f433ad02f27dcf286054b87513f685ef9e","_id":"@atomist/sdm@1.0.0-master.20180906213037","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vi4B1lHuGc3SwgHglwvA+Dj8qNpfJQEhCc4yH6oCdA2mpLrsX38Sxxt+2BBMSApVwTA0CgcCLrKQmQv1L2Fcgw==","shasum":"bf34c8f66b8457e0068fb774387e0bb80f9a5d7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180906213037.tgz","fileCount":911,"unpackedSize":1897127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkZ01CRA9TVsSAnZWagAADFEP/3bug8Cmsr69k9BqXF9R\nWjJwebk0N5tvxfKP3svPoVl/eBPCRjg49zihcn7nx3GrJxr1+6mvwRV40F1F\nPhxH5H1ky2iVb8+ipFfX51UNKQpeowWtfGo2EFiOA/zKVoT/4Ocsh1NK1E32\nNMzqKeJ8G16KDYsxlxgIAN5IfvcBTx+IKCYCZc7/JquIVNC9MPrbwNhc+Vzn\nlvjTwGMcAz3paOYyZSBHU4u99Yh+Qqii0DKYDsLJ/ReYTRJ+F1qKiftfdqkM\nEuILbfexTc503YmUUdsGbwFw1g0ceqZY4ngoj8mWeO1iDZMOQ/WS0EsuwuV7\nRpgcPXdw/uhBPtHaA5X0o4U8DapHQAdlR6Pal22MaUrm/p20R76ulEkqGFzj\n53tYi6fGtmHWqDXcNaB7fg97kdbWVf8E33WUaRHQ2gdcc+Ss6r5GGUWj6U3t\naVY8OW+xgX2mlJXpmMMob3ymMq8HH0RALOAjKEu4fRdX0jNH9dnhrWuNBGwO\nJW7ubFJZooNZ/ahKpB03XNdWHHe2Aw466qfysfWgZoSBQ+49x1W/9GevmIBi\ntCBEJnm1GYoF7vWb1HqWHl6nXQDDotj0Swwss2U+MRPvodYqiZmsQzeWomi0\nGdyP8RVL1flAXewtmENWplN3SdW7lF9SIhpc3fcBe4tvr3a/vK3NQepG6m9f\nfPdl\r\n=sEag\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA2Jgw9rCi9sLLKlh/N8xxWd3Nrzzzz963vPMqafppNbAiATMeS6Nh6Oybcnn3vRnNPX4FFFZCuUPruFuJy5MRFGOA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180906213037_1536269620813_0.49442790011806026"},"_hasShrinkwrap":false},"1.0.0-nortissej.default-transform-result.20180906223648":{"name":"@atomist/sdm","version":"1.0.0-nortissej.default-transform-result.20180906223648","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5b88329bed7a384b288774be23fcbe6faa96d9ba","_id":"@atomist/sdm@1.0.0-nortissej.default-transform-result.20180906223648","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OsihtI7NvkiP0D6q3hcVvKMW/vn/iNH+CN9/LR9wOCYD9BYLGaWbnumMVCsB9Axari13tO3eoQRRsCyhij82rA==","shasum":"1f39952ed34b18eca93350e1eb3eaa88b528c1f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.default-transform-result.20180906223648.tgz","fileCount":911,"unpackedSize":1899684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkayHCRA9TVsSAnZWagAAgmQQAIMfeASgj0YAwWI4X8TB\noHy6LX8Dx/4zhAYTcF0pUmMdhGZWzrVhBYoYWv0vlKh3q714UR6ypNw+fYGr\n9AAlmlUd/vXgSh1VHSw7eb+CQEblXB6PQcBer2jRHGfs0LBzm9KI5AsnwKq8\nu0oMREdwWetfvMLjzKj7lFaZwbaKyLM+TQUBGrVjedktAAm6SOUGr+VppJHB\nXhZQmdJ08l6Sp1dTs1zlBmpzOnoyty0jHbR6SgoSx0K0+8NgTVWCvqyiXyln\nWa5TKkzcVsdT+mLkv+he+5yky72+ef+GsLKzJ7WncM+36uxkiNMDPhHxM9ns\nRyjg4S8FZjDfU10RMnnzKEszvayIROW6udwGOuzK8yK+kLq/u4Tu/8DqOnCW\n07zgqrwdEfM3/K6wH5Ll7hQdstKFy0j6zBYr1HuJB10bQG47gEnvsi6aOjZ8\npdoQDVIdJB2dy/8+iFi56bL51Dga8g0t/ID7+NBi9AcxSSA/OkqbpyAYt8h3\nWK0NgZnlVPHie0/dZCgSP1PxyaOS1TBcCnlbM1mh8oXgyLzXUhImgm6K89Ju\nngii+SB8S4r9xsK0yQbuvtEwKKOmUbVICo0MFE5c/fCdH8U5K+80NGtD4gm4\nbR/caa6C6H+NgujgsWTspTlC03t38zGsgRAAh0xQQpbe1FfFMclU5gN8TV8a\ncRpf\r\n=A9+F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZ+aQnDOTOs1jF3b96/76wlCr3+YOJ/LoPXh9KDRGMaQIhANXFwyhF1c8CIiZIzBm48GqMPCvrIk/OZeIpsTzBrdBW"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.default-transform-result.20180906223648_1536273542549_0.06076399413455569"},"_hasShrinkwrap":false},"1.0.0-master.20180907090021":{"name":"@atomist/sdm","version":"1.0.0-master.20180907090021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"e5b3371b8e411d0c1d1025efc2cb7282ea1c12ae","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180907090021","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S58DcLLsqpYT4DMlu9IzZ3uG+wiqPGkC90gxcD1UcX89ARssFj93qOFvCf2ht9XboQ7EZ2nV2EPPDK9aCsYX1A==","shasum":"a1def6fe798b8d4ee25aa63ee6bd656d8f0c7b85","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180907090021.tgz","fileCount":911,"unpackedSize":1896584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkj7DCRA9TVsSAnZWagAAaaIP/01uTI1Mj1MFyZD0nppe\nMk15ckGFLzQhxxGGPGi15VVD4GpYopnNbnhCD297qLMjh+y7FLWZx0gHeWiL\nI2G+rXX+77BoUXFYLouVFqTgmgAo8XTKXUejc74DMHMIYXzrWbHJBPBhHLWb\n9QXwHawpkPvwy4G05gGKHJLHFH4Stw49l+6HWdtGmuiWoaGGB/yDcxXA/F1V\nuc5d/LoYBktFowsmpJi9jfcZIA+V55BVpti/ZCaGmGjd7RELLWnY7+hcz0DC\nYKeoSg9aBzmfQfEaveBh/bzwEsaN0y8D6AwpTNN9pQRHOCnj2kFqDwup9iT2\nUfEImT7l2yLOi9dhVoDS6m5A/Lg3ISkaYoVnjDcZ3VzCliyMtkXs+mwnFoPi\nlocpglMizMF5JFlDpj+ywvoe5zvNPw0iugRJbqWu/ioB7E9gsjhGrDmhokKd\nrZwG/Dbl5wthgCh22SdyLR15D/J1n7DYlWNzCLprwRj4RKgt9DfGjV4XXbFN\n9KFJQchoFvB2LuNxtiqvDLBTl/BoHmfJMJoxJX/0+nEy0UssYjqU2a/LA1s/\nVJUZZf1ApjCHJlAuDk+UVFnTsfY6risxkaG/jWrdv5k/0P0VR+ddm6DY9wzt\nth4O4/4NjDnft4nXBooYbzp2541k/Liuh9Z9E/mtBGbbYteTmyU4rcv9cpLW\n6Cz+\r\n=qVRy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDy5BtR47aWs+ukocalUUM/bYsLs+9wIGOGdZrB4T4E7wIhAOJpkXsVM4BG+KckBkXdttV6H+OnAY0onkRPtF/sH174"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180907090021_1536310978876_0.5042025811509745"},"_hasShrinkwrap":false},"1.0.0-master.20180907090623":{"name":"@atomist/sdm","version":"1.0.0-master.20180907090623","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"daa14affcd2c2355b3e33adfdd985ab1c27b1ee7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180907090623","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LJK6nJYARX9BrB+LAgRMoH19MrzGRSANd944EPMqfEWV//epN6QVfSyK2PVs8Hmgqybj2fLn1NTcuKRZUOLJdA==","shasum":"8747fbe6f31ec5eba893d45de2822b383b42a3c5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180907090623.tgz","fileCount":911,"unpackedSize":1897230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkkA+CRA9TVsSAnZWagAAWQEQAIRoDq5kS+tFR7AuwKl3\njCe8j39lGzKIbiX6wF5HjiFSfIPx8xOdqwGc4eb8VYHbw/qXMshsCRWqTHc1\nfAg8mBAL24M5Y78Xt5GOHNBxRP1Kp1AFiMuRdFABVJVe7BrVYtqUSuJguGOt\n5WNaqOO+tBId0IxquEH23E9q3PPl8c5Mi+lzgdaS/sC6OroV8LOFFmHxTtWn\npgeLGZrWBMeyB4sECm0k7ALT3W5xKwKJCGUuN2mvKnTm1YJdt9eEK9pJqGWX\nN1KYEYgQoiJNHKftqZPka6bQLirZPaTluNfqcZ0+7ZHQnaXg+DHye4vj+v+W\nAUXT0DgSagpjnjW7CupLjAyzSXTtk3rJ4avXyQAvQp4kn0zsy8dzf4zUg3fD\nHjwoxkEZAtJWIGqsAQopW37i7ewz6V0CrfHEeclGpPMIqA25bNm7BHoYlBU5\ndIVgY3Xc1iNX7180Fg8k6lcyY21ams4NGAjb3AN7TaMpgRIHWHWom7x+d+b4\nDyIX/6UlVfq9t/jZYoOnPO/HPQCs915X/6+BV63YhV8f6UZn+1YEIrREIbcv\ndMFdgL0NnHEWDfyJMoyu78v8JNTle1/SMooKRxXmq57oVUPY5HlG+UBOT721\nt4/vxBbF9wDA2n40EUNPgtKmcRPBf46bahWs3ZLzKtGpr1DHVxl3LSO//HQh\nViH+\r\n=Pai5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCDysF40pgTMxEg+JMOBhwUTDg4AvlUMnL1MDIKIZpfAIgLtUfDQJsB/DJp89k41VoTzRYE3/nQkLdCMSI/x4f0f0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180907090623_1536311357605_0.9079936239632398"},"_hasShrinkwrap":false},"1.0.0-nortissej.default-transform-result.20180907220813":{"name":"@atomist/sdm","version":"1.0.0-nortissej.default-transform-result.20180907220813","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.3","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"972f157207f45828b1739b90b0233962f3d032d4","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.default-transform-result.20180907220813","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R43sjnFuosw75qYkBHLe95IBsuVsPcI8PtT+uZ6II9JdXp/lqPqq+8sLiKxixFinITafwacmYpfCToTO9dBLMg==","shasum":"e8187137bd8ef8071ce440eb219e9c56be03c0ff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.default-transform-result.20180907220813.tgz","fileCount":911,"unpackedSize":1899615,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkvd2CRA9TVsSAnZWagAAL4UP/iwmiqnW6fjkbJ5l8AZj\n0tUAhhmUCL3dzCzq8ya64y8uvGXCdUrB55Dde4YfU6lU2ksXlguI+kaSj41m\nyitEki8yxJ99DVKsjUAd7yEJ0h9/p6vrNs9blcNVgzBRp5GKR772b3DkkveQ\nLmk5ZTbcV/aNKyVLLb6SYUaxqsH0KRufspuis2/J7X3nY0arQlGOBwYOcYwQ\n3zmpzbtv2FUOY4QUaGVBMrotoxcDnd+3xIv0ZII9lA0Z98hAN2lv38mfsopC\nzyK9XXtYtuPwVZQvAHLK2IuCIbgncYq1olg/DgiumJGcTfLOhv8A77S7j9h+\n8WCc/s6+A4e6zSqfIU53ovFQ3v3pveDJyoNG1p34XRL4Vixr2K/rRfAo9Uqz\nqo/jiaA99ItfTyLZGvkk9y8YXjlYT3GNw4bhdsf69/CXxkbfEDsRBWksG866\nGpegRFFCbcgt/+W/yGYcz2eq/HRqiGo6GE+F2gAkloTWamXxxYcjeRWZ/khv\n6zcmvNhHssrFkze3OYuwrNzY4srE/9dWnqHXLgrDkoRK9eird7PR1xMusEW3\n8m5XyTtaTJCUFTECnJZuD8BVivq4wlskMk9qoSOL+bA2Z4zQ39SO2Vj5lTn4\nAUrQjD1cpuvB4GILD1d69bOAvICKLXhYR+xuis/Znce/qTUamWJRivpdW/Vf\naPah\r\n=bYfg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCw+TZ3J+qRi2g8iQxigWqRDc+tcAq99s0uIGbdGJtn4QIhAJ06jfWyIXE8BfaSf6HBT/o3Z43GK8dMMt0fbcCaY7mq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.default-transform-result.20180907220813_1536358261113_0.3979627944928794"},"_hasShrinkwrap":false},"1.0.0-nortissej.push-impact-listener.20180907221954":{"name":"@atomist/sdm","version":"1.0.0-nortissej.push-impact-listener.20180907221954","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"0c28f70f2eaaac9e2de8e6b4a5912dcea7e7480e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.push-impact-listener.20180907221954","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1MZszoE3yL3MHz1K4ooBLVSlPpgg/gKC/xVl9CqPdPuBEnxQ+KWo5OuMrfTtPNrzb3dQ+CsD9aPH5gPb9zFLpA==","shasum":"d8cf219bd7ed8cd8b0fc58fa1de6b9a838e116f7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.push-impact-listener.20180907221954.tgz","fileCount":911,"unpackedSize":1898514,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkvpECRA9TVsSAnZWagAAtZUP/3h3z9PblI92+ANlb4SS\nNQP9OQWgfBO1PMqU1Jal4QAU/G3+TYETl0txU83uyuFOJ2l76gfeKzK+5RYl\ngAWMVdJoU4AOWW59h9SY/U8c6ID8xlnOxVv8HuCWit+448qggNMkcxMBMg26\n9gYZSeGRzVKNOn91gKkQd1FhRTQql1LVNxqaVpM0mU37ZteIGdYRGeABO7ef\npZVixXKW6cbd3u3nHJhwp2cyIm3hNlHIUXMXYK3iZn2n2eZZomf/5PrvQ8gk\nK0eaOTzoN3xH7tjAHSsRItpVl8P1+S3LTDf3gIYud7R+UtdZuAf0pSBlLDgC\nF0GlN0Pz7p1bP9TNN4vYbxdpawPcLcMFtm1sH/esdJzBMCdMf+g/b9+H7VeO\nHARCOk/uhQMvd9sC0jEIK6s8idcdeIqz9WjldUWbhgbcGLLnuogEpHT8jVa7\nG7UuKkHZF3NF23B0E2Iuq9vU2lmFXXObkhL5K4AcyonWs7TkXocePeBqjn34\ny6GS04niVswsZ3FdqpqAQ85swa7LPRSlVXzGPv1rkDq0DRyKqo+3JdnBtaMJ\nJaFDIGZFMrrh74YN5tui8jrSGbqc36+uFNu6X6/NH7NWGCvLtaBeWGsT4EV0\nnUp0Jd8QFZwzUr5t4g0mP9jloz6C1+THSe4mkeAtsXenw7UVTByFZxUVJJ91\nDz/t\r\n=7gk3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHiJJ32p0fj5jbF4a02hvErZ3UGtlDYQwDo/2ehqef8SAiBPF9kfS+PY/6MoZfqhVhVGZzPIcy/MNBjSLWwCJn0oTA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.push-impact-listener.20180907221954_1536358979821_0.7670759539006828"},"_hasShrinkwrap":false},"1.0.0-master.20180907223209":{"name":"@atomist/sdm","version":"1.0.0-master.20180907223209","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8cb2de9dcfc76680f9786dde358b37e13834a66f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180907223209","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EOlM8cr1H5l46PTdyr2JvJfArEs2hR1hGCgCvcpFVaoPmg46rZGecDN5v30SSSzeuXVAIx9Oi5cjQQAjEvLNCQ==","shasum":"a6ad545137f5e14d124421e31586ae85b6b08a58","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180907223209.tgz","fileCount":911,"unpackedSize":1898490,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkv0sCRA9TVsSAnZWagAAOfwP/11CwJThcRo9pvKear9J\nbhjRU0NgGGvggoXmZ/k2mSCouyImEICbOY2osaWbygqGyUXJTUXddmvWidj4\nze13h5uuWYDYLMcgFYfRG0SqjvPqnZNe0hQlDtoM7bOomRevW1TVlc2sK4PG\nISggrF2HFZbso9l3CZCMczTXSDhbkNn7BCxQV2FkO7LUWlPc2mcuQN4tp+va\ng40iOC3d1QD/Z+qYLsY+8L0nR3cAmDYF+5PUIXFNahdvqGVogMBFVRW0C2hx\nIoW4/OruV1XlBN2uuukJJmK4n/6qeHTT1oMpSdbWh9aiiygd6BJOaIAf213L\nS23h6PbcN3XG1l+pwj3s2rQfbXmdA4F6wahYCsoA+sMDeD0GnJKerdCZaD9L\nWToeA1Xz86ON5tBNB5Cyc97lEtMRsWRLvTwYEtf+OQvN9Et4gYY6qIwiG7tk\nyEtdkC8cHD+Pv/O2RbfCjELnxAeeNBtSEgHnK9IKmCoJzI4KYYgqzePX/khF\n72boMQAJ4DFJJvZqGD0/n8vd14HRUl2TCH0fv8Iyn/PAmgVfy5vuKH9fAMLo\n8G5iWkpepKx3hByJ8x+0W7fx5YuCOVx0sawHSuaYQMusJEMizBx+rv3PT4wf\nmwsjAECtvZ9/ATrqHsGb2vJWp4OEWKqHajYYSdrWwKvvQK1oVWQeHir1cAQe\nOg2F\r\n=Dv0x\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHyWaY3n7D+6VMlC46XQUNS0NQuhQiXLURC/YQBrgiKAIhANWdceYgGvrqtjgL/0lHF54+X6uL+APWlgYS4lSAH8hE"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180907223209_1536359723891_0.5696970320340689"},"_hasShrinkwrap":false},"1.0.0-nortissej.upclient.20180907224944":{"name":"@atomist/sdm","version":"1.0.0-nortissej.upclient.20180907224944","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"53feeb0da10689ab72a779276b1725b382a444db","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.upclient.20180907224944","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wfMrN0YZBM9DhHDDmNlhcHeQEN7Z7ShsigLn1b9EoCgJCYoVRdLdwlAihlFHnNUdpgVGq2QQkRvFD0fdJIva3A==","shasum":"4a83ec563ac87b6664425b719060ccf3a42daa65","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.upclient.20180907224944.tgz","fileCount":911,"unpackedSize":1898848,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkwE1CRA9TVsSAnZWagAA6YEP/2N5TG07vIhhz1nzX9C0\neMRZronqKO5hOGaXouoSStM34c+ljAUYJRneQptQ8KjPK/fEQ/8TJEnn85V3\nlrTWLe0uzW1XS3tWzCrBFMT/jEsgKJSTEt/MOsvhVlAxgXQsouARCV3gRjk9\nr4yfTwK3NEPNOV4X0jh79J3kxFn1XYuJIT1CQbN/vf18+T8wAVvqQJtWI+B+\nX1x70TjxYodVSFmPo5pmFn7ORzxpr4pZCQYqi1+xtg31fIk2VWNxae+B4Uku\nTUG62OXvmhmpmcg48EdPBoB3CLtc4t9OynXOMAPuQVwZTUyZw4B79stlIzIg\njbjS25eLUQzhWDxukPCZzSGnurhog24gX5gQI1Z9AKUIYocJKc3tGJRDZ6It\nRgqH9mDvNdoEJixk7K1msMTGWE8u5Lq/gDN2M20BeokNQcOxZh0S6E011i9X\nV2Shtueznq4PWN3aUMIZAIsAxGGPjtQmDn/suYyXz3bt/Ftil6wftC6kVVg2\nTgZ0GkkD4WAJpFeQBqw8Veo27DOyBEe7sT+vF206fxk7AIJI/vSWXoAuocaI\npMZw8djuxeb66O151beigEv2e5h/6cTKMjz3QfelZH9iDCJIX+nthQAMPuCT\nKHc661khivy8IMhzg8+OchY+CNW3XU7EHFui8DvquP7V88qHLBrjMNjXmKKk\n64ia\r\n=qOCh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGkP6Rm+sK2fEoZvcwPjM5wGoJ2iYVW4ckdsBKfaQdXQIhAIdnFrxkO7OCXR6LkKbceYNVNdXmFuj72rJkrdZASZ2d"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.upclient.20180907224944_1536360756741_0.46465944638973844"},"_hasShrinkwrap":false},"1.0.0-master.20180907234351":{"name":"@atomist/sdm","version":"1.0.0-master.20180907234351","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"5592d39f3a08c6c790f287cb20ce24c11a9cb045","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180907234351","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uFIvZPAXZsP79dDY8YSWzYhzVMye1GqVNaj1IV2h8twBfB7jc6fE8bH/QQlHReJA+GOf5SwXgSge9Y/hKlsvlQ==","shasum":"727d8b70a10509d84c6fe6ef5ad8189ee437a96a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180907234351.tgz","fileCount":911,"unpackedSize":1899012,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkw3/CRA9TVsSAnZWagAA+wkP/Rpc5G6/UpYlR+3dymOp\ny0vm3BpnF9lvXzKErEDct/jSFvAyeap46SPQsVxqAibHO+hvw5aLC8ZsNCVv\nje0jkHb5IRCo4EtanQNpu3TKwrg4BBsPbFUzihsyld0H8kU/ebPvE3qXAEed\n1MHSX8A3IhUUnX/OO61pIQGhAmVU5FHvLhsQkPZhs5mRB61eYl2IYovKLSaQ\nKucCdvZY6EwNCNpPNycDb8XkbNnGl7oIoYKEL27AGwOigT0wRKC4dhOYgmZ7\nZtIDnofGUQN0QEVMRZ/57euWicTzagGyA+kVbrYURyOGUIxltO1/r6fn8UCp\nPIac8vPBuSNhMFoJoMRDpUSpImlJNVas0GA1mHwpJONFZy6rJIOsR4ZtcPvc\n90mGE4lzfe1+oXNkaFnuHEEIdwRReQfGNl89xKRdOG81aI6bo9+ANgWUjup/\nyaobpj4lYOvmX12I+DOlC4p9GhFEnfxAKRY0nQUCQkKSJig0dnnZohAj+vpq\ndB6Hc+MuBhIMIzZPsluPgNIPYJIvoccv/KOLF5Ho8qxqI3A5zBeMwz+eo4cW\nZGQUXkidmpdQfBBfwQRtmsaJqlSNza8DS8nwxWeAxhkv0edfRI2BnfIKQf5C\npO6UV8QniNURAUrPBVU/ZCfHrL+x4bbw4v/dwW8zg71M9OcDshaPeGEWGZZK\ngXcL\r\n=/jFt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIANB4NykfKHrn2ycD5Pd5ZFUzykSXR88XQNsA8NKz4JNAiBosUzQlc5zFRbDwLARnRBlzniRUUQdWaJpAnxtYEMbew=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180907234351_1536364030113_0.4119557805719791"},"_hasShrinkwrap":false},"1.0.0-master.20180908002213":{"name":"@atomist/sdm","version":"1.0.0-master.20180908002213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7b060c95a394d077323af503459c8b65a04554ba","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180908002213","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JdLutbOf2ogpSD90JqujyQFooz+2KjoGEUHsiXP5Zb6Ckd+YRZ4uJLdi+e5ntIVwoEm4d7n2aiugAN9wQLBf0A==","shasum":"b841f6c9a1a47f82bbefbd7dc8cd33ca5bf8cdc0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180908002213.tgz","fileCount":911,"unpackedSize":1899358,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkxcGCRA9TVsSAnZWagAAwG0P/RGvG0g0S6yHXx13Nl4C\nmWo2DARMl//l0EPhOjoemdgdn/N+gDEySe3vIZkbObnFTVi6gNy747+/7yNh\nseEZAuTHhDeX7gLadN7mKji20GXt+1CwIKZ+2coQZbUbf1CnsZe4eU6ftFxD\n+7PPWfnimqTGkkgVeacjd19plLu88p9FZ9eTNRsBvlrgvRdda/HFe5Ic30FO\nNd+36sNHDIMKmkvUxJSeWL0IySEuIWySu+RD2TqXQqP4cY9yGMbioLlFJkID\nV5tCBu6UAfcUM47VWSpJrH3dpe/t9RjwkyOv4C8ADQ2JRwHSVWjlbD/Phk4w\nGnvOP4d63gCJE3Wufp55no+1T8jMJgD7z12Q8vFGEFcNEQkLnZ6hF6z8/ipr\nM0FKHRDha77dF5UMvpcJmetm304b3YJRNT9Tabm2phRdLeW/jMLyr7N8bKTV\nZiOErMgzENae90G+qeSl5f84vCe3Xs+vyLL+1X1Vaz8pKaWANswXjrpipWjo\nD/k1nc8V87nqaC2dxloV8F/vPOHWIn2rv+6r+mNbOB/ObfAR3VY+gNCzy21H\nlvg130TECIv5zPY8OwbpFngL/q0Mdb0NchljIC8rLXcqHwyBsJYhvmBu8c/C\nCbXybGG4fFlcx0kgyAcRYCBbXIQ8nmjezXmbvSSw5dIlYJZ5600fQSNOfLSY\nD+YL\r\n=aa89\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDwYeWyafwlVgXiF0I+Ve545Jx3w2mBPQIhpPu5eLwKLwIhALjXTkJj9BRU0/nh15cX4RKwLgfAmbsmZxVQlJ4FwDuM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180908002213_1536366340756_0.8840574335230695"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908003259":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908003259","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"207d2a2b722baeba95b0093d25ea731a266ffa53","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908003259","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JIj24jWPyefQH33Dgb89rUC3AYikJJh5Wmzg9tji22xmPkRpaqqefbvTjEQaGG9KL4YFw82VzSGb7COEGQDhgw==","shasum":"125f8348bbba7f80743f662764ef0b61ee9da655","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908003259.tgz","fileCount":915,"unpackedSize":1902601,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkxlfCRA9TVsSAnZWagAA6mYP/2RtG902KV+E+6RW3BsU\n9NMywBwzgNlVXnd3/49o11fUCl41d/Ow8b0h+OGDmdDHetcsXoZ5+jJFvS+M\n6L9TSd3EWlU1bWBpdVIstYiA+46tngbf08pIKQZZMgE1L+xKU+4M0gF/jCLw\nADzrOQ1/tTThnrpQT6ZCy0jNIgUWCcjUovo8wPPQfG6dFHA4ukLYzSxqBZXe\nVv6FhXxQA9psSZohRRdi7IuCwK9Pnm8nu0PxCEhLzCqJ3ds1nBx4VbFzF/J2\nytbXX48Vbkkv2rCwZGvjohCSxHFRwtOEqkxXAPxI71md3CQJlWvyWcJsPuFA\nGoAwrQrSutkAcdBGK+G9wpQLAcyrscgbD6jUqvf6UfAZ9rSi05t6ZBNlNIFU\ndY3N13pgbHu3uxvhE+cjTxl4EHVSWoE5BTEceFoZuXeHbHSjLxf46aZbwDFL\noxoDqYxFLynnnvmLufREld+8X1FUeptlT8mRooeCAbw+Qem+gVpafQenyL9p\n2N+U7csP3lcaM3zhtM5dlHpdedeNUgtvDKTPw03NIxa5XbmY2BKxf4WqFWGh\nmLJaI1v0MTh3Jaz1+xjuOB+GkTH/nUhhwW4u2T6B22WkD6BJqIE7wNZ4lkxi\nKjkQQ6aO3XmB7NrgQmzDu1ZW2vxaIikwJU0jv8BCKyaTkU6XsZsx/oqDh8nz\n/lTn\r\n=Ix9P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4Wc2Bgr6DHHivIzGFnYYk5ZX17gZ391ZV0ZHrFFiSdgIgb/bZvnzn0dZqycKjS+ilD+sVUaRCYO48HZ2iz2kpSyY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908003259_1536366942588_0.1437949332296664"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908014511":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908014511","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8bf477bba074d3ea6b8426655dae056aa425ae6c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908014511","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Fa5BTMjJbeoBDDRLIgqnHQ2tMTiX2o8/N25wvcXQxNjpXyZI3j4EslDzVuCmKU10XPYc6XxyY7vzVsFjQzFcrg==","shasum":"0d0e956cbfee5bd33fbcd1aa8b69ac0807456253","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908014511.tgz","fileCount":919,"unpackedSize":1903221,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkypMCRA9TVsSAnZWagAAUjoP/1ax1Hs2eSlPOj4fKwpl\nJYccJ5XNqJbhx3a3RJRg5dM6UuWHzNicn44qnQHft09IhIeeox5eUPrsOZZP\nZiGEriMchlntFn3qOgb4DYH4NdAIZr4qHq5Dsd3Jbm+8rMBKo6WwW6N7pUJZ\nNakZlpJKPomWTJp1vmKcaNkqbxaQNEOJ01XIpVADEsTCgMebQIcPOc2YlWcR\nfKnzeO2XkQoFR9WbujUNbzGOL3SH7k0U7w7nW+D3ulLH7OoBdDAp23quInpV\nFNMV+U0ckqFmmenR+yEto+xt3cJXsUnBsxsaOGslDq7mcmFKcyQ2xABT/eWw\nA0xmdmHxbvTfOLFH+WynjOhdAuWzFzHQpkrnuDsQnPRrtKMvRLcWNEaZ/LXw\nTNY/EuF18E21LsZ9HZ0V3WGjFRUQnTOf3HE4lVfcS/Hpq3WOgGu1IQ/NOTjE\nJOQMylLTasqOeuQVrp5nIcXX2NdVrrXj9YRgHFILD0Vc4/6rhqXheaRnmgdY\nWdCFhWPntDqyrNH17fb5yUywCkDL5+mPgp6HcE/Ay+TEtslgJgKDR+VUdhgp\nkI5RHTX3klHuR1U//VSsf4gt0WnGWG/3OPKcGZLV0kpqkNwEwl7fssbrRlec\nU1/hex0CEY0MpVDlk064cNN9f7egBNDEMIh5TatnvUhTClA53Kq2JX9P6B4Y\noyxT\r\n=zPOC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZeKy6scB1DXK5hAchoovTX+DheKzokh6DY6zHXql3UgIgRHNa5oaVBzpYCIEoFDy9PM2FUAKm7dHbNNp6igsZrA4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908014511_1536371275325_0.2225906303357683"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908020015":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908020015","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c8b169c1ef023607fce6b26a62e33bddd4fa730e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908020015","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wE4oTESizK5Rbk5sNpJkBPyDTl+Pqk9VYqTAvzaIJy7SW33LWiIcJmf9Rz/Q0ubJvUD7f6sc0VLl00H3gSuK/A==","shasum":"9ee1b00af5ab053241fcbed3eec913441c6f37e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908020015.tgz","fileCount":919,"unpackedSize":1903571,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbky3TCRA9TVsSAnZWagAAvaQP/2OiY4QQWP56xfWUgohk\ngYubDEPKxdCE9rMNTNjkXDmMmPpy5Uq4AsV0ZeLPKzzxwJ6McwnbojmUCTbP\nHDGKRraIqOQHP+RPZjWnZ9E3ZqzUbRXyLh4WL/UzEpL3Y9pcArmepCDjZJPK\nxkYoJvQpD6huTCpgFpHdpiCaew7rMM+X/QzrC4V30o+DrnJlAUGtwMAFvHUP\ng8DHv6I5xXK+SFSEfYVNpwqLiTwWDsOZfPm4UZjSihuKXfoTH6wUM/vMrE81\nXwE0i81nG3yVd0sFwkZpR7CpTBheWw4mZRDJF/KDrVCCSCcRYVcmUMkNQ4Yj\neiF13lWqEcsqdUniBcX+LS+Yg9dcCexBqo1xJwErq/iy5U1ohyGf/0PiNEND\nIM36x7zCKuTE/KWy9Hp4Dw4/6gGt/eneKD/3snrblCFfXpcJPAYQDl8NvI+I\nRL1tuQyflrNxgscZxrQejV745RSn5esc//l4VqtbmdxiywzggJK0okQoBux6\nT0NmjSfd4ExdYR36EBCNq77bRc13n1sOuQ4N9N5UFQdZM9FFzCFhaGpBLAho\nvmWTNxZJNLTxnND8DP+gIWo0uRwf7jqSE++EB/fvcXxO/YOAYhRtLhsZe/Sv\nrNsxD6EhkSjK1LhyWuaOmCRLZcowSTVZ1hQlSzY1SJV4L+khdFaOEppU3Tch\nTGjx\r\n=lxr4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQDrr7Ik8IDnsXuSXrvTpaJmK4ntBgAvpihEUNVoYdeDKwIfDdqTMpvtACQKJAT21N3PISfU2jrbqSiiQqhTw5nClg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908020015_1536372178752_0.4324906608544288"},"_hasShrinkwrap":false},"1.0.0-nortissej.default-transform-result.20180908020233":{"name":"@atomist/sdm","version":"1.0.0-nortissej.default-transform-result.20180908020233","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3813ea1855cf3a1f7042ceab81e4a6b6822a9699","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.default-transform-result.20180908020233","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Pj3PZ9rtlYGhndx7ryOAq1Pl1dwMEF5wKAAOUSNirU8C7O7ds4a2ttjHl1Mknt8JCna69QpEGPGHKKIN76rHNA==","shasum":"552b2cab8e61da55a5a5f9d04f51b2709bae0418","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.default-transform-result.20180908020233.tgz","fileCount":911,"unpackedSize":1901993,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbky5BCRA9TVsSAnZWagAAZ8MP/iILf38EkyDuzYP0OPvY\nTft8eKspob53MLLxNDlzMD+TZQccoSPnSwy5QLTXuRMj69R/8P0N5GrwpFH0\nPVi/Jh9bhkATqd5jz45OHK5HgWXCFl2TFE8xj1S8+ClQszJulW+clEjDLK0U\nl2cD2AuB016bjIzVQrjqFxojR11NFyOND1kZMuF6TXOG2KKwuisSIsJLtWAE\nIhRNjEHfiL13Ky0F/6IdxfbTx4Zo+JitQ7xRliRsSlSu8xv4WGAAQQ+4QLFc\n9Nv0cRKm7hg6lgI2Z/VzKWp0AYldo6DW+32cmOoc9USSWM4wKBUOvDnAxtV2\nIrKycOsI4K05sB0FEI8DqyNTEdB0rxmKLIuLp1h+6Q3wSu7MlpfxujUudYJt\nqWy7aD34iYciQqHwWgRejgZqvGdfrHju8pBulp1LnjalRasaCtTLWuZh9Y+O\nCa9m5KUWHljbRxtvTyVkzEUjGoTCgGYuSOw6DxJTJnYa0iUww4zr2Hy2HnEv\nR1MtwRxPWTeq4g519hbMrkMn4u7wqsrAebWHiPaJYjqtr7KrQWYOI2qSYsAF\nX3ldJiEtZnBp4ObYGicaMi/Aj1Et2jbTdQEIsdB1DDweExsJ6ajP1hIEuYCg\nV0QusYzWrXdGh3Qw2zg2ncW031PFbNoi42Y5JWjZmYbBlMXE7eSyIqxuUw7R\no8zD\r\n=XFxI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjybav3wsx0x1pRtghjEr98Jxqho2tdkV82wNI+OAP1AIhAO8RyPwQ4dgM/gIWmTB336veas0vmeRRNctDmEuIs8Gr"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.default-transform-result.20180908020233_1536372288976_0.5475594626627351"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908023334":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908023334","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"f054d66c8e62aefbdeda65f0b649fb51c4449d06","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908023334","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bTUwXnaOcaD+dDJTtsCVOl2WgWLVggWr/v+4sH1uI5SqH56fyiolTmSMtOHpp5O3C7EcxhbhzrkxZp74PVghgw==","shasum":"26957e1ffccf49c0b64ab14d9756714c5defabff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908023334.tgz","fileCount":919,"unpackedSize":1903542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbkzWcCRA9TVsSAnZWagAA3pYP/R5uI5TFYkvq7cWR0AB6\nrASvGQLaVkxWL4+zASV4SjgYchlwRXHBQCnDjvRYRofOagAKU+0SaWoOMt1a\n5Pp8Fl4c5u6+PSvrq7zLaCXJRl+bKyqqfA2YRYdd/J93wkEABHyzBbMBX5No\niTvdQylilJnwX2TpPVz3MAiyTeT24mShRm80m5DcH0UzC8PT/aTKEujU2oqQ\nX0iZlzacq0inQ+Emno/G3VcQGrLqJda4ufQZXSiUeWMFNPK8Zf27ZRSQL3vs\nqG3g+89CeBs4v0nqWIrHs9F28NzSkTXnpr65wd32m68JWhTzfcpxMnH7/1Qm\nhx5SxD1Fl556rsSUlXp6q45PYhwcaKuVGEJtINtJyymPuaL0kTK/bZBdLJcG\n+K1HYoJCZRat8qScctGb+WejYbCdZ+cQsppnXc2lZR4IrRwHDQBfAaBzEx5a\ncH7Jm7+DVB6rKWu/B63yQ+KADiAK7w6zn3XkBkNEtF3liQwBgEXinIlC14su\nkQcuxp7L9Q+bey8L/1fs2C+YjRQCqgcEr0ki1Hf/i/u/KfQMq92XvQt0Z9Hb\nnR3npEJZQCoHiRyP3JdN4sCbAgKWk9TRNpt1vvtO/oPmBRPfJ0gGH+qgfr6c\nFPIrczEfbhP6aRU5DFgNiBUyor5c/rqYkqgfFoG3MBqwtGy/cVdr9QHqzGg1\nxntD\r\n=X6AV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEGIsJSuicP4dRmkoTDWcd1QVipuomULJanJrh+EQt4AIgERzcloE16ikJj68LKpgGkn9dgxeaLELukEXEJLZRiKI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908023334_1536374172033_0.8622809308313342"},"_hasShrinkwrap":false},"1.0.0-nortissej.default-transform-result.20180908160252":{"name":"@atomist/sdm","version":"1.0.0-nortissej.default-transform-result.20180908160252","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180908143715","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"89bd24142634cbff4024296ef79ffbc767948a0d","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.default-transform-result.20180908160252","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ma+3ICul8uY+bhQha/Wv72PYUokDUjiu8WBOOOXNwBAE0KV1F8quinwdL4OXJeVSnAD3QfmuiitiquKeSnnkVQ==","shasum":"30d2e1652a410be0454ef7deb1472d39b4cc2463","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.default-transform-result.20180908160252.tgz","fileCount":911,"unpackedSize":1902115,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbk/M9CRA9TVsSAnZWagAAQ58P/A5gpRIN08Ionm0dE5Qw\nXCjI6bB/zqqGGwc9Ok7L1DaCgIS3qJJeap8KDer04rwbc4rK+mBT3nrPzoXx\nfM+iGmiCFFFPCqdk8Ipm+gPgxk61KMwBgqpsBFTVsATnj03VQpvmPr2Dapok\n9SVx7GHtrHlVDlQJ9GGWDrI7VxVCjt55arnYM+/ObsJfegzEfuYMOY8T+DSB\nPK6Vom+ZTUjhTlpM66yM4wIIaitmxxMKiX8hPi+GmyntdE85SBqAm69h0qmp\n8pYe740WPdmJTEp6cjizwqWGKuSlhIr/SBkrUEuOZ6clDOAj1N30E7fHccPk\n64TgrfbOqknjjznyp9na6FQQL+CJ7Vkx584JKHI0UxJ7x03ZHnv7/cyNzTqF\nlbpntQ3sVAtNrZkKb3MnTCTYvKBXrO7dHIpVjZln4nl0Av2jfjQAORMDtleP\nN34ggQcyuthp9CDTNHKb4ewia+FlDzxUPSDzplO77qAyN8vfJvMoy/eoAPb/\nVpWX68IAmU1emKA6zqFy9byBTNnjX8K06P90CyTsAcOOhVfzgz3WpZW6PNlr\nIpkfgufVE94QI78X5ZXIKrZPIhBGilp6mZXgVZLEX6MXfVEwblaOyKlqcYla\nqZhfcSwjDvYxw1tiwRvhPe5XrCpleluu7+5wieXUhhPEmS0Y0tuhUfzK1i+l\nuJmV\r\n=n6Zr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjaBgMYiNiYIJ4dIClVTAkNzUuJ0pIDpS9U8OH8wtcQAiEAp0ycKu+juACGdCfk6gs6UXWK9l1l9p8BIY2ANODBd0w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.default-transform-result.20180908160252_1536422716211_0.8181260723222687"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908172152":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908172152","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8ac68a33617ef193739ac616fc0a4889d4f677b1","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908172152","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wjck1+jsT3577E3WDjemGkSg9KU1b8HpppMI0YxMwN+kqK916XnhqciTGaJ1ByNHAjBinDTaEyh1V39HtZrYlw==","shasum":"55a747878f39c87b7e40d165d204b21d0d250683","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908172152.tgz","fileCount":919,"unpackedSize":1904163,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblAXaCRA9TVsSAnZWagAAQcMQAJ9oQ3MVx8A+TCgnieHL\nEh2JUOkDXZbqNCetearTQzFlmlQfcV16Pacn9JC0qZxuENvNUcZN8jJBWewl\nGHt0GInQhWlnxN3DaKjtjgAcK2VnkkoJS6O7/ZEknmrZYoVnyvq3at17Qroo\nBZWAJy/YbNeJBVlmdVB3GGDb6iSYNAInkyNHlSJEiIhhzQPrmSXHDUv0VkTn\nmD8J3KUgHDpqseohds2XQMl5EgLxeQiAIQZrawdsDFiKyWOLYqjofdP1YkNc\n3ITjgSjsfFbtmOh7u3XP02vNPFJ1O5dnnR+1CILHdYIG+kG6yMq2BpvlKasx\ndbrjzdcpgrU89Y5ON0/iOO0SDo8RITyxpinEo6+Xa4KrYVIVyamSjaoLCz7c\nUG0xIbTXOh2qJPXFVCV0DvaHPyvLEKuIcOjnaKujzm++V2DeXIbyedQYH5kY\nGmvzH1BLC9o78kP9qo0G5G2zCvUco4Zh+H1WsFSDoyBDO7Tj7YRsozt9V6b4\nBhlWHU+cQK1URRn/V/f5BlQm3Vha/IweyM2rbGFmXufXIeIVl0uld8qsT3mB\n322tYVDupJw4XmYhkJqpCTR7jx8PNHURRsx8huYO/tzHUF8FmY1FOGEdc0bk\nJHuF3CDhdWCe6NKC41C+3+BFuB6lFbEE5nd5HvWv1Sj2gYmkJMtoIx79qZWD\nTXZS\r\n=FNU9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8ZHh3dvvavYUA1A7TLTzaPzgBg6dkV3FVE19nQS5wYwIgWWmQOzLgFZSQS0oCTaE4JL3zdQlvoZp8i+DN0gxD07s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908172152_1536427481595_0.12729046261659738"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180908172754":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180908172754","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180905201706","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"702087059394cc1e660911de1ddf2e3d9d6dd849","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180908172754","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7nbdveWMSM42RePkLYBCMbm5Wvrx5kNKNXOTlXeX7vwxpX8ie8oT3DhOxj0vKAYjl02KtgbkVrWuNwncxtBmwg==","shasum":"2c4dbc15442ccff8948232cffd5aaf7ec150e8c5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180908172754.tgz","fileCount":919,"unpackedSize":1904153,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblAdMCRA9TVsSAnZWagAAFs4P/2sFa+R+LxPYIj6ym3zR\nQMKeug3IO0ICeNQ5yxIimqgJkSPZvF9qc5upggOoqzqpVN0Bf17uuW4IDxCe\n5g2VJjh3kivziwrLOllyIJ7YK48JIuIpuyjdPSGxNj+W9EB14SDqEAc5AaiH\ng/CTtoDgP5LZ3w2ZcfMOd/Y75kAud9mslWvEHfH6T3I5h/87fD1lZNBp155D\nvSvHxs5WnDtr38pmjGfQC2Dk+EBuOV4sXXGCHim29Kk+ibhObewsy70/2XKW\nXOHiylRUv615AJkU2FrjjTgvNDoG1hp4uYZIPcyKei8mSjh+CIbS2LNiNGhH\n2dYUFKxMvFC1GpuZMAZ8JNzEponnfUfyupB9vclIkJC48wAlLURt/OnGWb67\nDVDAF2bQptKWWCJCVt78WN/zLZBKbQgQBG8FPqYpgV/P02plpFwbwbCvotKq\nqabnK00PJ66JQPN4hm9ElABXt3yhWKbJlcmdlxAoq2HAt06+BhPSk2HgFpNj\nTg+FwWWxFJHq3HSFmVzIX00aPSejcTJk/dg2IgSj/WFm7PKKnWuhmABh5ev/\nBv5ThbSSp4JujC8Y3R1RakGeRZ84mlzwMjB3e3+qTOEvIdu0fnfrj3gqz4AS\nah/doj/vFsTrAJWF4Q66QcNWBVAWx6axSfzyYbgjcA/HpeftUgdolDBJ/2a2\ntr66\r\n=zNgp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICXAug8HGWJifI0xC6SDbAzyRr3aPrFzaZSubrS2IMhtAiBpss7/bFXTxKQdnJAvP4pqSfmK0hdIkOPXYmidSNUf/Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180908172754_1536427851283_0.10613574029460171"},"_hasShrinkwrap":false},"1.0.0-deprecate-global.20180908175912":{"name":"@atomist/sdm","version":"1.0.0-deprecate-global.20180908175912","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"03351f26ea45ca80cfffcd909964833466b8df34","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-deprecate-global.20180908175912","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wOy8V+RIek79ZmgmtKTICciDyNHI/1mDGFI7xo/pKRhc0jyUqGxb/ImYKLwTLmVg2Xs5OGlSl8vXAr70u3pMog==","shasum":"ec881d83a90b1d1c250cd4782a390cefa3e5030c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-deprecate-global.20180908175912.tgz","fileCount":907,"unpackedSize":1890684,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblA+sCRA9TVsSAnZWagAAo7sP/ictuj4ATon7vkQc3QFO\nq8cU89gusgBOssRHwWyHmhv8xw69ymGrE58xoocLCx+y80PHSeDydjK8oWwa\nMmqRPdJyripK+X2U+mvMvqfXFmmN10uIy9YPMxy6RO3PYoWHrw30jsmYg7jx\n+U2lCCVgXUV4/Gn/LEIyJXPGgp/jsqg+rLhGXAyfCdUF8EsDqlwrYpCQnGca\n+J17UGTLvoRUxSeu0Y9k4cZ62vVCKwHnm81k4emLIW1BFM2nIDTfaLF6BDva\nhs5zudp6QfTAfPSvdZJvDn7a+kLMd1p5eVTq/cl6Rlknf3MD9cGscZMAeT8F\n8OTkiBQipZRHg+0D3PCO7PEKo2JPuFxGTwNtx4ajh1ZemZRTuIkVYZgnFoyp\nUMJkMc178IfnvseVICE0MECd4bAjXiKERKh7ZRV3INR7BYWsEDpy6gbMovsX\ntBndxYkMakxf3XuRXpn5g9GcksTFhKsgm3M8kj89W700rXiUgjkar3LhDT0u\nNjBSROaR3ysj1vi8vb61+yog4GXWbItvxPRWqG/Am8jBmvpYvaTIcU6cgzJq\n17j6TwH0EOmP6aX6qoQSwU9N8ukBsd5XWb+qCPNbnpJv9Yl2s+aKN+EYw6d8\nuYTA4VLD5bYimLxUXvxRIxJrKLCKecdY3WGfQm+qq2ZtqFPE0sT1exhLUACC\nSlwd\r\n=K1eb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4FKHjOm52tAPGFJLqIXcHRjIvyxjv0tifDsUMSDuHxwIhAInVYos5FXijgSUmU3u58Q9VqbQH0S/ZqNvt6j4SNnBr"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-deprecate-global.20180908175912_1536429996088_0.2373474492173404"},"_hasShrinkwrap":false},"1.0.0-fingerprintarray.20180908184713":{"name":"@atomist/sdm","version":"1.0.0-fingerprintarray.20180908184713","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"04ed38b6759a944c1c0353e25c10d4d5493f0fe0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-fingerprintarray.20180908184713","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3xSPRZbrnm8fTfkJ0TdKuflcbNMlYoP3GK84v0rdKrfV/K1yK5weItGtVV44DQ2KKskbvIqcfVSmgPC1z54POQ==","shasum":"a8c212880c3c3b49e432fd3d708f88f330a1f18b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-fingerprintarray.20180908184713.tgz","fileCount":911,"unpackedSize":1899382,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblBntCRA9TVsSAnZWagAA7hsP/0VWbCXata1wg4gKxoxY\nr8ftajPir+PS0xNBaS5XD7kqPYEHLghqc7t4DX+U/ObhMv050VnuDekdOCQE\n5s/QLSlizJTzGwJcFbiO/80+ZtGRij7hkNzdCIExKWvMQHjrKnQD1NjUho9V\nntOKmWQ3JItb/FF9hxtbUBocZfX7u6jCgA5lUObOoasTjUFWY0sla3kj367L\n4FvCJJviHXxUnSeUQMOpaBdAqw+3fMkg9ATnI6qUHpHfdAzdx/k/apjvcvD5\nZjdiGqG7nfkM2jHiOIf6mkUqhrTRYwKt8mALd9EKX/V1bCFBz9fk8bmrF8kV\nD04ErNpz0lJYWBrx0Z1OldDIYpdP9tScipNcww+x+FIRWFYa5+AJS2XmJi4h\n+ir4VpcIsahy05gOd1lMW+avmcWo9du6uQw+XnmzQrxKWTWrHth+9H8CN9Sr\n5O8IXOmt5eISZL0d7AN4ERukVnPkgd/yG8E4bWY2ZM0oXRWEslC05eEC7fYB\nX9ZedEu0iyrdWm4X4igv4V90nex3LG2bmM6AXYHxh0b30Z/O2WBnN2iz2uHh\nx9AE7Pdo4o3n3z4X3eDM+2A2j++BfihXhtQAq06AaiOWDWlLqo7uqmFBWzg1\nUwfZX8ZL/15zpuii+n7+cAJGNYRVuFpl2ov/2pzWwGWkkkgj4c4SiiCooVjW\nMPr7\r\n=KrVq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvDoO8DZcVWapcYmoT1OQ2jc7+vZlMa2T57RC/iie6FQIhAOCK3MeQ1KH5ZJaNicQgParBwMGi55aLNfmOx7hPrhgZ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-fingerprintarray.20180908184713_1536432620111_0.41721282911597024"},"_hasShrinkwrap":false},"1.0.0-master.20180908185452":{"name":"@atomist/sdm","version":"1.0.0-master.20180908185452","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"7bff6f250b1800de5001b6679958aa55d5eb6ffd","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180908185452","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+UpsZ9BsmrIW9VkXI0hSNIiT3ZB4Gy/VSW2OP1U3REYPeQJLme3ozGHoshrTYeW0AtGuRGkO/ICqZ75t+ekNvw==","shasum":"3f89385b5483460be2fa05d68eaf6df8ab72590c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180908185452.tgz","fileCount":911,"unpackedSize":1899372,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblBvtCRA9TVsSAnZWagAAYGAQAJFojBh6UyGrYPeqQAjl\nvzAXRZuUmMvzxwaIHc91/sZc4c9U4tHmnFSPvpUMB4s4mMiTM30GikXkTQ5S\nDRWt89dP6CLrZvBQCtCz3OShf7XBdg4gLg2onz0GkufGGcpcwIcWra3Xz71l\n6reBecbjHAdJ6OqvNlkh+71buhQ6ZptNTi17Lfk0YfOyd7LD1lSnX5aPXmrO\nJCxti0Cukf6E+0cqKcP8fvuc4ZhNOPTxXb4V8GSHzkjGImTmF7xchXzzcECL\n+c8cCG4BNrZOyQrxt1w+91VN+kfvo5FvM+pyyIM1SjozuqniaKlALPSkdwTr\n6XboERmTmuTVkM5jcCqU6VlbPfg2ZErPVAn6jzMc/DkI2EW+w3UyfQ8ocD3I\nBt0yJtI5Q9fgmL1VFxDqfjfRHnXhm/aW39IrJpQTXP/vmqFPvVZGSCtFRCdQ\nQUhkCPTHZsFg2IkHsyvIhQ2rq1zG4Xl4zFGcwHUD+jGrkl50gAi+D7OxGGiV\nFypYPwRshPHrWXdmQiWCbSSS3o5PJQem0fzmD91yVf3mVBGwOwlBDHyWUqGf\nYeC04m6K1OAFWHYfNW5aD51ihain4o2vSclNPxm7zLHHIARSN69orc8zStYN\n0eHOZ2QU/dL4lLk5Wq/nhOshC3fwtxHG0W8q7EKlW2uld0/RHOzunHpbRSmi\nL9W5\r\n=Fd+4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA9Fvrl/Ka/dxQ203z1+ML78RonEdpznnGP3lWriHNLuAiBfZKD+0yKufKer+GpHJMThkhBSe0azV38MOP0zZ9pKfw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180908185452_1536433132761_0.22223998572237336"},"_hasShrinkwrap":false},"1.0.0-master.20180909022053":{"name":"@atomist/sdm","version":"1.0.0-master.20180909022053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20180908173001","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"3d2434520b9c95930f5ae9df0d9a0423b3089af7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909022053","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RMJbjZL2ED7/hXvuxdJQYRBaNkvVCLwD2Y/6AHvHUGS5JryS10R7LNG0PeHMubPWw3xvyedr6ns8vzXtW9u4+w==","shasum":"f1ab35e36c0df2d08cdcbe0c74607e722eba890d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909022053.tgz","fileCount":911,"unpackedSize":1899501,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblIRMCRA9TVsSAnZWagAAn1cP/Rh5C1S0DxIxT+GMCQJw\nWP+cOhgJ/yn35P+ojO8csHB1ooe6fJLI+I3Bp1//N9LBcbdVsu6fGtyC+LAH\nztBVV8clD81QNoJzJFRzuROvshh9p8Gw3ALapmVPeJW/TgFlPukZUIEQaNnQ\nBqkyOvHuQvdf91yyOs2kviW0ri2bFKOma6TlJ4WG+8//QnF6aAbudMo1lHL1\nnZVvMmyCvbEMfRhE+1lkHhKjD9DkLZia61uDxV/7UMujl6S9BsmxS36WpyQy\nBrFmpyyFtf+wVDA/wKP49nyCv61LT2o0IkBLX7cCiBCUECxvyWn7BrcqZvXT\n//tuUFFBQnr2ZCGp8y2bhFow4XbrX8OdNLfCpfUvngaAHCuo8y5RbfXwp+2G\nynCfubKDGbC+DkTdMxZm7dneffSVtYW7Y6IMEMNP3SjXg1EUXySOWfeZDJhH\nY5grJ7nAbVhh1kvtO+NKapiYwKKhq60WzR+jtil/MGouTWFwn6aTNItEcUty\nNWlG50o33sI9K3MtFJaPxMEoDjYNIRA4BX/9KSFLRgmC/axwMwsW1RsTNJX1\n2JlXoRkB8qAumY+RrzFK8VEsHBsEomy47pOEFMuWmxKwv6JMhHvhbYglGblT\nPixaNIRy4YyZjHrm+aykX1k6uHuM246bgqD4g0yYd61yn7kbOYg9w2ox9stc\nJNi4\r\n=3rZz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKW4KC0s8C1ZVCmX4a52DB4T9qxHkyfFRz+30OQCzdRQIhAKkvJ2CaSIVfw6Sl4Svyf5SmkoRBsNtOwa5JS1hQBLDe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909022053_1536459851655_0.7321537518093473"},"_hasShrinkwrap":false},"1.0.0-master.20180909085133":{"name":"@atomist/sdm","version":"1.0.0-master.20180909085133","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"c139987f1740afbced963745446d7ab53243652c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909085133","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DG/SfnVuGrf+ECOQnhCoaFmXBnu1+jyc70psu8mrB0Wm6Ptu2kXp4raxlAGBeBTLJ7tblRxDuw+t4hc7Jnj6fQ==","shasum":"3fcc41cc692c6ef485824b6f4d48b9ac01bb6e03","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909085133.tgz","fileCount":911,"unpackedSize":1899725,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblN/YCRA9TVsSAnZWagAAEi0P/3hBEK2DyJcQCcLaRNkU\nHvZ+l4SOGnwauDrLdgJFgldFkqmYGLRVo+ZqdcvRsHnG7kiwMltU0GsPbym6\nEJjB2GLx75DV9Lh83d+m7mg7FF0jxsAUTfZkY5nw4ombQrddySOgrGMFoSHR\nUISe7v4P/JsMWaR/6aqGaPkLpGdLYw3yLqe9ZKvpr02bem6ojNvJ4uuWoWsg\nPrNvZepB1VnwpcyZGIVmdoarE5gfgNNJ8nuF/MSY8/YXFjbfo7ttBAZwE7Yp\n9IRy4vZ++5ynj25KnJXnlYIGB8Pk/xzPvbwtZpF69cuS5lfEKzkelDKRl+Ei\nPfGoTk3eAFFayEN83ITEzwSihmMrk6R/QPRx9gL4494NS3sYdaItQjDVnjLP\n5ugcKUZmbhAZ4oW24xEfGn1jKwwaAdrfhc7ba18PA/LuJWJBPwmgPMiEKMTD\nCG4PQUTL+lcJaZxBNqC3ctTGtpfoOeuoRHEfdJ8hZHulCP8mC5M0KIDgGv2u\nTQ3M9dj9j4NmB9Bm9rGpmeC7VGuo6PznkE52aNBqHDmoIkDaFZpE2MY/7d80\nu41Mrh7bEtaAa8fbSGMFH2QPCmxCicaskbqC7+lnpoToEwoH5fVcBPPkOkh5\nsib6U67YI3l1a39QleyMS9UfmrxsNV90f6idjpV3Oazs9/J1kv+9aN81nsIF\nuCNq\r\n=X6Bv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFRflduKVkLLCFa1ghM7jO88hz5n0hrM54V3gzh8UCeNAiBt49S94qN0XPNempk0XEQpTkYzCzOHKJTJMOz2T9q1Qw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909085133_1536483288074_0.773329397996328"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180909085220":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180909085220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"865b922006f24b35eb06a3e9cb71b8bbc6a639c7","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180909085220","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-54YtkFl7TRZLSzTeke/nEuvcpsR0cNIK4C9CD5ednm9P4nU9Fr13c37j6qJPs2e2O4UQvZeIAdfTu0rRorKEZw==","shasum":"eafb1a71729f1ec0460d3e4ea5487f72ae22621f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180909085220.tgz","fileCount":919,"unpackedSize":1905388,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblN/wCRA9TVsSAnZWagAAlWUP/032IennfenFFUpLzA67\nIBgjvh5WYQ2v/TDy0CB1yCprAwzdeZtxVaryKMwevWeBUaTgwrP4HuUNPAHn\n7KD/Zjv2xRFE6RhL5PBvO7IdTCSXGSXHYm8dfqNLfrY5XO+BpJVWllkgklml\nEd6Faer1Y5SmBIXTe//BSRw9lhib1AiEdYygP3T/MBVGTH1X07pAnnFj9T+k\n4Od0SG55tFA/+QqLOwgDqcB9jbpk0NzI74M6NN9pCJdsOwmfA3vpyyWAA/8f\nVm1KPxScAyDHVdFwcBWAkhq04CB9+tsck6YFBvxcUw76CuHIXTrA61Yne9ns\n6dB3maB7RyRnRa+HjvInbIIZEOMw2xpAbWxhwMBp2+nv55AsP4VRhKZQtHyy\nfVdze2TT3fnHjcEdfyhn9jykY3bYPfWB+L9krjLmYfJABj7vXYP1xwui4BEM\npC5qd4BynSmCydu+ny2DxJYR9ltd5yis42OEkHh7KQB1uYB08YhENnDVSJU9\nairOlnLZz6pqSVgg1SgQ+3RdYpa65EoPi6idlFDnpdmrZAjyL1iZ0LHK/5d3\nZTynwjs2MskYj3OlX97PZf6aFg/iEmXKnw40d6LWM/o06WobgnMqox/K9pyN\n6TfV0WmEOQxXPRAHIYFM2PrP+j7TKhTk7cxOaNu2N4ZJ0fHPEygdRyyo56aU\n8yxI\r\n=vGVZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDcSyoVFEMhzRZGbylyI6IYNkEi5ct6jE/jTnUfAOyVwQIgbjZB+h8BGevJOv5zCb1u8OhQUNfsZRrzd4GnyQBabgE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180909085220_1536483311374_0.6004234879674326"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180909090246":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180909090246","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"feb5daa13008ed97a5abf9341797c8509f0e0fd2","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180909090246","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a2bUqZgn99VRLEQC8sZKTc/Mq+ODbkfpIgMF2lTFkt9TP0AUSZz+YObjAe0Yojm1latZOX8LZBI+QwBlhuFmEw==","shasum":"d960def623966276a3ed60c5cc5519634b1f5bc2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180909090246.tgz","fileCount":919,"unpackedSize":1905503,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblOJWCRA9TVsSAnZWagAA6RUP/0Q3pn7AfiiXjkee5KxN\nn79mkzTS1FhJKUJY8POeu/CEMOYOIjc+lZGcnhpEtdn6Wz748QJzxvOXXKUQ\nNj0EWkfL2IZJgWljquOdqTXd8LuZnUinMSb+qMg7PoMBM1iJzD6OtHeabD7e\nD6qiXGhzgVWS803OBSOxvNGysjQlBZOfk6WwO+KKYHJYq1ojWs1jRFrKLbB7\nl6GTeomrwH6d9W0Nqdg3pLbVGIitpKk/7+uYrjLeHHcm9XK4tDLX1vcbfN9i\nYKJ07tQOghrjDcXQBJ8tleozm3Pu6SDSKNFKPUGP0RmZW96em9jD8MW9u6uC\nXUW8cCXBMHeGvSFfBfBxkZRV5DS3v5QrzsD4758Ft8r5gEy/8xa6xapmKIIm\nZOchgNDb5QOFGd+aOvcLh1PsSDSVgTNnk27ejgk/l3oFjep6Ozs+Dp7XRDJT\nBYM2R0/R4LLUl29zwtJ7P8QANFCJXJhDWim7a4Ecr1lJsdJjFj1bh3D5wtca\nvOayFQ09xTYxR8w2czNBSr3jFZPhdZgWzzFNOZx/VUzkRkjXWrWDIt3/y4rC\ndzE2X3EmYr9sckeTuXBUX+AMXCF9AV8zTIbw90tOFf73XYJKUNWPTybbBGWI\nN6jQXSrmSn255fnV7Pi7JP67HRbpesMwk+pLTEUtkRvdoJAGtV0NwyvSFjSF\niXol\r\n=xKF2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDb0UQ9J0a86fq2xPbgdRvnRTjRF3KDFlQKdiwEwANlHQIhAPdELHLgKfHiYQERWV1pgZ+tkhe387mPLD6TMD/BBMWK"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180909090246_1536483925708_0.8900041341493632"},"_hasShrinkwrap":false},"1.0.0-deprecate-global.20180909093829":{"name":"@atomist/sdm","version":"1.0.0-deprecate-global.20180909093829","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"2f4c84200d3bb71fb780a4489eae179afe66a6b3","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-deprecate-global.20180909093829","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-abyEJrq68crXjo64aW3REDkW+GCvTFad4RIvcoK+HUR6e+ecStU5doJd+fdESnjwdh38eSV8GcPTO9P6/WYPmw==","shasum":"a3e7a64577671fcb98f12b0bb98df239c0aa3246","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-deprecate-global.20180909093829.tgz","fileCount":907,"unpackedSize":1890821,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblOqyCRA9TVsSAnZWagAAMDAP/3ttw1PuH1HYs8/C7WQt\nP/Ec/kjaBMNxoSmspf4b6UOFe9VMNNSEXU6n9vsU7oJhl2cQGiu28TfGbXsp\neBbNiSMXtfkUDfJ+FS7i8u/C2C1qJQQRmefW5J+nYdSlxvYnbXleDbVmwX6/\nq9Su4H4cDKxxZuCBvZ+lLzn6fP6btalLIQGKNidvVTj6g4pZpMtgSt0hhW1r\n7Vt4mjSpBZxxxefg8YUBXcTK6SdJhfLtEL9b1+JLo97Iq9kxdvCDqiVB9jXM\nr40X4EY9VL+KZkL4ZRazF2oENU8mQ/oXcYT8x4Q2fq3+Ogx41hEPESfg/bm2\ndjH4gj0hWfSKHatyGMfShYQb1QoEwmwGAUSj3ls3kJaUQ62Di6DArBDziQlk\nosTTxeYWN7eN0htSGbK7QESG43TjWu7tuQ0DKSioUZ9UxVcXhEUzNyLSvdOt\ncwe2cReVaovGfULfpOis7vHsZhWwmaAjCNI8Le3mp2zO7eo2vJRdjIZQpyBs\n4vmywgLaFK3BK4IlHn3cRdKi4L9kK+Nfn7+cWsjYOaP/8C1+8VhVEzWWc6Jh\nlxZM/KRgpvQTdysXT/RCGnJr8cp+6+uhfTqoJQGyiL8JVPQ1knBF8Fh4pYt0\nkTFR4vqbpyEHQjjQpVJNI1P5i9/jQHVwn4QnyuvQNz1PR2odfxAbTQvX+ZoE\nLSZl\r\n=mqT9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQb++PiSr277IGIRjSu6CoMqCkI3MtWhqIssErYjwLiAIhAOs5rPUgJbgezIibHrhXVgFaZutwpFlWux+DH2Vp23Zk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-deprecate-global.20180909093829_1536486066043_0.9312118603741906"},"_hasShrinkwrap":false},"1.0.0-deprecate-global.20180909094050":{"name":"@atomist/sdm","version":"1.0.0-deprecate-global.20180909094050","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b3433c257360550822ae46e7b783590f4d5d1a9c","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-deprecate-global.20180909094050","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7ORoEedxNWryEwg8yNT5gDxvOIDNhIJhnmynLfdtcTtv7wkP/I4bRaXID9Wo277zrtIhquJU1oRBxOxczA+nIw==","shasum":"324f6104e74672560ff47546753157c31d3e298c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-deprecate-global.20180909094050.tgz","fileCount":907,"unpackedSize":1890821,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblOs9CRA9TVsSAnZWagAAFpgP/A+6Gm45PcyR3H+mrEOU\nRftxy1zjMmIf/oFsQ5rlo50XgVXMQh51+eZBsZuQ+yJ2NcJnTIpjuuo1ve4o\ntodg8lZZCIs76VpMdxwzlSMjQI8HIi1DGSXiGXfmQgVQ7ISxuzSeY/ZZL4nw\nf98A8PcDpmgcp4WVwZ9TvNJP9q+yQ9gpYSGqdjw09xUJOOmP+EkbJ3bu0yVa\nDJjrBnHH6quK06T0PyOIX/vbzkFxs22J58w9Bs7jBMWhaN3am3pNaHXsHUW1\nNp6Aek1gveCSURMs2tBZ1Cb9mTprTjc6ebZ3OgEQBElB9+FaTOUF0u2Ct4Y5\nAItfaMxxwqdXUhp6kXbs9U10Qe7+u1aT8hoUSkon5XKr+G9zCX9V6XbwsqTO\nz5KgL8r0g6gjce6Ph+u8lQvEc/3mGp1vzR14o3kbvNivv6jjWZ9rYnUlufqo\n+2eUv8gxm7/S+j1dlfrORbqgBTLiJbvZP168/3178w4rP1h0TdGakekhXt6J\nikGet6vjTaUdrUfnyochBcnEMepqmMDraz3mqcKEzPnqQOgBOqVlYscoezr9\nfEYIB8D9bX5uorcIaPkpbGtqoJ/skcmD1vzrTzfuEPaQKuM0ojjqOg3YoclP\nxRg0yzuuVWLX0M5nJy6/gafOIRMJPKu0Z1TDtCsMicyBYOZk4kmnLZ/q4Xml\ngCnr\r\n=XcvG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEeCDe8mKEPAPtb3xBtUsGD5nzi6jrUShlwRy3jZiIi0AiAkr8e1zIt/bSPWDcsP5FzVZ2Ashs+cpnqWAB2Dleao0Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-deprecate-global.20180909094050_1536486204427_0.042573320461867015"},"_hasShrinkwrap":false},"1.0.0-context-created-event.20180909094224":{"name":"@atomist/sdm","version":"1.0.0-context-created-event.20180909094224","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"dc8888995dc45f0d0412b5885c61888ffaa407a0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-context-created-event.20180909094224","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bWqSsNEwBp4slfPw0uZtTQn96DPjcexO0+FiUIoHOUPwoVWrtq2eieSbMtjjZPG20tCe3ERliwtyyVXL2xQKug==","shasum":"4e36d19c7a072c3ed847568f4271bb644c0c77b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-context-created-event.20180909094224.tgz","fileCount":919,"unpackedSize":1905706,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblOupCRA9TVsSAnZWagAAJvkP/jffBPRSHgi7HeKWJe7r\nHt0iJ/rEQLHb/9tznJzuUpIOVjBcf9r59g8ut6ZBjVUJQVZ7XSw7gRXJxobF\nhiE+Fv5RHPYLb92YOSBsChzvbz09Y1m3be/woiWOEOxXcRZwbBe+tnswE6UK\nChGcSIGuP0Cvdfh9njA061istwXeuxAp1d/tk6xXLiQE5C3FlnMj1nRGKLkU\nEHXVgmrEZfruqjvWfjUsEhiRJALNkqm7dquLY5TGKL5znCy4+HeHQh2Dssfd\nq6D8eklXPBubjcMPWokhbycP2DW6nYKKkssVK81PE5xBA1l5oz1HR0gf7eig\nr8BBQb/7yKTFCo/ZnNdRfU5ZNx0pSCsC+lkcMt6ZfyhHofgZtLcRd5+1jC5M\nfXGYvatZ52eSHaco7P0VYutG+RIS8sNOd9W5cuSX3F8MjErA2C3F0A3uVUBI\nsV1iIE+ZNPBhrLw/erG7DF/qq2tXB/S2C+rZlW0IBB9X6C8P8G8s4WPilhDa\n2+SOB3wkWygyaYrmucJku2b1mitX29Fl+aXaDehmpC4EIFNjjTMj1csu1fPA\nwfCM4FG+aXlAJjmkg96dRG34aL4P52y2Q5kPJrWwdf1sH6N7RmwSYB7Sp12r\nvctxyKZ922Dn67YV/tK5NhEkBnKrDDwTimb0r/+95UDuVtwertYAm5AZsLyq\nLY1X\r\n=4Z6b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDT+Irgbj0Rt+cojBx1H6WiSDiJ00M+xk1uc0zqgoe8JAIgd2WLjqorthPyrwjEzVEgpSbjVffZV6dzc1VhkhdTfeE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-context-created-event.20180909094224_1536486312700_0.8372571558045023"},"_hasShrinkwrap":false},"1.0.0-master.20180909192848":{"name":"@atomist/sdm","version":"1.0.0-master.20180909192848","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a43974799ce9f44fb801a65df055a53d34dff718","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909192848","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UHJoDoSOWdbwUYTumf+Kk3ZHcN/XKVdXd8TmP8bA3q2sEbclaznoPfVtC1a2P/iamawIehsLoWnejXGzvRvFhg==","shasum":"a35ecdfbb4da6eb8efcab8bff9a4b787a3149511","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909192848.tgz","fileCount":919,"unpackedSize":1905691,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblXVACRA9TVsSAnZWagAAujIP/1Eez1GoU5+vITgPd5UW\nkZQpUBBAa1Qku+Gt1BaF75SdRSMcyO//C28TiGfKPqPaq7ZhlBhNkZ15unUN\nUzldhLVPcCVTFTIC4rKt+W0Y5sDCiPVEzWqakXC1Ux7ZH0dsxWdduxmkZpNt\nVBlO2zmKwpPiz6DzYushatOcvAnD0AGvS7J4MOCRn7jTshLWQ4pecONpv6qw\nrAI+9msju5v6kpyNd9weAPG6HxLKaklM0waK/ekkwyo+rVcyoSIJ9+eNqifA\nf7IhZVynuJih2xgAgiDNn/B85Lx+srjspBA6SvmPNW7qKxIUTCMDhIajmT8F\nsb7GDh7BaHg79XUrN2Z8K+Zkr39/TcWphk7pl0FhOhifj8Ga7kyW69HLppH3\noSIPawaKFXwRZLvM10TXA2W0JqeJvk2UI/dl6u5iz3bXBJNCpAyuN4UdfeFQ\nVMAFvUaCjCLrPM3vyto/eiKlX92BGp/soMANxbTvF7EDeOMs1BpXjkU0ff/G\n3M9O/I4YtQIh0grJc+jLE+U+6la5CNjzqmZp3LT+AUcUT1ZGRyYjOPCZNRgn\n3Y44/FQRdwev2KCzBqPoREC+MhPy4Wt1EuAJiaRC0oas+CLbfz6burd91/hc\nA41Bue24N9FL/L57B2RHU0vTc89bCUz6g86YpKVSmRcaEoqngDKfegQkswus\ntUyy\r\n=joiK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWZM2vBX+csrUJD97jYAdn/V+Uwrv5CRWvLpFFnJJ5gQIhANI2NCLbPFQF3eXGtsUUyPSLgzjtpc/AtWSzhNvyah1L"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909192848_1536521535238_0.19358476204138575"},"_hasShrinkwrap":false},"1.0.0-master.20180909194634":{"name":"@atomist/sdm","version":"1.0.0-master.20180909194634","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"a644741b95a8c92cefb3427ec4902072cd39a4db","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909194634","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qUlS60lnH/YnVomzXNON0A9hGJoSbbvrbPlN5ROwPrY1cZtkFvouoDK9ZSkGVLB5/9Tb5sGrnfqBkt3H3XlIgw==","shasum":"37d7d8c06ca8d4c55b034e16ec41adfa96558bd4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909194634.tgz","fileCount":919,"unpackedSize":1908790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblXlMCRA9TVsSAnZWagAAGowP/ijro9rDmQJ2KfpOOQD7\n9nzguJpBuPHQkMLGWr4A95X4fI7Y9PkYwrQ7LzO8S4HoXW8Z2jFcWdtRfhTJ\nzED4uYjiUpHBeI+nsLFfEdlqHmqCwTFvj3nJmlKcuFEoRAV64dkWpaCMhj5I\naxAOndL2LCUZXWvI8wv3hRNdT/9TKGEU3QNJRtnx1W/BxiXPnA4+fmiSZW1g\nuxaALKOTx+4D01MG6pl5MHNGOMPDZKgTgn6zG8SlThEsCHYsAi6b6+X989dR\nNAy6gRT/Pdy9Rj+tQnE8kFu7cc/bwVE3/fTE0ekc01KSlycUUfENZcqgR9jD\nOIocHPG8EN1eaFuYMFsYMy40hH223QXGhTIyPPIrhSADCw5S83DcypGF5kt9\nVlYOFe/BIOqkIpfTJeLOFcLcMcVRWNlJJCSsaA65fpH+UTsOg2lFF4m81Clq\nK2CV22gNXLKU1X67+gryQ1AHUtcjLzXdhSlMuumN7406fA0+73mo2P/Jw+j2\nROQi7RkCnaEgrKbxn/6Su26FdiLbLKQ74Ekh5IJv8aaNF+NSNAdpeHFzbaNu\nu6p41WzoX9IkKLlGMTXALDVo9rVJvcRCGeV0IzXxO+9CAea3lGzgl7CXJsy4\nAG9ZTeEmpGjat+1FZ9OjFIeDpD/DTBUIp2Q1RlyifvxZ6JY9lm95Bw24G0kd\nzKFW\r\n=7hXx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC+WJaGoQdoSRiRbz8xPgRS8qGC4tQJZt+fjChD3/3BOwIgNoRDJcQt5TFvz0S6a+g3bf+SbKCPEWMB/F+TOT5rGRQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909194634_1536522571299_0.22385401200283983"},"_hasShrinkwrap":false},"1.0.0-master.20180909201953":{"name":"@atomist/sdm","version":"1.0.0-master.20180909201953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"40765c178c4f577ca52c74838c1db64137e41a2e","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909201953","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FdS8JNAgYt53iSEZCy73iQHppjL90o/SYyPzuzSiaF2qW2CYNb8e3qtBi8qGJ/oGmBaqIXGiOGwH5cOJaBWrjQ==","shasum":"7ba128a813f9995f533ababf46c1472507dec771","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909201953.tgz","fileCount":919,"unpackedSize":1908866,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblYEiCRA9TVsSAnZWagAAYHoQAKE8xWdKcnbUlVzc4avZ\nIo7BrDrMDOcJjnKHye6WmEGSKyMzQUziBFRNRv8hp0XKUBjwWbQ2lmZcAPVV\nzJ0TV851W1lah645Fad9uz4uWpfBVkEKLCMWZI6Nx2lD2TgjoMQY83ZCP/5Q\nWGLSD87gJLDczdMYHWrWTHFRYYJcGvO8VJVmvrKs5yjJaDGH7lm0rplu68Ja\n/5xnpT+26jctPJqqkSPT7dGMPPYuPZDE0uy/iQu8zTOdr3j6tTL8UwDeNfRS\nNQGKVrND++3mbKyvm6LCfSB8nqU/uS8OB5rQlZCSUt0azYP2Y1WoITiBJ966\nYLC3jys4n7EhiWXa+eBPZn2Lp9+4cKr54GiaL+ragZkYY3F0QeiQq0xKnsS1\nYGGh5dMRgp7Vd0Zu0UK5Dv6AqXIOcxLuD2SC296zNEdlwMqIGtl0mIsfzLx0\noLWvBW/C4N3UTf0IBLNEgyfkhZPQoKdCHrmEXnCkrwEI08rGKg19lKo+FxHP\nN9IgHXb4uDzrP5Wx2mv9V1FdizDzdN77wIJANpgn7cowRVltLI0T3AiMYa+T\nTSCepGSPwvCcHxzTjECB7jYUSyO1OavOy1HJUyMllVh6X7jvfxkxrF1Ko538\nZH7x5aZ3X2/VmBPIvzM0y9+dL7zADQGAUOWN/3cn8+fEUPimvOBO8EfU+v4D\nDtiY\r\n=Cab2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPaXJSQrc/gR4m/XE3W+j1eq4mg3TeXqFMmv1YpdyhVQIhAM8pee31O7LcSsQMSz5BxR03DegIx+uOqw8LsVnanPcN"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909201953_1536524577744_0.0969178511643729"},"_hasShrinkwrap":false},"1.0.0-master.20180909203432":{"name":"@atomist/sdm","version":"1.0.0-master.20180909203432","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"427bf7cfaaca2b52baaf37de06d3923a4a9716bc","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909203432","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HA1TmUBWus0b5nWOuTCfyXvI0MQ4MedNYxQNDqAHDHs5Fh3vUBreCMr5ZFkajbzFQHUDUfsLNkamL3+5w5eG6Q==","shasum":"ec092679631ccba26dc36a72f29561da2fc81bcc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909203432.tgz","fileCount":919,"unpackedSize":1909003,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblYSRCRA9TVsSAnZWagAAsbIQAINQ/SwXO4Nf9KdJYUTr\nfBsJH+yQtR45eEYejKoMAw+JS3sH2/DS88yUSeisXrUkPNuM+fV5eOVWChga\nN2oJFpuYAoYntfdgp7EuoYCnPBTLpm9NaSq7g8PEe+KJ+VZnAUDgq/pknP7T\nNw00Dp2G3FUXho2AWlROID6bdAaqsyCAOKVs5S4fF05o7FgILxbHLOw+yDVR\nTum2nr4+3m8wmRmw2f/NkKunJIBUDLhEcOQn2RcwwabNsZTfkVajBwA0jN3u\n2lm6ysA1aH+OZKt1QwdPlofsiXDVyAKUjRaYk7nxqDgnmmBSgNc5QPD9dEdM\nk/3rdpMQWwiOmpmK+i1Np8i4glPFKU+hjkOC2s37RqnTbfNzgWQZUpqiha/U\nGo9oesggcEw9/rWb0ox3laiUnSk8YCjfQlcGs+zp5SW6Qao4hdCpmp1CdsFq\nOh/jTJc2ni3YIbMfyK/OcrWp/9oWsRRwbzUpBOb7I0VM1pyII1UdL2R5e7w5\nVgl1KWfvf5lpmEG5M4L8Fkm66bQCAmlhB8id6AQRZkCdJDt7Gh/i2S1Xopd3\nUp9MOdh2o4s7RKZZpGfYzqQ1p9bA/IDu4gNBJvytNsEhZhWc4xr+Mx046+Zc\nTE6vFAbggC5yYvr1CU4j6N+HUu/MCrnr74ezqVIsg60Zoa5sxEkYJjPOFcZJ\nxEzs\r\n=C352\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDkCfA78Nd5ngwH8/B6XWpERYj7NaSjYLJQnlzvnP9p6AIgQpkDT6S/s0AWuzMw7qxhmH/6yG+QEALqGXEczwrjAN4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909203432_1536525457199_0.1154673457476969"},"_hasShrinkwrap":false},"1.0.0-master.20180909205905":{"name":"@atomist/sdm","version":"1.0.0-master.20180909205905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"017da01881a66d6fc8876befa365128980f7a521","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909205905","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tMBUC4TId3NYyNLESRSZ3X08xLjYbr7xCO78sZtR+yYZdJBzLaVlw9krZy2h5RYg8G1u/DwZbo3BEllcVR9o8Q==","shasum":"c263b28c5cbc7ec5cea342cfce65d37c4fdc1247","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909205905.tgz","fileCount":919,"unpackedSize":1908527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblYrbCRA9TVsSAnZWagAAP1oP/R5cy2f8dmf4hNmJKAcV\nQCDDxTEUJKBLUwOb5laQGCRVY2zhOrqAs356IoeJJmAWRlul9udXneY28r8/\nC937I7WJyRdD8Og/QWRUkgHIFasrZ7cXZTeQ8bFaDUkn8VErgymVd8ixIjGE\nU2ubao9mUsQ8X4cF6hO3S3FQMcYhftwX3PHD9Hv1F4HISlf9hdmJYQ+IN2Is\nkBaD9BVxLBD+YuDrmnexq1XOKZqVSToKn3pTg4ameLaK5oeBOXg+ANWWRJUh\nMIpyrLe6C6eyqVw4s+XGdels9BUibt6LzvkvzF+6BFGVJWoAUeol39VOyH5h\nSIrQ0F8YYra/SFxKaAG9eumUjxwlxvlTOhm4V2hqfb3vZsA3VbRLeWGcZiXx\nyfVnZ83sVzo4MclEUGlh9c+d7LlV8+NYS/1FfbU+OEjLyVvCy2XlpqwntsJ1\nfnNSlvuJoWPAZEr+S8mCjv0pEG5DVV9w4cVr9WSo1evIuDJtwSjgSYJ2h9jH\nSEkQxadX7PSBTuWmGrlE/0RWiKFfkmABqsXW9uL2qUYeNbgS6BEZy+PBE3Ok\nsPdHSwc89NTqWJs5kaRXR0b/AmxdRF05Jr+Ha77rLDescgjxHqXyNCPphwgE\nYoHYOmQZXIwSbZGtVqvg4MyxgQmXzSK1qlYoYUaiSzSbidzBJwmRY91kMCNE\n3y8+\r\n=OLXP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFqXa204tJMIohnNi+xlWP/1ZY7Vv8z4F90Ho5yqHShgIhAP60eJQyJfoh7RVgG6jjjQ7VKDOzBh2ROlfkg2YNuExe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909205905_1536527066881_0.8426181328584526"},"_hasShrinkwrap":false},"1.0.0-master.20180909210749":{"name":"@atomist/sdm","version":"1.0.0-master.20180909210749","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"36d6d0869268b298456302d361d6838990a096c4","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180909210749","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-b1cF3RK9BoojpikqZ5uMfW09em6E8cNPYtESBgfkI9NCYl8p4qhD9fbAKqmmnHzsNd4M6yWOrgZIMz57h/0jgQ==","shasum":"aa319df8f1c540656e1eb954ff9006fcfafeb04e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180909210749.tgz","fileCount":919,"unpackedSize":1908480,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblYxqCRA9TVsSAnZWagAAzukP/0F7+wAhvKTsaSQMHmL7\n71dzPgNs5fimsyXhW/dR15IXYBoFFJ8Z43zKqQnd6r1drM1zP1ZJIIBSgcS5\nf2boezhBXUGyysbuKITHhUt9RX/XEX0TxEf/T8h6WT+98HsGaOmEKvD8Lusn\n+itBauPuT4q6J/ZvBNTJ1iqSGMdKcmNO8+f3AnpHrTFkD26CoYxEx+oXDFQu\nWfKidMHSeub9oBZFW29Mzh159C1bVABKKVL5xXs2aLzsAJ1JTJGC1SXCBkck\naVPVtNvfn01Mdjz0Ta5gEgUkorPvTud7hdUpyhi23AN0gOtWYbbAO5aj6Rd3\n2pOKer/z9fwLALLYZLjErS4bg0KZNNgG8b0B3MBTDjqDWpwLwf8t9thkD3tt\nS2EPNd53B1nxpL8c+HgF96CYlqmAQzQplbCIFcD1l3p0kfpeQozFtfdzOAtq\nZoM4Vuy/mDBGpm61EGN3O3ODLN5WVHHq/iZA82XZ91cT/gV2jOSVh/6zs/5j\nkgkFutRsOfxxc1X62Cj21GvuNkJZFRD0hum1g2ciNWe6m8owniNHMbo00TYb\nOp04ZhfMX1xYRya6IpS5NJcgECLChbVM7dD/9DCh45m3aPHF4/ETHF4x6wsK\nGqh1ufnzC5TWFCWvgdKNaGHeawEVNMWWs0RnBr67HQkSxXmQoIPQedY1SZMf\nKHZQ\r\n=5hk5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0Vp4A+jxsfXe6iiP40Ih43VAUzvT3wVHOgr/gcggqQQIhAJNwBsjaWtwJgkFLjMpsLHLiJCP5Gi6a+NjdgWq24w1W"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180909210749_1536527465204_0.8640625407927651"},"_hasShrinkwrap":false},"1.0.0-deprecate-global.20180910065534":{"name":"@atomist/sdm","version":"1.0.0-deprecate-global.20180910065534","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b4a90fe36c99ee86a3afc758fba149b6a4bf377f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-deprecate-global.20180910065534","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-y0lp1Rt5JCqh09ICc6kCxQqVeQb9OyJwmY51i44N0DKHdcyzm84Xnf+1EDGOorLc9auhFA00VCYRC5oc9On7BQ==","shasum":"3f98d3da7033e2c5a618fbfaa099a4b6fc99aa1e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-deprecate-global.20180910065534.tgz","fileCount":915,"unpackedSize":1899671,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblhYCCRA9TVsSAnZWagAA9uUP/RQ6+SPaNqkW991wnyjz\nFmWchKhyqpGfAeGWj8HQPqr1WOEch6g9cXkWGf28UyFNLyooPF6BgmppvfGa\n9JR60gxr2kLfj3t/UscVXOJsoRXtU0rq+SL40OTRyWFVkNPQchOAUdw13xSP\nuRO9d5Tq4ZgiXy+gBWFWWs4TE4BDxHyDeHTBn27KdPZMevbhm9Ptom2SV8pS\nNii5anL0dcvblamLP1fL5E386X/4wpf+frZqOOn0ORPZLHgnMoSJHxfmdwp2\nfEW62sHZ3MFJ138WO5avLTXZxL6RZpnn5ztZIFFvx66kCD9L5yyO/G+gWbZP\nvec1RZmfZr/qkAlTeG01JrcXmq3wM0KQUt5fg4wYz80F0H8STVlZme5XbH2r\ntLr57LZc6bVyZZg+K+HZUbPHe8ETew7aBwOtPDUBZT1CyECuJNdFZaw918e/\nfpm2bdd61K3sV52+00OtWInfctEcU2DtYeU+EHdQ0Cj7LQwVFI64UfMNinbf\nC+jeY1U8fWA5/S5tc9UGzbGrYaKcDexiPkV9JB8/ZAb2TGYOdt0Zo4ImYL1V\nPAf3e0lKkkurHLIKD0Tv7Dtk/s0f51NRVmp1/bFedLSlAdLvCiOv2AntNBlD\nem2veWkV14fTI7Oaqthv6YG6GW5bJqlpMLxHFDujGWugU82aD9h49dw+qZ3L\nkzgn\r\n=sx+U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD+QqISjDV8rqmschAcK1XPrNXnDtkgsGH0Ht7AaD8+zAIhANFxHiXgxR5La3pF9/GkQ4vIj4J3tkP7dnxrZO9xbIyj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-deprecate-global.20180910065534_1536562689370_0.3947032728716313"},"_hasShrinkwrap":false},"1.0.0-master.20180910070004":{"name":"@atomist/sdm","version":"1.0.0-master.20180910070004","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"879076150d4f1277d8c011884158e255d4c5d159","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180910070004","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a7mU+KNFXUN8tdOp4AAkX/crnOs3hOd16Ge08UN1ORSgdJaDM01iHWApKADqdVh4BRcVvdBTF6GtCX3RGU3aXw==","shasum":"bd2a7ce77454872fda7a4eea5bc4a45a86c7d941","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180910070004.tgz","fileCount":915,"unpackedSize":1899661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblhc/CRA9TVsSAnZWagAA+U8P/0cRyzizqQuGGVxeYd2W\n20quMhc0IYQY7SlRUX78drfUkjqUgz60zMNFfMwOQZ9RLi5bsqkqI52cBeSM\nceHAK8Sz5jMeVB9400eNiZJErTnNRtr93eYErB7kFtDwFt9QAbpnlY44WCKa\nUhtBUTv5cvZ52LarBkLQmi2vn7AxvP0FEogXkrNfzRBnf8QiglUc+0DCtiAz\nTFMSUKTiVTURJD6KspNSIuLaDwySpFoE2g0D2JvoRdlVI2PUVY20FMVKZtxJ\n0IeaQZmgNqrCntbx6WKX3Te8fZUrcyEIuLaag8Dym6Ur5Z8EsyEOVyjRjNg0\nTZ76d0mftKquE0IYmiqblHZi03jXjTyQ3tTPV3d2rxfhsHMXal08l7BfA5vP\nmQWhF3QT1wWFkMQfuuqDT3xHn41jpexgJ2e8tVXZLIy5uvByU7ey7nd1FfrA\nxjNW9+oh6VpKbHwVvqruZFohCqVxF0TPnyesdVyzjlicru850CIioZRZcIAo\nj0AnXqOAQzT1a39tLY5of/plSimDsNkm8DOwm3baYfdqSW9VaspEhhvjl2Ge\ngi2zVb4Tv4qeZJzlUZwiqOuSCycc1oflCu7Su+sUtVh+8LzmPvzA7DZRk+n9\nNC8CwOqh7BANT13NbMVwXlSZa0jOJrXQbVRf8Wbkgz0qYc3AbQ1QNuYTHdsI\nJB6m\r\n=0AIE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFyG7PJtaxiQfp0Zc8fIaqIXdPJNyGP6W0iV0d6Cz5RwAiBl0q5Rzy8Efbs34Xk0MDVNNzOAw5cyez9bp4MwWazf6w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180910070004_1536563007028_0.30266843287926015"},"_hasShrinkwrap":false},"1.0.0-clone-options.20180910185946":{"name":"@atomist/sdm","version":"1.0.0-clone-options.20180910185946","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"bb88bfa5892617cbb862ff2eb22aa0206a4d69b3","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-clone-options.20180910185946","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YghtDk2pdJi1DkUK9wNKkeTemaLVKBT4CYiXeBbEU4MVibg1YzIG/ASqCQXdcgscCoEEbFIvKQSbSGrSBMW4fQ==","shasum":"1cca63e92fb6c71bfe02034535650f1458437f01","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-clone-options.20180910185946.tgz","fileCount":915,"unpackedSize":1900445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblr/TCRA9TVsSAnZWagAAIA0P/RF3e4aYO46keGWO49+9\nH9tFiB1apCQVUuRzIJIivvJaY9HGhqucJvQehwRNi0JeEdbsX55xKT0csif5\nC+ZbmIfhlaV33SIM7tMNj2EZDH7/iZ/aa/4Rg8GUH6+z8ShmjMmp1o3M784K\nkgTW/uz2UZ6emgcKJWVqo4oB4wWUcFBnrWdF+BQhaHXASGU/889ksbcfnbw5\nPN4JV/4ZNkv62mdwdjQg0d/mhTXKvKaAT9tAaaY+IOeFkZeymZqbGuS6cwLh\nYbW6jSA5u+le2WQMwD9+6yoA+sCYlhkauyPvxy5bEQtmo4UCMGbCbPS51Qle\nOeQxL6S1uK4pMAY6mu89wJlPd7uR0dYVVqWUKUth/pkzMaeh+oA7Ts99elTC\njBFHlUJMS7NODoFsYVoDU3G6/uQv/CmhI5+sSUagtsShoHR7+aHnKQZ/imQn\nlGn8KbrQq3xdfr6P7jEHlR21MtEGHiN3ZlPByEzW4SJflA/9jISOyPQl8U5s\nJv5oI5zuGXWQa2mN/3T6awnlNgUPiHukQ/l3Gx17Z3g6qgozt5rW5mtjKcyy\nhrpnTEAIiRE5qX3wb2ItJKSAnvudOI/hycn2FRz6iKTL594WEGKMIqliN8oY\n/tUn9nrKx9ppAok2Yh3yV0yJwcTe1bFXz2s2u8XnDIyhOOfb1/blPWmolSgd\nfXJb\r\n=92So\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPs0FFK4kNrTyqdhiTRz1u0yK/QDttXGT4Xmod/W12ywIgJ1XRnk/RIIzNsSSjAgMPARBgqBzJGzXd0v6SnhXnGnw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-clone-options.20180910185946_1536606162872_0.2884606153082645"},"_hasShrinkwrap":false},"1.0.0-nortissej.autofix-warning.20180910203230":{"name":"@atomist/sdm","version":"1.0.0-nortissej.autofix-warning.20180910203230","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"27b57be0fef6fd3cf8f9eb0387d8d588350ef11f","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.autofix-warning.20180910203230","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-baOW7pRFNX5r0kkrxGP+RZoZMoC8K5asd+5MDlet35UX37J9SrMK7U/0g+DL2WDphpmDalqvVkDEl3bi/9H2BQ==","shasum":"d1000ce4e1ed3100b214ad07ffa2a873e3e55da4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.autofix-warning.20180910203230.tgz","fileCount":915,"unpackedSize":1900539,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbltWPCRA9TVsSAnZWagAATZUP/RBdY9opD3kznyj8aCv/\nTBEFX19xchV03qHMvsaQxmtUNKXaQtNdYG8AHuW6kU0E1YYu1dt+h1pECaaE\nMUVB26am/xwdjA7tBoxELs0EElUPyZhl2yc126pYaITrYdFvKwuASzR6EHpp\nLvcRODPf8uk1WBBOYMbomtKK2BJtC2AhlRCrOQ+5GlqPU9tWi0EMTXam//Rv\n6F5mFwBaB6kGRJkSEx2EIetJcDn4joR2Pyq7fX5yLZ0enUbLjfmeoO7ZBXEr\nEMFSxhxXg64Oh6EYqn6oe8Td8wAMUBgwptMGEVuTF/ArEp0LpEtFnQJobBq8\nVG2d236EDWz7FxXLkkVD+t+idYNiFR0TomWT7Qf6ixi6clHJm6ApubXmFCNf\nxwj1D8RsGiqLHLafTi6pPL3e3fhadL9BqCUfPzCgtRF0O6zg/GFnik/M8GfU\nrEXUTTEtj/piegkF6wYAp8l5rZYvzhzL1SJltbphKQgibJIdNYksqGx+/S8p\nj2uP7/hfXOZ3OLjXRVnbjsNKj6YuYoquPRM98ASHfwj9Tv9T3ql09N8E/Olo\n7BkgF2y0gb9UCzHK3h/Zry5xO26IwKNQs6+8uiUwIW778Eyk65c/8bGGfrh4\nsP7pwjtUf+MPjnFWXm0dSXCvrnyAEnElGY8i7bDdXmkCRRUj+zDd6fe8FdZj\n7S+l\r\n=f6d+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCq5QJ01O4FO2isSw8m918BUAmAYvoIbT9kRl1YzN9EhwIhALfi+Z8FvC81gKt/6Bxzt1+TEyBIt1YalIedyBxIjeQA"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.autofix-warning.20180910203230_1536611727171_0.7512796600502059"},"_hasShrinkwrap":false},"1.0.0-nortissej.autofix-warning.20180910210323":{"name":"@atomist/sdm","version":"1.0.0-nortissej.autofix-warning.20180910210323","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"21bc73570b87cf62bcb60e341bc225a11370837a","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.autofix-warning.20180910210323","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-iNEUhvV1T4vO65dHf0oknSLfJrc0x5TpaTe4D7zwrOsBtxFuCkexl7RkS9M6Ul9jWFwg1zQBXPMunk79VLfziQ==","shasum":"23b9f892a0db3ce318c578758ea0ed9da3895a84","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.autofix-warning.20180910210323.tgz","fileCount":915,"unpackedSize":1900549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbltzACRA9TVsSAnZWagAArdUP/RPXBxefIAcQgF3RQIIQ\nBTZxd7IyeHg/rueCKGvZkK4RZ1W5sMjNthW5QLC1q56kDaGzMrUJ7rFe3K/1\n/sx7UrrutGskRQE7wvZfs6NQnJREs7g10hZT3lxCy/D7DKU3e09AGQqjlQio\n59xBC7wCbzwG3HS1o0evPfpC3T/I7+T2JHLlU74hjwUB4d4qIoBaCBLqK0DW\nTzVal0gXPAWuleZEXCrvMC84/bAUEXIGGG/hVX+Deajxj4odXGXdLce8hryl\n1nrpJRtXCA9cKJZyI76p85GIo9V7Uq5HLjwZviI99mahs/Lu7VfKKxJAOQHU\nUZ+FfG+cvpRz+diXkUYWPEac8hNL4NW8ZuATvSqYWuhKhVYuNSmDFo2wEbWy\n5o0BDe1YvUKOV39Y+SaOrTCfFqCFZhkksEJvUhEdaFoufkE6yX3YiI0auO0p\nqVIyzaA546Y0PMu4eIb4niQ7tKH/PJ1TXVBrlk71iNr0gfwDkqTLUoa5LJSx\ng8kSmb8z7uEHDmmbRQmvmKpNInfeB7ybnA0a0fPz248lzTZlUr6t9tJJitZT\nHMgwSgYlEyOI+5GHavqlx3Yf6QsfV6geMKLDegdjiec9j4AQo/UjDjRKvYDr\nNHouLRUbCTB/RA0ZPi1a2E/MhlmpbSOR9OYzQnyfc/EwP1E3WgC8SeiRRf2K\nTu/0\r\n=BFdW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHxZTxpUVWptcJKkD3v9immOe1sHU1kp1MEvspPUWuKbAiEAgdUB5epxA1GoqEzUxIrI+B1rfQIa0tmvWfO2HS31cco="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.autofix-warning.20180910210323_1536613567914_0.8222230554507344"},"_hasShrinkwrap":false},"1.0.0-master.20180910213210":{"name":"@atomist/sdm","version":"1.0.0-master.20180910213210","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"452aba0c30541513091e3bf8304b466913e25ae5","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180910213210","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vkk729+3Y8KBcysaJU6ino9ttd5NEkb6jBx+AHjUjI0x51PU1IToUfKi9XIWpdLEYyuxbUw9QyAb8HCYiDgnsw==","shasum":"8838494382611b4b14e080a77912698585ae1d8c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180910213210.tgz","fileCount":915,"unpackedSize":1899946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbluOKCRA9TVsSAnZWagAAGxIP/iZjSHGTbe3PrZ5stp+i\n/vtmwo3U55ulS/tQ1xlaFVPbK5cLYM5Lds0dAbCJS1wcndD2jyMBsWSmpRYZ\nXdK+KH8GI7yqKFAmB8sW94wLMrFFkT0ugXS2Sh4L2LlXwCWLfzbqXhXRHIQ/\nYjrOG72iCTY81tBK8Wlg0tEiSz6ddup6SN6EdMRDQkSviZ7h/VmLpczhHS1S\nKqev125WthJEHJpiC4MFSdwER0VguQ2bgKblbS4owv7AfJEcLDGUsrFF5dd9\nRRB381RW2L3wUeOgZIHTrVqGouVJ2ZrOJR+E8XdmUIH8zPqkfnticxQdxlKq\nKQpNg7ruheNVEC5/YR7Uhne4Z9iY+O7P0ib5SV3hSSLm1NTTlDn4u1NP/MRF\nW8Cjx6612U1Kcpl/o6zoso4asIfjpDydCCtE92YTzcsa/fD/W4tifn7cH7bR\nvh9XeEck4ZNohGmMoyGZc5TG/AXJ6FKbVW3WZCUUe+ZpcOeUKLGMr1VeTkPv\nVxWU7ifSA59nulcjMEy374avcvedAZDDve06JDltZvOBx5TW4L7H/7N33YHe\nZTCJq7/qKzdCkYvNsSnJo8YeToL3T3WFvYxST7gDlPJFJn8g19oFS9rLQMSb\nteQAcJgN3+tD1WzYSrZpAeWUPDVPAOp40jzIkshQycQ5w6qJWp1/QqBeewdT\n0A1W\r\n=2VUN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEMQnnssHoPQxatVsNxCP+W4hivbFl77Vilr+9SaOYCDAiB947NW0b/CdlIMaIoQonLw4/1ZdXNYQ3ESkTVdsKOvnA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180910213210_1536615305824_0.6025343269552019"},"_hasShrinkwrap":false},"1.0.0-master.20180911004414":{"name":"@atomist/sdm","version":"1.0.0-master.20180911004414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180907111850","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"8dc76dd188a4c4cff6ff1a2a36099e5ea6d21974","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-master.20180911004414","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-q7Yd7O5zkgmxo1BJK+9wrgd6W+Jd8UhcgbuDUJIY/MZNBxpfHE/7IZDm+YHv+5R+nmWkVB7dq70bXrLR+g8yEA==","shasum":"be8eaa560868323ae3d2d3509e882d341cae48d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180911004414.tgz","fileCount":915,"unpackedSize":1901233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJblxCWCRA9TVsSAnZWagAAB/UP/jvyWNj5X/pIvIKG/Hek\nacncJVGXrkStdmTTLGIIrEfGK6d1WYxNQwf3QdBqzjhVXHbXx5g0dN5kAOs6\n+1moCjaF7cmhfp7WrDjnna4OC/ksrFO6E4l9NL/cxS6TYAT7e1yW3xW/is9H\nUmB69+0lJJI1uEXPnUDALiA+7A+j04rxtuVzwX6a8XWEgi8odnidukiDv2GD\nqvOjFlqa1kAvZ0k9ZAx1kQIYSiNx+Vja5pXC82wvW0h3Kevi4+UFLipSIiS7\nbfF8krWUfAyPCe/Q8RUMhU3gNQ+fG6IcOIE1EPoLAbAQwa+Vlcf+txQDsF2d\n3265/RjCPWDUiLunVnmgMcttKW0EdtxAeMgTI1T4utE657VDa61LJGgLAMg6\n0vOh9o6W5v98xjllqZnTbbpftDMYLi+Uuubfr1kAMdpZEYUec2NPfPizt9SV\niXs4WhMtHjMoPIJKo1EnR5xQufmHK/7Il5zdVo84MVdb1K002UGJYEqX4Kuh\nXeHYBvAEv53gug6laWcqtTDJ455CtYQC4aJv3+MtR2knZDwpYtj57wCsS4dw\nhJU6iazl75j6enaCOh51kJu13S6N8orVNlUPwxrI2WfQcL/wDMYL4bhm+BYS\ncT7X0YF+dh2u8C2/JW9vHpuzCEACrPalUWcW1ojQIUpqL5Q5C4g4NCgGyWiO\nPFzF\r\n=Ld/3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVAcJOoO6QOWttX9cSaMkzb0/gQbM6fpXzK+vDdIOWKAIgH9uSEwfVGNzlRC/jAEh8nZXB6Y5BSdOLCWX7rLKAnhw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180911004414_1536626837720_0.9876007806698583"},"_hasShrinkwrap":false},"1.0.0-nortissej.checkout-branch.20180911070300":{"name":"@atomist/sdm","version":"1.0.0-nortissej.checkout-branch.20180911070300","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-nortissej.checkout-branch.20180911043314","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"b23142dee9d38baf388f6de2e836e6a0c096e9f0","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.checkout-branch.20180911070300","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DjjFtBeFy/xvccAsw05amnrbYN07yGHGd2TKVvsJxzrHUL0pMqJcT1xPcfeyX2We0CW8P5bxB5HaQreBckwqFg==","shasum":"fd5159a4a53de7d7332ddc2f61c09b6c5cea970b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.checkout-branch.20180911070300.tgz","fileCount":919,"unpackedSize":1903014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbl2meCRA9TVsSAnZWagAAGT0QAIn0+V++8bfkXqZoq+6L\nY/PB9VSTgeiN0KgshgOh+LLbYQnW/OH8TnwWvy7a4Ua8Nev8y/RFqwM4OPIw\nnEhuAwhlP5t5Y+uSW/jCRNE4u9xWKbQZ/WdYD5aDx+KTZaU4w3E9lML3Xwm8\nkKTWpB1Y9p7G6RyPcAut7ofpjYxX2+jX14SyJ0kIJFxJ1scSxLZ1JlmoALpk\nQInBHlFvfqIkyTtZEMB7Xk+blLyxY7orKx4nkQq29NYEAjsAUzD6FLKs2l1M\n+82ckDl5UhjGoTGR4Rg9wnoHdF9zDf1FOT0Uffrlv+pmcS+jJsD720vcVwOp\nC6ir0yj3D4hUHMyXTWsto28J98xKTk8JJzlv57i0+2OEgWIaSYTdAPyg/aBd\npWDaz33lGA8fcRY9IvZf6w2rpyDdbz+TLCoBPfUMDGIaQhM6HDyXZcmM1oad\n+5pLudlpxYPUtfRQ1xITS8FTrRvdCd0/AdSzO1sqQuBfEeYkgVEOrs2M2Z5G\nQ9P8yB41uIw5Hz6dz4DnbhSPiU5EKsbs2Zk9Aw9Wf6RL0WNvZFj/AMv6BABh\nxq0/UpiFmI2mKVWN1aGmYExW+VAhX71qWpJy1XXE9gtMV3TrtBoBhiMbKDIk\n/imhAtNBwWdlwSEyruDl88pe/uizdAdwN/BARrAwZmhBttmvcJnwwbSEfUQ2\nDx6A\r\n=XEAC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHuI9DU/C7Il25vKrYxghuwN3HrG+2OateJXx9eqqXaFAiEAvdrpxMshUNDJ8Qkf1pzfP1FHOQ3rghE5FxXCJYQGEio="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.checkout-branch.20180911070300_1536649629482_0.43275539364529236"},"_hasShrinkwrap":false},"1.0.0-nortissej.checkout-branch.20180911074648":{"name":"@atomist/sdm","version":"1.0.0-nortissej.checkout-branch.20180911074648","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-nortissej.checkout-branch.20180911051036","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"gitHead":"74ce3d42c35689ed1de05f157db19743af112ed1","readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.0.0-nortissej.checkout-branch.20180911074648","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VF19+lVUkuRAARhER2e0YlbmiDjt9HkhFfmM74phDijteJCz3o3Pd0C9aZhjMRGDLB+b0sknr+3NSaqUnKOs/Q==","shasum":"ab546592fe8f760b5122558e5a9f3107e9d0190c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.checkout-branch.20180911074648.tgz","fileCount":919,"unpackedSize":1902939,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbl3uZCRA9TVsSAnZWagAADgQP/irRbjClIGR0oCmAp5yY\nVAo3svLKDMMtc4igjv/yq9SIGe8PesZKWjmBRMuGtpSBpxK1vpm3d5jhNudo\nzGK9/mXpJUVs2nc3iE/AdtYN2HWLO+SfUizIwmhabvgt+FCjuzziebSPQgXC\nRTgmukiZ35xM1eFjaQ3+i9OgKsZBCHFbyhP+v9lY0VQRG91B873uYsZMT1Sh\n8ojanUjNx5nYGgaYpi34NhEJzMGphOaQIJxLUbpFEaHDyaBIguR8E0VyfjJ0\nN2lgkOpOeG4uJPZ3Ai/e+S6C/BPYIdlqEVqp7AziLdYOm7/XCBpg6bISwVWY\n4+u+DucmynEJsq4J4RHfuJXHSj1DtPKosvTpaVtuPWRmMcxRC4N+xP5oBIXZ\nrJGqCebK39rHz8c80ba/80G8bsgCHmZWVNRTmcOzPzPAc6WHta2X+VyAZzdo\nV/DHMV6j/ZN3vKs7LTnrAvd6Er7kchQOHhxWIIjbn9EtkQXbCOhhFSSMPBs4\n69uQbUqWc74afVI3v0LrJ+lavs6VtP+Nz/v6TAuUKYlNAodRkZbIQMnVZH3a\nKOxr8IDCP6V1VaSRf4vRPw/hZuUw79iZkMabNDgLsexf9V3smeRcFaBdHVK3\nPp6zPTww7O3OF4q8G3Z1tje9R/AurIhbToCn5SaQF5vwlVGQ0yY7Dmgaf7dN\nmlvJ\r\n=ogaa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAeiUsZxfzZeXqeBsLEngweISLmaZWPPEh3KS0YamifeAiEAmmlaaJVI2/3RRj1HzH/hPgc8S+xS6cDb3Pi2Ny4Etq4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.checkout-branch.20180911074648_1536654232701_0.06275834037764527"},"_hasShrinkwrap":false},"1.0.0-nortissej.checkout-branch.20180911091518":{"name":"@atomist/sdm","version":"1.0.0-nortissej.checkout-branch.20180911091518","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180911085654","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1fc1a717ce2af127babdb4970c2c5b0c0ec63b1e","_id":"@atomist/sdm@1.0.0-nortissej.checkout-branch.20180911091518","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z5tRq0+ul8BqdZgWPuYp6GbwPYFMomVsbGeR2PkuX6yVgGaalih1hRiXmohS37AHrJeKgA9LPnx/dApOHon3fA==","shasum":"2c73e60a201cf2eb0aa9b1ac21e1647097028791","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.checkout-branch.20180911091518.tgz","fileCount":919,"unpackedSize":1903433,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbl4qiCRA9TVsSAnZWagAACgMP/jnFe9cExpWO1fP+bxrb\nQi1Tv2dOZn+mpHJSoSh7DkDJeyMyApurIEd1rHuQW41hmb45OkyGx8nTWHDX\nAQawnbEmv3ANnyLUD2d/F5UvM4FtsWpahgmiSl4mb1MMmbGZ4qyjCkQPxzPv\ntKWQkdQCN0SH2atPQbwbzo4eMT3ZJaA+dMg5orMtUg//DUHC2pJ8Lf0hbT9e\nIxZSw+vsT1AJO0CPLOZ8rcvhQXh1vkK1aQk4gdRvt+1TEbkNCUfAozjZpDZO\nDZM7vggKrl1GWA3obP17WUP3SFhdAfcM/Xnb5uuBAj38Kd4/6owHf7Yhe3wh\nmmSo8rAlM1obY3TQg2FX8t+Gd7jH8VXD0av7vMY7LPxE3u6SQQFhO343bvQQ\nh16FB0YLeFFtC6KoI/y1fzi+59epUg4V6lS1ur6lcLDDdK2YY/Bigywz0UOE\n18umcR/x6TbzcgV1NS5k5uGJDNfvyre4TpChlwH1xYvGR5YPwK+qXy+j9//c\ns6tamF7fdM1QDok5WKm7cZoQpPcXtXRSqqbgaxyV7cys0LsioKKZpx2idnLz\nBeOpdsHC/kUGKe5+AfEzHwNf+02mrbpNJn26/FkROtMhpf+KuJHLgGq8HldR\nGtYUCNnr/4L+Byevpn2OkgWxsTNGDXHRdUjDBVFbNOJW45OpdNvIJS/+JP2N\nTbrt\r\n=1hl3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCf1BpCpqDYeje5dbkdr80XeA10bI2UCAwFxxTn+zomdAIhALVl/9Glsg9aPS4dyvhuwVEJoDGiKVZ4p2rvsKwOMlDI"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.checkout-branch.20180911091518_1536658081393_0.536730975515537"},"_hasShrinkwrap":false},"1.0.0-nortissej.checkout-branch.20180911093258":{"name":"@atomist/sdm","version":"1.0.0-nortissej.checkout-branch.20180911093258","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180911085654","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd098e27eb9b33b35aed6522eb46b298b689df09","_id":"@atomist/sdm@1.0.0-nortissej.checkout-branch.20180911093258","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3dmBljWf5t5qirtHA9cy4XxFoy313U5mljsBy97l8JRSi3TqonPOMMcKfmub+0YqgmIt1vpXa/3TLcIxwldqTw==","shasum":"0450837edf641768f3e91b3130298dec1869af75","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.checkout-branch.20180911093258.tgz","fileCount":919,"unpackedSize":1905159,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbl44ECRA9TVsSAnZWagAATnoP/0osh0apuf2p1FigQypj\neEWMdeHVQvQGCuTh3Jl7zhxJ7nrD0Ew88Z6MRboKVxe0Lvg20qI1vlgYTSBD\ngXBEy7Ct9X8jHJrr8A790eQ9zSb55Hb4etMOqGNqk8b40hyDZWpOQecVh6RE\nfbi5UFLblPPXOqXhAg0ObxiDAZMf+AaWzzHS2igKMnuL6fGP50t3k9ebxKLz\n1bz2Cdrcqv5FtqQchvpJCOTVBFsuKAIvo5Mh1c0fOwqOBe1ZH44TtEB5VAbC\nwjiXAfcSliU2z1s068KVW/rOG0M8ZFcbn5K+GgQyihIqu68anmhNb+j1L/Yj\n/pRZtlEN/w5l35ws9d2C6Hszf9NzD6DiPwWQjbbkJiPHxvDAvgGCYffhrQ/O\nv5sEe4V42CNnlZdnnPOKCXpsmc9PcyvmdZSqLrRRwtX3gxhMqIzV9w0M805R\nxYh4PZ20VQdCn5bREBvFUKrgvT7mh96Uj4B8wt3FQ7kbj+8rxp6qSUN4MtEa\nr89cb8ORA//vfH0qBOkB0Q9//ulTgvc29YbWnr3NN+brpmpn8VSVYFnnJ44R\nTh/0L/qZCu104cob3Cg6mgdPWDyp4TW9U7Qc6gedjY5sHwg6bw48LazFEAAe\nqsv71BZXqexZCitN4VhdIYpx2PyLezW/A7s8CEbPwlB9TpRyTZ6l8b8Ao0GE\ngNBP\r\n=Ky+t\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDPs8dhNCnNQBPHBlY+TqvQJ0tl1kfjM1yF7gu0EioFrAiAlyuB++5YkanrXD+eybW1CS/spF8IwjeaWXU3TnkmrCw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.checkout-branch.20180911093258_1536658947648_0.5445259725171467"},"_hasShrinkwrap":false},"1.0.0-master.20180911102033":{"name":"@atomist/sdm","version":"1.0.0-master.20180911102033","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180911085654","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9918d2b90695f5157a8a3af6752d948fc4eb36ab","_id":"@atomist/sdm@1.0.0-master.20180911102033","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HbydBtNa3p1aZi/D9ZBUJtIdHSwP1rTZCKKBAlGxjWREYluztSAnSPT+xxBIxS6oPVbpJgft3qxcW8adbp1PEQ==","shasum":"cd2c1617c9a901024cab17c602a44f49d3b626f3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180911102033.tgz","fileCount":919,"unpackedSize":1905254,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbl5fHCRA9TVsSAnZWagAA9zoP/3ABZpNrLR5uSoidEmVJ\nOttPZUk9MJG8dENW1NWibracJ9ZYeVQcBQlw6QWmlrReFNgHtor8fiMtSrOg\nQJ2PqjrjyPCT2cI29AELTKz27pklVKSISX3pkqUJ1cQtFDajzATiuX+AfNBt\nnvZqvd2l27r/HmmnlJs3RCotnL+i5nmWYpoV4IMagTHmaos/IIJ+zvE0/kj7\nrSHEYw66LeD+hgtlfFCYnxe9SRtocUOTI6+Q3LtqLDr0huZ69Bydu9GFvwof\nDMJ/EmopPOAzZcOcX+CMGtoSMyJ2LncQLmBtve3mG9U/BLbf1SIkGaec0/IK\nrAfWmjxNF1uWqbhDOrwBa157jBCvVuhJDHhcmscwcpxlp5wIafDFTYnciMA2\n9IEVpg4L36AKrM6dzu+/mfKsr5IJV/ixgyL0wYMyt7U8/2LelKqdIJ1M5PEO\n9AmGpK4BHH+jIhdyo9sn1iQqdiPMgdJYHq3W8Ml+PW7gPQp8nOd3Zxiuf0Um\nQuG723jT83eb4dMlDrIQaJbeMAW9U6u8eVTjmZOv2ukG4d1r2MPNsQ+ECwJX\n/M6iRe+1oXLzQbCjv8jYjv7mveXcnkFhZmvwx/DqcI+Q+N9/vzEcRT19Jm4w\nf14rESRNypPvoiyuVHAU+gnyzhnEMsZr8BLA1vRI0T0Ph38hPnkVVOBxe150\n/dVU\r\n=sAjo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpk9yFMWxqXR2jZxqsT6a1cYjTC5BShT9Ski2BJb2+9wIgG5mlmJmbupVH0ZjwMq0bWfESO/VCPKIsY6Szl3nuBac="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180911102033_1536661447235_0.13906928076438274"},"_hasShrinkwrap":false},"1.0.0-master.20180913074630":{"name":"@atomist/sdm","version":"1.0.0-master.20180913074630","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3471196485","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"08f31f22c0a11900cb9cb5d46694586c18c26580","_id":"@atomist/sdm@1.0.0-master.20180913074630","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wRyC+2sfeo5jlwBwJDJPGIYEGcFDe7tuW+174l6PiIxfchLVmoeSuH8FSzKj+bevQcqt7kbJBV4Hhv+z977wpw==","shasum":"e83f13d40debe41b3c7a57da10466ee48ab26a53","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180913074630.tgz","fileCount":919,"unpackedSize":1907021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmha9CRA9TVsSAnZWagAAQpAP/jk01Fann/9bU2iD1ccQ\n6eYscjLijmze8v0EZ/PxETBqLrVgxeCJe20in75NTDw0MSvbmuCNHFGG2DCV\nr1M6GfohGtFAexxsD3AZl+27Mw5vyMk0rhROG1SWX85dhkkcEI+qbfyIDIFb\n9qUtFCgvwZU/WZe5BO9UEhqIskJQYN1l51gIfeK3SwCE9wiZF1lIU14NLpuA\nJjsyMj5ur0THDX2rCRCSyNfV064JQhzOdQVPGr6u0uaiGhVF1NjQqMTyuFm9\n5T8dL8/r26NEYHgw9C9HFAWn+dE+HxakkhJKsg1DLttfVLoKT/aiZlLsDACu\nYM6iq63S2TfINj7/v2rbdue6U1O1BJgrK222ZTY3XznSgvw0pOF3tyRChA2Z\nvugu0qRJy+yNqfZviP0iWfcB5g3JHrX3/gUlSd8XseXTecVaT9h7uusVxULr\nejfjaUGydndWnsMHJKpJIi344IpHi6ZXVd0ZD9mmmJtDaphIZWftS7eaC4PV\nWsREtefr24MZATN1KaAqgsAKFFq9mgXKLhm8+Ho6FmGJjP88G2Y+VzfiN6EK\nArqaDCjCUvp3TFNcDkBn8EgNk0WWSeQwjQ/hu7ZEXm4zbItXe+lMvyp+Kyr7\nGtkKhGRHVohl7fsCVodjtV/n7Kk0GOsejfIx/0JzciL4J4+O4nCHrPDojaRd\n86cn\r\n=/L5W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB7AEaQiERhjAEoFM2Jwp/jY0Y4Enmz574+5BrGoZF4yAiEA+kgrQ0pim61BbwU1zqG7ioikLx/8qT/JsRLjaRMxrZk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180913074630_1536825020836_0.569695436982061"},"_hasShrinkwrap":false},"1.0.0-master.20180913101258":{"name":"@atomist/sdm","version":"1.0.0-master.20180913101258","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913093131","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e3f6c406a0aff399db2420013239ed57ed9d5be5","_id":"@atomist/sdm@1.0.0-master.20180913101258","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nvconkqD7gXVB5zHQhnNSTwmImzV6I7KnOhhvcLQsf4bU+nbEXhPy2+NxF494JUxaO9Qgm1Qf8t9xJY8SQP8Qw==","shasum":"c18578f5daf0923d48119f043761195d5907096f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180913101258.tgz","fileCount":919,"unpackedSize":1906641,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmjjOCRA9TVsSAnZWagAA+B8QAJ6MPe+lGmCxkMeM8wT8\nK7GBvZDKI16QbUOQBuiXd3D28bQTbTFqfPewBuCNg07yii8KUh4wcjJuGtG/\nK5+xfY3lojJNbMqGw9LuGpdrSKjTJltnbx0+2mP27xFO4eRub/ePXKYaKG5R\nkec+M6OeroM37ZTUFBomCWeo+acBUz60VEpxu47CUalYG9qh00UrH+LyMnvR\nVqqmoIxJLH8clT8Kj6zyDGGdOZeF2XUdE+gmOllK1AgLKHiwTwdV4QBCupfg\nqWAQXPmTpS8n8MY81v8nFwOC7+tDm0LU4Lisiub7/9e6mjx7FwE+1QPL7K7/\nDNrGVqP3inr1gA79mAN1Jyn6EINFyBSw+yr1X+66uzn+7nn5VbRpqwN+yoa5\nOCcolJa1xVCci1YETmwKD+tzct83J6QYi/D9QP6BiOqzzh1pq5yT9D+3XaKh\nfEqhgknsTnm5l/6v4h2a03Maseq70n0xYa4/PrDbzO08hS5oUGdRakM/gfg/\n0tdZFLggmckOou9VxADGZBIhmtgZX9GgH/U2bXUabfcsr3c28EqcoBgL7nav\n+0ARXKh89TgWx68vCMiAYdvHTfVTIMs/uR2J1j56UI31DkJgNumics2UPI0L\nTyoUDj+rpB0Qp5jV93uDmXSqwMzYHEn4jzyV++evA3GA7Wfwk/0knQ/KEptZ\n0pe1\r\n=DQ3K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCnfa7XCelR5Esebh6v2Q1559GEs5YlXgNQNtMg2fJlxQIgUZtgoYDI3esEXnz59Ty/nF8z6gLCNR2KNlpizQRJ8u8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180913101258_1536833741593_0.7692039708874883"},"_hasShrinkwrap":false},"1.0.0-atomist-update-branch-master-1536845873907.20180913140545":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-branch-master-1536845873907.20180913140545","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913134559","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9c4e9d6adfaf706a37a567916f65aa941578b5ba","_id":"@atomist/sdm@1.0.0-atomist-update-branch-master-1536845873907.20180913140545","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Rkxyn0RNI/zZA2vAPm6VnFGki9YtHOFzMXNgorWfTVzCogBGxtqw/93VbxKbH0urM43cOj14SaAYXIj3JidBPA==","shasum":"f073f5d83b41af0c5feaea37508b3c8a04a6070c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-branch-master-1536845873907.20180913140545.tgz","fileCount":919,"unpackedSize":1907686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmm9uCRA9TVsSAnZWagAANZsP/3/4AuHoeZ0Yqdww/h+a\nPUC5+ZXXVQw+hNAxglTIm4smgxXH6vVmoXV6CF9vbDPs2ghFCqsgxzjQ3OXa\nBAB1nLGD0qqWdN3s0em+xCSWK6ezD2//+BM8lG71PGLCJ4i53mUb1WJlk+a0\n+E5lIct0eDpDACSIjTi3Y/Gf6PcCNo5dYZXo9vKLWHcx6AG8tzv+sN9djYCS\nPdp1m7HY/oVG8Stdz0hwGpHlXqzZL9saU2aJXDaFUqDR/9Ti3G5T1TEfAlWW\n9yZlADE7pHLLgwcBr50lLNwQJ6wKZ3/qK7f97WPc0Mg4BJf4xLZh+A2yIaWB\ntFWbk5coh6+as8a4YOCl1ky4mSg6buoCrRMWL1fALqYw6M/i9gOPmkCSg6so\n/NznLuvDx+UebI0mnPru2t5HvLqc3DHkr8L9AByABJtD8T61Yhp1bCN0otHa\njX9GqXDiEt2/051IkUJb7dLrJTOdZYp8Pm3FU54FGXvYHGVaTjsTmKVWg/Vs\nyZDFcRdwakLTYZon51016Qp4J5gWD6z1cs3eyHo8vE5ps7UYEPSPKfiZdZac\nK451NYp+55CJc5+4mZe5f+heaxI8aSDBykuSU03r1mfHTS7e9fVs4+p40FiO\nvFO9gaG//BWchDzUHNX+2q4KdWlEnLVDD+JwOYMagcPfZ3TAs+bh5PZpUMf8\njNU7\r\n=C/pO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICotOTG7ok30F59AjQQ6KCxIZgbI51mFZcm4Vl1o8jftAiEApRf0uoYnvHUfrXrzamgsn6lJI0M5ym7M2qPcmu7a0uA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-branch-master-1536845873907.20180913140545_1536847725409_0.39107847488897907"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180913151805":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180913151805","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913144729","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e6aad8f14c5438ed9debc69e1f86a0a424496c2a","_id":"@atomist/sdm@1.0.0-update-latest-master.20180913151805","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FdtoUcu8n7TlakoI0FvKfFivGt9vlBY6r3Da7r5QILKsBRQiMIxphs72lCq0qAPwln8VoUir+DfZbI48lfrQaA==","shasum":"1a843dac7cefaa1283a8560048e87325322ce4f7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180913151805.tgz","fileCount":700,"unpackedSize":1532595,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmoBfCRA9TVsSAnZWagAAvr8QAJLs1jxY4fJlzs3kQxJB\nyxYNJW/dFcDyU1H654DhRT8UFaMs/6/6yKaZQYKzgJhr7b7wGfU/5LzMfjRs\nrc8WLUbsVqLxTf3wWT4smm4jHdty/xzRR0qK6+Rm5DYYjzKoHFy2x/yclJty\n4Cz1Km9erBvq2BQix/f/r6AN+TmJ1pUd4cuN3bQXcSmmwUHOAXrP5CKpiuCt\ny7wpA5gDs8ayUYS1Ma4viLGBlsKgOC2tUJG0tDp/VG/BOOITOcZ6wqfC9dBT\ngyEb47mtmulpVzdB8r10mojgeKutVwAzufT/vPAmTuzOSGN9jJ/z6kvpkfCB\n/RVX8+sFeYBiGWMIGkXaAq5bxqrVd9D08sjeK1LTC+O6f3daQnJp+vSW13Hp\nxY0e/Q5bXB7ogwUkp21zlXwJllOTYVwCQDPzREAHRe3mvedF0prCQk/KYhCN\n8wFbRoUVU2jaEu+RJ+MjgyNLnAsP1wcgBwXB9jh9fk2/9O9BCSRF2LbsbIRt\nrXAF9l/NsMnNt1t+Ju6GCtuf3AbHUv2VoK5321Yq/rGSXcRDThOrM5atz2jm\nn6juo4I/EN+nyP/lRUYPGU8N/JmRUg3BSU0CoWqw/P3bwnvT3W7OxbzBt3Nk\nZcD/FgzhOlXZ6KYKoM+bO1yMysfHx1+fH/lJoDTy0HZ/qowLK+G6KD9v7XNg\ntn0q\r\n=naF5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDytigJ2RPeL6EH/GyvMLXmt81n5Ow2U8yrhyq1jtd1EAiEAgsmUs33ehhWHY8FqiKwgfW+u/CMESrGieIMyQpaQvno="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180913151805_1536852062810_0.2600715964349434"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180913155945":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180913155945","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913144729","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"74da6c511d8b1f4472b3dc0978f96264fa71c13d","_id":"@atomist/sdm@1.0.0-update-latest-master.20180913155945","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-79WZDqEv8xW2/9a0GWPZ7pKodBtzvWDJ4IObXzibejFhvP0/KLZ6XcWYr1AD4LMlm1SLzWnGwr1Paqdd/uTHow==","shasum":"4e9c4d44c0d30f5b224c8a3e9a860dac6a3a9b5f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180913155945.tgz","fileCount":697,"unpackedSize":1520727,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmoohCRA9TVsSAnZWagAAhZwQAI3Q7lWonKjmryvsitB8\nqSZdk3A6RtKyrConr6Y/Msdy8o0G5jTDbmlfTdrJdH3isN8fAYQoTO/PPcXj\nehLWOjjETI3McZMYJHl/IwU8WGNKACWlgS8yPSuEkvC/XypxCa4AWDnoiZbF\noruxbJPH2viLLy0dASuv0r+Jh3OKd9UhSMwHtKZnJxRzp7CKKijzG69TFBeR\nMs/OU0ZGsApn+N7BVdEcxaFqIbwRX7vZnrMWohKbKjZF2zpBuiSN6FJoJvoG\n3xZVUlD0p5c3d6bNZj2jxZlD+OOWnwP71gOwuUANJ0R2Wc2XVFh//XUQZjqp\nSuXmdF8Zt/XCtpGdGknIBKTsLQAZi0aezhJmAvS9MbIEj+2RUh5w1DrGBZNQ\ncb9rUcOOMaT6ZPB03ZULwY1rAqtGRbkL2BGuBjF6Hq38jbinI5YSb0G/rhTA\n0ZPhv5Zdb5s7Cn6px36q4xM1EsOcjVtU0bbspdbJRGx5kGrfo5i6EupXwNES\neWXusBMHstvMCALHh4ZHvPXZiwOrdG0kDyrtLB3qzG/NTWM646PDg6O38YYv\nk3BdKy0IdfGbyru+iocqTUYeTflRTyYHPL/BQKNdEHHXm6A1YfKeV6nRaKFF\nin58NucPxy6bJwJXghQiNVGRkKwEJ/lC1yU2Dt4jU1JW1/ur1L0zU9Y7m8xY\nNKkx\r\n=L4KN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFyBUp2+UZq0O2z1F9PCsF+9jrl4dyua5RW0eiSxysLvAiBpW0oA8OT7RVcG1UN+zfFuJK5nMZ7Q3vl6EQGnX9HxFg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180913155945_1536854560774_0.8462365603368436"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180913201907":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180913201907","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913144729","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9181d1dd64035757e1794c8f51b83506de72ccba","_id":"@atomist/sdm@1.0.0-update-latest-master.20180913201907","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NX6vtapGwOGxXsUt6BuJo758uGCjKRPTPlGqSNM7wagCE134qqtN3r/Xbfnpzd8fKkHKeB+Z4nPHslf3/LdmcA==","shasum":"7549799d315180fefa78a2168ef3040b32e6423e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180913201907.tgz","fileCount":700,"unpackedSize":1541786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmsbuCRA9TVsSAnZWagAAOj4QAIdD3nQoczpVDoxH1JjS\nlBvj93rdjYgz81ZRqQOU1gMLHgNwow1Qa9OVlSS9rVpsytV2qn5yfNSUWlWp\n+BomDmmePUva9no1mfOiZ7l4ROv7HPhnk9UqPi42Hto5lX0DWaPGLUa7aoFy\n2SjmDVINypVVu5FGAHlC3tQEa74N0ADW49o5h0dSUxdBofXbeykMeY4m1/cR\n8MkA9dizI9YHIAhYkKzk+vrAccpw260JmEtui7azoKrjFMK9UdgIp7BzBEIT\niG8rCFiWpE0cXC6wbLMC6DIDZe7Nx/tIGtg+fv9CciUP8Wk4rAPbqSUJlS9A\nGJpnpUunBvInzGHPY+u7kO9fNgEaNuBQXEvq4ZphTcNi+8cZMfZ1A9AvAcc+\n9DUO1KbcrewZgXvfVtYS9kWs24TmzFK5lG6hh1+X0Tvr8k7YWaRa0VG4khoO\nbWmhKh+uFBSmDp+epd9CWjlVlGMghQMa3G53STvR1Yla2bqSxOKxH6hDiFci\nxp1ofEJDTEnLVeyswF1+WMU3y6Yuf8xyFROrzAZqBi/vbB/rlwX3G2bFNWgR\n/8Sh8InC6BMHATP1bZfJYuLPoJM9Nq+u3YqtKRe5GpYi3EoZQslrFsd8iCCg\nXEBiD4nGOhMm7OSDRK0QnqtLzgijXGVO2GRf9i4rHsHXDDWxlV6b5e336vXP\nhHs5\r\n=01Vq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5vr9h+Tgzm7ipAw6CHRYiYIeMB6ItoqkfeWbzALymPwIgFLFW3pOhNJVZ/siUDkheJ2VGp89sWufB88nH+4ccRw4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180913201907_1536870124861_0.0033435864608728494"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180913210549":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180913210549","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913144729","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"83aedbea09f896c29eed8dab7b3440622b4f75c1","_id":"@atomist/sdm@1.0.0-update-latest-master.20180913210549","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Xj+ze5sIOksqJasiAUhHfph/S8OSvZn5klK6zYJ0t7VSqs9uYUeshjDTl3FfoW4LH7gpuuMe7f1v4Iy8IvzvdA==","shasum":"c51675880dbbfeeab434e8ca6ba42091cc8a8090","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180913210549.tgz","fileCount":700,"unpackedSize":1541794,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmtHmCRA9TVsSAnZWagAAoBQQAJvecmMOAYMMIVpjYZ4j\nK/fePZ1MdFB5T5KTPd1kHnzb2/UHm2tncroxXoJebx95s/uOkEuHDd61O6+4\nWlVurjTiej7aopqdF+YPOhdxuRB1wsFKZk9ZdIe76IOD6LyHjYRXIf93Hrnz\nyLlS+zykdsL8xwg8wJNcOgO4v0pNk1Xsc59B/b28kKKn+98/LhPiCDoZ7yw9\n10JYQv4mGsO47cR7OuNbmy/Appx0wUmNw+EBN8q7jy4sbbbOi7+kO/ouVysn\nBTgP746Ke/PMjHP35UhgmKSENgtN29JiQ678X0EiKtoCRlna11iJ6ZF70N6M\nsOqlYUDguflBX2EtFEmnOdcvKk30F2WRshrTcauQCqDv7BpQMQmQgP/Zx4qD\ns8cyc4anzMCN7jCK7Of35dq3HDAge8jMV9tnLCLfGlZljYewncZffNXu7iiA\nXCWuFeu8x1mFoya0nSCm0z3LOmsCV92Y8ZFibesdoeqF7eoJiKsZ+yV4vAAi\n3n4Pf9QhwHRoDwOXxBMYzhLIi9mBiCkhnVbbgUnu2WrnQdZNQKK4BX0OPqKD\nQxwAj0Nh+/4RYyOcliZ2dX6vWuOh0hlMEYsJIUs52cueLXJlT9qP7WplB/Gn\nJbP0DBn4RVwnGsZXl0gU4ZS0nyHe9n3kyiuB8mMvBYnQ1IxNo329wu+fAVJl\nClGm\r\n=AvNg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCohbGBcHmXiryp2TDoADXH4cy62oDafgXADCEjvMDjzAIgIv1srWXkfKLAz0BgCX+2NFXB42Nfh73dwuYPni6eOts="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180913210549_1536872934188_0.6620700828945771"},"_hasShrinkwrap":false},"1.0.0-master.20180913213812":{"name":"@atomist/sdm","version":"1.0.0-master.20180913213812","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913093131","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch src,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all generate:barrels compile test","clean":"npm-run-all clean:js clean:build","clean:build":"rimraf *-v8.log profile.txt build coverage","clean:dist":"npm-run-all clean clean:npm","clean:js":"rimraf \"{src,test}/**/*.js\"","clean:npm":"rimraf node_modules","compile":"npm-run-all git:info package:scripts compile:gql compile:ts","compile:gql":"npm-run-all gql:gen gql:copy","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf src/index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d src --i 'api/.*' --i 'spi/.*' --i typings/.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:copy":"copyfiles \"./src/**/*.graphql\" build","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"src/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","package:copy":"copyfiles \"package.json\" build/src","package:scripts":"copyfiles \"src/scripts/*\" build","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc src","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d43b097768730c890a7f8f1a94038681c7b9f6b5","_id":"@atomist/sdm@1.0.0-master.20180913213812","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kDFvJEKJR0V3Y4mh7UUtPHVKNBfkIrK9v3HC2ho73ROkrc+OYNlYebYVgv4Tk2fZYTjFLyyNmPYPwPvmhLDkhg==","shasum":"1be571905adb3f92f0f2469a01e8398129d741af","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180913213812.tgz","fileCount":923,"unpackedSize":1912659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmtmOCRA9TVsSAnZWagAA9Z4P+wQDrKvINX2+T6q9A2lV\n2moqoN7+XHtsT53mwADOnl0u53SXgI1hZodw0XfAUCzkZ6/O1692Fw/iF/r0\nqxILii1ULfMPc2tB/ZyP9Hh75LBvo4tCvkmXzgYPhLWoTwfCn3FuXtyDBHPx\nG4H06Y/YT47IbHRWV4Jsq1neJGp/la9UV9PyoL4LRDyRiFXZ8uYiuPUewYVo\n7q0YJW6qO26g6O+m3xuMQiHRBWGA4vy/4T+EbYsWvnV6FL28xKWtDAeqm1wS\naDssF06yT8Qb4/7drvAO9v2dvCHQbTmrrhT6wpXHwrjPosr5VNGmUx96g3Sg\n7QUR1nZoVbFEyucnZZDYyuDJ9QkLCb/HyzElSFmKp6BvPhbonBZBDXM+RP5J\ni6fjUOuRdPGGUKNBnfU/fmQze53R1QZW55MgwkCctcJoQbvw3CEJ8EGNrw06\nkaPUBspZ05kJUz0OQfSKYCUJj8zcYsGQr+Cvb3/g/JJK9pDhX5G6Z3H8KCvR\ndV+P09Q37w39evS4JCIxFYuVuQn+8SldmB5lV+Xyl+yWXNeJxqkpGkEakUgG\nKi/b8FyuI7O5neT5SfxjJESCrIafleIju/zmcHYVebxnOGv96xuPnc6G2M3Y\nNZBevX9t19hruOAvJkL9c/EQ6AoCzELkOG4k1pyLdNmG7QDAWrw8+qbGQ8n5\ngCWi\r\n=EYl1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDek0g5UNS2fIRmAS86RguW7+kQbkp4T8spQSUemNWgOQIhAPcd6OM4aWfQ+qpt+0XCsrNza7RXcAm50Uf0HIpy+s/M"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180913213812_1536874893934_0.6984822364015135"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180913221116":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180913221116","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913144729","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"90eed40fb68e12d31d714eec8e82f3dab0a78161","_id":"@atomist/sdm@1.0.0-update-latest-master.20180913221116","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sPNMK4KL5y2Am/myPw7oh1JaU33m+STNo/IYHBSg+aktyg8Cs7QNilPGRQOqRgchSNnaXlyb8xLa9dO2blVxDQ==","shasum":"373f25000671ca4892560cd74eadff527cf3494d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180913221116.tgz","fileCount":700,"unpackedSize":1541886,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbmuFXCRA9TVsSAnZWagAASnkQAJCBqMR7kXGwcodzgGk0\n8rBk5un2WB0zeQQEHBzNMyJCcZCVZtC6L+7lMy+Asf9MilIPjyL0op5MeSXx\npGEpWpVyZ2gIA1KU2xF+0doiUAx4Oo/OB2jk/0ZGpqww066O/C8P+MBMOs0a\nCqVopDt0DadY5JYoUALf+o/eLyViVDMepBMig+6keKWQxexX4jVNTfevYfSO\nINmr0E4+jIuVF1/bu8EW/5I0dGtUlx3PwLEihqxFCXsw8rUvML9dCphc9tz7\n9yN/hjqOu+mVtBJcU2xrtqPrKTDpa+qbMYnDmCbbvznYm5REqrVDbua3dldT\nceUq9+tLYJP+sSBfNPkUt50KOh3fF910mNzEuSGDBC/hclNiHfyvyp0O4zvq\njDfvJerQnrUhm29OSojzX2NrzanF4kYxHqU/2rWsLf9/hMx9lO8S1MI52kCU\n8zxUbQp5c/wlKrCoXKrmU9MIx33u2SX+UJjogEq/Z4+wvitVeAkWkmhN49xt\nfbaQfJCcYyVag+YPmW24vYIoiMePtpUZxve/chLwFlY1YuNOP6xDNMbEeWkr\nFv3MhiTcXkbXJvqzc+OeDoG0zvs/8CG8Oc0m2ut8M1TSprKUHKzSzuosqkLz\nyTlztGQUMRrynEC0VLbJTiGUSv42FEERD5f+Ko9rsUetmJ7MEPHnqXi/H/0P\ns5Nn\r\n=RbXz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHhCqBL08dvHE9nCRIcjaQaxS1Ya8JTVw6SV7hskF3OWAiEA7vP5nSp8H7bdpcEEiK6iLwVp3xl0H7bsKqnEfL4uwjE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180913221116_1536876886847_0.7089781553136638"},"_hasShrinkwrap":false},"1.0.0-update-latest-master.20180914075429":{"name":"@atomist/sdm","version":"1.0.0-update-latest-master.20180914075429","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913214030","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2803ac56b16f831c525a7c91545408bcb3569398","_id":"@atomist/sdm@1.0.0-update-latest-master.20180914075429","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8Kf/56vrLVv1kpDdmgEyFtM6pXxVlujSDzr/AP4XmzHmaDmispzYkRYB12RGLPbnuEb2CPLEnlybEYWj0BonfQ==","shasum":"744a6f04bcc6ac387ead5b029dd2cab2460a8188","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-latest-master.20180914075429.tgz","fileCount":700,"unpackedSize":1527420,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm2nhCRA9TVsSAnZWagAA3HoP/jvRLksshvKtsZc7LtnC\nIpWQIAGvv1LJYZdfaqWtzMwgonF3DypBYUX04fwc/UYHIlfpnMR/sszjRPOZ\n/hVUPKPjDiOLBOEMADKQG7yJoGEcKslrOkroiSJqMuBjl555MaIkRWnH3zAg\ncKtapHXcH0Hw3iNzWpDQxdZRS8A352O8KoFEpveiHzNNHDo/d6ry4BHjzJ32\nPbB/i/Xa0j6E75NbG3rW5sHy9o9PbiXNLO/KIyqE/4aBw+aFm6IezjKwd1M7\nFEM3Nprrfxe7Il2hm/ghJHCpxFAC0W0qWg5dCbZv6a6cR6zHdXxDV+zQrF4f\n+NoazFH6JjQBp9nOLG/G6KsPX4bKz6XkcxHWeIp6elxw3h7U4mtZG7Z3MAhu\n+XN/7dTW/MB0CaI7NB8s3iiLqsinZld1gLGGtn0NLbfMeFSbB8HPkcbe8oqV\nfWRr+UhpYfi9fm4B9xQF4X+EszasUx8Kw1IyjKqlAWRpLjGRLHc188vRpOEq\ng25e6QmDi4PfaNw47RwGHXCAVymsyq1Vo6U4thosm6S2GZT/18ETfyCO4N7E\n1iQBmrKNSmKcLeQ+TWN9+coz5z/FRMfU8xOGyyaODrCK8SfyPq4uAayBRWb3\nvTsJMWNU3LlcKluOZLPHT/inatastjdmPCWKsrQ2Hzab00qKeg4JNS8Acbzv\nJpCh\r\n=Hqk8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCw8jVOiwehgafzusGAKV9xEN2ip6ncyhFiZahGfcmFIAIhAObsuEPyzMyNcCHCh3X9g0tDnNC3uU9RDK9zELVMDR7r"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-latest-master.20180914075429_1536911841017_0.5166867910876962"},"_hasShrinkwrap":false},"1.0.0-master.20180914083639":{"name":"@atomist/sdm","version":"1.0.0-master.20180914083639","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180913231034","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"308bbd42ff778bc3e4371eab292762d486c5888c","_id":"@atomist/sdm@1.0.0-master.20180914083639","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TIUu4p3Og7qO3/dfufVbGjpqduinzgeVEYAUBq57rvdWsw39gsggycX+PBhZqlvGukSRaoQaZPm1b2RhxrjxWg==","shasum":"df06cfe5c4f56dee7e2eabd3c8bd9a836d4ac772","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914083639.tgz","fileCount":700,"unpackedSize":1526635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm3bdCRA9TVsSAnZWagAAYNQP/izEEzVopNbE5ZIsQfz/\nMStDYJeoV6sItgKAUK5g4G+n+B9rAPXcvskU08tDiCreX2k/7bv6X8gIOYqv\nBHP/n0fD1oPhykBC3jrXpM2YaVOLvFDjSWJsOzT+eI9H+O5/FylRRMMOlU+l\nMeujpWEmaVT628lF7W0wmLDZ/AKkOZloxjrghQg5kWBO1OqDJiLUU+hwGSwB\nmTChXUliDihBPhrIcUKO6DGIOb/enS8nIlndxZeJWSwHvKH3a9H3AIySRHLF\nYMBfACN4uouKO7KZPLCdcQqpyD8/TtnaR1BoPneA7F9kjNm2XkfV8Yik8yU0\n0qA8AalQgPcFJBA/svLR3eGFDXCg+XEYOghLwEkdc7gyViWVCS8ARRL4QjaU\nkP2j7DJeoN4JCCbyRdlWkJq/ivsh6SL5IeVp3Sl581iE9BqKV39HRA61ooK9\nCOsk862mhLZR/dTYObcTvy482zvV0xOZpD8T7O/8S3pvDGekNKVf44FmAabw\nhq+RKYv7GaXWkxgGD4PovPtNPwZe/QryvneJita8XyTHZNCkZcAioozCXgzD\npS9zK3j4VOrNvCM9qx2WSwLpmpU4i5D3P5OlTlAN2UMaxFeBRXKFxAc9TN1+\nMYQiZ79wlEWJtDpSFN/5RNMesL5MM0JgheVu04gZWXEP3ldvJwjImqDkuIhs\nuQCs\r\n=BJSa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDXB2lny9CZTfX+yQTU8HWRXLDqvbhL9fL0aImaFqUwrAiBKDKkn1qEq4KmX9zSL8cly489r9e4e2mlqsjiKsO5nmA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914083639_1536915164345_0.5653043145538665"},"_hasShrinkwrap":false},"1.0.0-master.20180914100050":{"name":"@atomist/sdm","version":"1.0.0-master.20180914100050","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"110a325314563e207a4bc1905b162c35e895e597","_id":"@atomist/sdm@1.0.0-master.20180914100050","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EKpo8M3XJhLa+lqQsMvJuZ1WWMgQVTYsnmyaR/dYl0UqHzwP0Q6ui256CtAD/Zv+nO/dyGC7YS9xm/Ntlm0uLw==","shasum":"0c1dbb8957c33c344349068791d60a6582ab103a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914100050.tgz","fileCount":700,"unpackedSize":1526824,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm4ePCRA9TVsSAnZWagAAAgMP/i9mxsLXk8oUmuw5eBjs\nulpXmpdxhyJgFEDo4QVb8vYFtDj4eMr3FOYt06Gry1cUhgUL2ZNT3xQT4FKr\nVuFox3EVTb9A/awB0g63hvHycdJGbqZDevxn06TYV/63EYPNFB53HoTx1E34\neZOhzGY0KPlhmgAuOeSW6OfR12jLHPi4P+z8ggGcbrT6fJ1SICCkHTjfehc7\nitKchcM9HsWZ3UT55bvyW2Cd63KNDldxoad6ctF7OGRyvt76l4CpclyUoFbB\n0jS8/9q+BNS1gt4Ve1PBAHy2bPTNdldVKHk44Q2/nN7iITKiv5Dc+b30FuVa\n+J3vrgnvphrV7Ng052Nle7OB24kAMCnG9HyhI/LOPTCab/93BEOvwUvYJ2YP\nx24e1gSBcNTJUNH9Xa7aptftoIrS6L6gnRK8uucTU0O72Dkk6wvhc64uJ1Wa\n/eNJNOjxhwPaW9CkiWUgT14MsBeANHp9BrNwVr1//JIte5DcJaXTv/qwG3Co\nasBRRNQIBkCW3wIGoqGdSRotrJX+c6T13iWNHiopIFe3RbOb1zwjTG00dTqH\nDAQtGpAmhhhUpZbRtFol7ZDA244Fu4+oftCiP/m9KRhckU2hBfOO+6NTEqbf\nwOwyLmpQmSfGMf+uOu8WVpYTQV8QarCPJn9/Q0MA3BnW0uZnIjzMerNaPEoB\nb5U2\r\n=inAI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG1aXyA5cdz8I/3hjoId56R3RjF/3GUwNNns0Wck3XWsAiEAlQm9Mr2RmcwhgayqT3NvQkXJ+NoNjQ9//TrtO60sJNs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914100050_1536919438473_0.24961623082546813"},"_hasShrinkwrap":false},"1.0.0-master.20180914100848":{"name":"@atomist/sdm","version":"1.0.0-master.20180914100848","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fcfe7014527cb6c397465ce15c745472c366b3fd","_id":"@atomist/sdm@1.0.0-master.20180914100848","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0GPhljbcXn2Xr+ChStnmtaARG6RPmq5Dchb6jn/YXSlOkBa9nJNRyFFvKUrFoPpplIMV/Szw0xQlrWrRQvFGeA==","shasum":"78d2a72f6d0387b4f7708f1e8e22624aae0c8bff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914100848.tgz","fileCount":703,"unpackedSize":1532170,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm4lsCRA9TVsSAnZWagAArlMP/1N2MmaW4CflQJnMz11S\n9zEATjV3SxT6flY2HgtHMTCIocc2s2uz+XxLE67BJkGtHiEyTFFf5MJyJFLs\nW9CuA9B1wms71iGIsrgVx8yPXZwTZxdyfamEwPqrI+JIKfyWumVbZ6brb56I\nC3s3uSgSI4WEA6I7b63SxB5UCTHC9effms3puw8Jhfaq+upXBX6ur3EfoTEG\nfmPqjwhjRW4nktVLu0zWsbYuozfLX2ZzoeFTh9L10AMGRFf5cYqltQYc91J0\n7NlbyUFp/e2f9EM0EXd60APTss9sfVUVX9k2TcpwCjjNkvVObqoksgY8RYBg\n7pcjbHNfGkBSHBPU8O3oiBlK8UmDDdZkz7D0lj/Um6us8Wd1Q5Vehk8ohCRy\nv4CBhrlQzo9jlj2k82HRrzlTTAHX3y76NOAVT2nS8JPeuaONdMEoDmE9g3Zh\nw3A3QUkbdhmZZdsG4k36eELDyz6PzDHrqChARXZJMEU62UcqDpRQI7BsiwCu\nstXfWP6h4QpkxqphKlQDx+MvBNjcZ1IW0S00YSIvIxhvn5Dc0eLTvPzy/z6G\nsUc2x3ET5npurA7aJ7/h800grRQoQ5/3oW1lA/2RcWRIEtcgUEzZO+I6hW2K\nNZOcLjQgp7QiznsxOBPqjY5P9Bl6pf/3C42NqHtqSncwxw9uKRS1qwMNXRU1\nwqd7\r\n=A8qw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICKXkVuAjiErWowb6iuls4P6fhmpALznpXJjVc/oHAY8AiEAlFe2a2SA5/QqPcE5gwrz0lvg7QYt4hvQu1A9rtwc6KE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914100848_1536919916193_0.4028673587073419"},"_hasShrinkwrap":false},"1.0.0-master.20180914101315":{"name":"@atomist/sdm","version":"1.0.0-master.20180914101315","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"10e2eee68c2d722efbb10aa3ab87ede80c38856e","_id":"@atomist/sdm@1.0.0-master.20180914101315","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aybHkXqQ04Dh/mlXYd+r6vO1JqpJPLfd+Oc+IlApTu/YRkBbiXbJkyxDwwCkb4uD0LqeBET7Ys1fPxZ8eTKMAA==","shasum":"a1c5e0a3f5e6c108351c74df38f9ec22d23a29a2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914101315.tgz","fileCount":703,"unpackedSize":1532900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm4qdCRA9TVsSAnZWagAAmU8QAIodvrdOyfWBOG/6VFUN\nJ2iMPhNZOfACoSGHKFw9e6gdLIUlcoagvusC+6Q0lXzICBUd9UoA30ebMTOe\nW2iPjc6UApi5qrKImYITwkYG16k7o8ePfWqGkQvwBQKqLeUkT5LzIIPRrpFs\nkf1Sla0sAEwh1fYbRmXGwlDEJniYuucOUz7WRe/aW7ljUG2qXODz2s29kU+B\njzlsO6OPKj8iP5K0qzpJNhdXIUdqMtAuIDfbJgajqLQjbJuduvc50AT4qH4f\nG+bmID6t1PrFRSwbq9PiH6PPhhsFEgxtlc/ifqFc1map/Ev2WatbezT8Nd0+\n0ycoB4HKtREB5mskSFcmTGVM1WLMdUYyYZX9apics1EepDynNLeS4jCYJyFF\nIHeOoIV27Yua9j3NIowH7L9PshFXokqsd4BnNyzNozhZMSTMV5C+KgYckthe\noudojhax0hgJZGTo9A2dXo/S3Mi1xLmoSCHgg6avrFW8cDDc75WdjNZOpJel\nuzKGZSxKR+4mW6psjm68Qi4WK2Xi+N3C3j2mrQPmISIEkzamQUopdBCPOI7p\nTB4hA1RoJuty9EPFxT+pJhLm1DxZUBp1ONP6mpmZhw2+1Mze6NutjwmTzMau\nNhSlRTQw1XdxiRfvUqgrTNuhuvwjUf9PwyebhkqkWGmblIph9pl8P7GyT+zi\nTKlv\r\n=c543\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDHazf5EaJ+g5w5jD4/zlJByL1oAOKpz7ttFgD/V4JtxAiAQGuPzhtv3PTKVUMibaChoJJ5CEFwzi2d100dGbCSDAg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914101315_1536920221215_0.42835628434783724"},"_hasShrinkwrap":false},"1.0.0-retry-docker-builds.20180914105128":{"name":"@atomist/sdm","version":"1.0.0-retry-docker-builds.20180914105128","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e CommandRegistration.* -e ParametersInvocation.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e435057954f06e4f9cfbfbac5dfb9e179646f1db","_id":"@atomist/sdm@1.0.0-retry-docker-builds.20180914105128","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TytyNogDn81v4btZEeKada+ilm8s6oNi970EblTiUyeFLsB8mv8fwckbauGq6g2yzFOEMclcVt5d0qbzSwPz3Q==","shasum":"3a15e4cb65ac0b94941e3c0a0b6f0113245734fa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-retry-docker-builds.20180914105128.tgz","fileCount":703,"unpackedSize":1532972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm5N4CRA9TVsSAnZWagAAifMP/ArTMXoDjFJa3soDKlaS\nL37Qf6g1DRXEMaLCJOEOJr3q0ev/+UkQabL3aivZd2fdFO2j1FyfTeV1AWkn\nWcVLkLajxkYVAoFH6qqRwa4kP1I7pBdDbhPRfJCGYlcfDf3sajSMByYGtCq8\n6kU5+pi0AXIh2Czf1Pzaczi46+POEmEhSggB4yNonUzIbgn3UylmPt1q0OfH\nFAfYCYg8Ao2t89vBUUxGmkKQvJEIQb1H5BtHQcI+T0f4TxpLGDSAcfZm8PzR\njt96QFjdbYCxb+1DScY7JUe+eNth9WcgocWPTJUpB+HTM1hY9R79TvWvjXVe\ndXMjFE/FqTaWDgao7607wuBJuELFwskY6dBmEk1yWlaoJKtEKJCMOOb6zCMI\nJwfFVZ5NcI4y4HIMtfzMgMENJ/L3O48dFOj8FdOl625wBtnCtrP9J1/VTEZS\nCdTbfuhCVaVp1WzbsKBBgvnQUZ8sTk1LCCJK+w1pI5vYRZhvMSAQ8qcQFI4E\ngQufPb7/G0E+Z7mfHElaI9z19ZGWbLEXviDHE+8Oj4RgxX6r3aRbZdCQo9TW\nYZ065wiKZnOd9XqbQmGKAPgUNNELk1LSfnYkPXE8PDf07OYY2sWZ+qs14JUj\nd92e8aBsxLFzzWMrkib7qQ6l5F8EnAyCNkh6YIZsnwT2AbVbkOfbervB8nQ6\nanAZ\r\n=zz4J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICUYUb6bHcryonFXJKKC8ZmsEpturMjz7kFb9V9ZACRVAiBYKu9rmCedTM1ByvxzKOwP/jafoW8IJMm4zk+yKAf9Iw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-retry-docker-builds.20180914105128_1536922488274_0.8789563751257292"},"_hasShrinkwrap":false},"1.0.0-master.20180914130235":{"name":"@atomist/sdm","version":"1.0.0-master.20180914130235","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all gql:gen generate:barrels compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3252e5e34ee638827396f9525158e6323f60de6f","_id":"@atomist/sdm@1.0.0-master.20180914130235","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AyNz1WrMn0COvt2kBQ375cUaakTO2GVlWJF4G4SkIx0WAD43OLq/oEHDWpvHdW585C6V+ZYrT/mYEkom5NLV4g==","shasum":"39d836b32ed64235f8b361199a80a36a45e108f2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914130235.tgz","fileCount":703,"unpackedSize":1532936,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm7KOCRA9TVsSAnZWagAAZpwP/iZVqevy0IQaq7urtCLV\nY4BFtq1o/y9pxJsxY1l8SqTUnr9QOil02JPE3RFAW4MuPfzLDjHUQXpafN89\n+/dvNGl1agxT06Z1Y9l5S/LgPRPb8+ZeI44y+sYNRQIRzXLIdvO5eOukRmmF\nwwQ6TS9NeTl+PYuI1vCJ9pkcfQC5N3WcARi0d/vd8RkoONtwrkxi8H97U+1t\nbABE9vWbfr+CWMYcwIMzk6R6yxIIYHE+1CP1Tl4+0u+jlhUH/ecS8VnhJOfc\neVlW3Z6key5xxOJ1r7tzX9k6OVtRFPp+i1IBFOg5vb0EK6gsha1OlLoF9ffO\ns7IxHnwkgEFv6jk9bjpCghr26usKhfV+EP3U7qtHyx9AgiRDHvSGHZXL3J4C\nljoDxQY2MJKZoRiG9Y42FV9k8oNuiqvEq9RZYihV2UiF2szSAsC+eezlzW9U\nmYWqn/ieTolZ6oNSkwS0oWGc7vJz++RRKXAnIqQJkcmaAc1bNIf0llp2noT8\nWoabblKxsKE+B37+Yvlf/qF/jjPy6/PPLS2m0iF4ffSblfsQ9fOKX/gS1drB\nH7M/dnHdf0+SuMsgRJ3gGN+FDopTPogLRRi8LhJkbc67mZ93YCOWNyVCWVCD\nW9Ma6OTbDrWIyz1NYyQnzTUOjVcodq6r4JI4AdyIczJ2Zg099pJa9IRaXRtc\nBpmH\r\n=oYNc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDArzsQOiL9gHrX/UHvM47abbN0hXFUdjaDHL8DbLgNSgIhANf3uy7wGaQIPyDQksEq3QAUvcCl0vvIuTB3ZDGQTO2w"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914130235_1536930445895_0.10203423591093563"},"_hasShrinkwrap":false},"1.0.0-master.20180914132622":{"name":"@atomist/sdm","version":"1.0.0-master.20180914132622","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","npm-run-all":"^4.1.3","nyc":"^12.0.2","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2","typescript-formatter":"^7.2.0"},"directories":{"test":"test"},"scripts":{"autostart":"npm-run-all --print-label --parallel watch:compile watch","autotest":"supervisor --watch lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"npm-run-all compile test","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"npm-run-all git:info compile:gql generate:barrels compile:ts","compile:gql":"npm-run-all gql:gen","compile:ts":"tsc --project .","debug":"node $NODE_DEBUG_OPTION node_modules/@atomist/automation-client/start.client.js","fmt":"npm-run-all fmt:ts fmt:gql","fmt:ts":"tsfmt --replace","fmt:gql":"prettier --write \"**/*.graphql\"","clean:barrels":"rimraf index.ts","generate:barrels":"npm-run-all clean:barrels generate:cleaned-barrels","generate:cleaned-barrels":"barrelsby -d . --i 'lib/api/.*' --i 'lib/api-helper/.*' --i 'lib/spi/.*' --i 'lib/typings/.*' -e .*\\.d.ts -e test/.* -e .*buildDsl.* -e .*deployDsl.* -e ProjectsOperationRegistration.* -e ProjectOperationRegistration.*","git:info":"atm-git-info","gql:gen":"atm-gql-gen","lint":"npm-run-all lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"npm-run-all lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --exit --require ts-node/register --require source-map-support/register \"test/**/*est.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.ts}\"","typedoc":"typedoc --mode modules --excludeExternals --out build/typedoc lib","watch:compile":"tsc --project . --watch","watch":"supervisor --watch build --quiet --exec npm -- run start"},"engines":{"node":"9.x.x","npm":"5.x.x"},"nyc":{"extension":[".ts"],"exclude":["**/*.d.ts"],"reporter":["html"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e411714191338e10c1e731ff867c15438d10482c","_id":"@atomist/sdm@1.0.0-master.20180914132622","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zCyxje8y95UAiNidp9ODOJUO9BCSeBpKmK2oSxTMPrOu9yS1Zx8V2fRBNOOjisIfkehD6humDHLBxYaN6ekTFw==","shasum":"fb69e1ca0d8f5d5179976ab2e5fe17413ea3a9f6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914132622.tgz","fileCount":703,"unpackedSize":1532397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm7fjCRA9TVsSAnZWagAAFVkP/i6fHC5Vw/s2Evb8xxKz\neEvfkTkaYB7UbdEmdYCDPUgxfkuSigFbSPsY52+RjmRl4zbH+dZJZyzV3kdA\niYW4TZKhmQ//eJbbf5GgxXnoidvatZm9/m4+KgMF+tI1/pWfLUFP1zdc3/of\nnzy5k74KucQdbNnmiUdwuoLp6d/neE0m6f/OI2cXPGNGGD3Q9GjzCDfIOOQH\nVuw1iajCP5e1KMOUzLvOYKYpmNmeo6dKKtPNwixabWF1WbnFZYEK4tcfoOwR\nvcPCwP2l5ENbrQRb6lTI/xzLwmVRRMAOFl17Yl0L9tiSoeJk9Quea9fdC07D\nnCVDzVJfSxI0wX6ACz/CrE5IMGMgiaZEQC5JZdZt5HmgEAgaqLs4mC79omyi\nF6NlXMYZVVgl/9Op8qLxlcL3Pp/gwRRoJbUVHDlGZ8kyUoBi0R/XhtxAL3qy\nqIDFQ/oZTUkztQ+3N6VgeNzUdTGAUEKT8cfNzlaXuH3uO2LuhFssjdz0DeVM\nneSfOTIWTgy9E4zeMZbX/AzAs9cWzFsCvh47IFM01KobTsj+uegZQFwq5C60\nYeKFWN7S4do5xIydMXdQ5Gv6k3hqjT6TKWcziIQIAhOAhQ15/QxI8GeqfCJL\nKjRMLAtTEvhzlMZCmEW5sl0IpePW0LDhDFRSu5Q51J2OdYkkmUfhwKCK6Mfn\ngqbf\r\n=qM83\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBLU0TTu5P7Wpu3e2EA+OTY/MFyMUdZCyd0xfcZqgeR3AiBc30T+/+pvcE1QHw0qnXnOKLPBAyNnpRBwmKVcGQfrRQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914132622_1536931810426_0.184574260519339"},"_hasShrinkwrap":false},"1.0.0-master.20180914145042":{"name":"@atomist/sdm","version":"1.0.0-master.20180914145042","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","barrels":"barrelsby -d . --i \"lib/api/.*\" --i \"lib/api-helper/.*\" --i \"lib/spi/.*\" --i \"lib/typings/.*\" -e \".*\\.d\\.ts\" -e \"test/.*\" -e \".*buildDsl.*\" -e \".*deployDsl.*\" -e \"ProjectsOperationRegistration.*\" -e \"ProjectOperationRegistration.*\"","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{src,lib,test}/**/*.{d.ts,js{,.map}}\" {src,lib}/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels barrels","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-s lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-s lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"951010a029a3e6572c5819eb43210351a35548b7","_id":"@atomist/sdm@1.0.0-master.20180914145042","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hXHusKSxnpIH1Bov7sEHe7GbUZ8wHy8wxkXEElnlAfbJxij4KfYxa7EynukM3NbaJTC+XvpZjv67XL8zCVSnQg==","shasum":"89a16b297aba134808c6a907a89a964bd6d4c1ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914145042.tgz","fileCount":702,"unpackedSize":1532650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm8u4CRA9TVsSAnZWagAAk/EP/0sVD5zoEiRekEv2bNb6\ngcn4aYydcnynEOO8rlh7QVXfoCVyc3oNkCUJya4MxaaRs3q2fpMDt/tIC+nw\nY+731iUwSjFReIMG83bI9pSAy+rYBhls4OUWDjNR2yIaMXkPBcJvJ9XrKbhz\nq881bjMC3HWJtHI1wg7VHRF7yUaQJjlXGHNapbrhv+6KE9/W1RP/F5BaxmH0\niODTdSUlEoeGULvRIKNQkPTaDWSCYiLXf8VD8HzEHvA8c6LyVFV1CVVPkaDZ\n7UF3RgXuEjDaIqpSzWVFbOrBD1S8LuX9vkksjHMuFCop1LFdaodFqMEFAjdM\nzbEHbt/lM38BDy5QjVBa8iEdtWl8sLNf4GP90BeFAi3hHuo5+n7Rf999wMvq\nPntp3MmG1ZeQIupLmwYMtUKseL0vRv5g2uMfaQOUhAEX4zgUdhYODwhsoIcj\nS3Y87uASqV4uqKZhxM7Rywc3yN1f9DISgr+tjVvtu4Ip6V7/cMwvgQhqu2TO\n8czYdL5kT6KPTe7OaPghRrHq6Upm578/fy32ZShJEct0aBmoJiM4EmCH51Xe\n7eJa6739csFJlgHfD7VG74nWdZY5FuaDLPLbcm4iDb8LmjxM8xRuVi699llS\nv7pbXoqUmrpqx24Wkt6k5iD3ceEYaK2fUUx/xpOT2Ydd6OPf/wOeLLyMIoDw\nQhdN\r\n=0Bot\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDSfmarKvSCop6WK5b/bJbQwUhowcZpQDmstZoC7DO09AiEAkcnwUCk27/K072KGUOqJEnj9W+YrDKyOMw1HshwP6Wc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914145042_1536936887831_0.767231369686608"},"_hasShrinkwrap":false},"1.0.0-master.20180914155617":{"name":"@atomist/sdm","version":"1.0.0-master.20180914155617","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-s lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"{build,node_modules}/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-s lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9224c0024953c92cd6b7c4f09a9908b3b2c434b5","_id":"@atomist/sdm@1.0.0-master.20180914155617","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-A9LptC0utB/wIf/goDCMTBIXek8Tku3zVmvkdtDne4V6e0qOVaQor71+PH/HeQZ3t7HqSp6Kh8r9khH+1rOadg==","shasum":"b2654911699d9c0ccc1d5a9da271f262e2a7995e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914155617.tgz","fileCount":702,"unpackedSize":1533098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm9sKCRA9TVsSAnZWagAAkz0P+wWE7rd/q7ZIItTw9Y9A\nz+4ugdZ9tt14yfMtEfnmRhPdBrY+Izj9epRAczu/ICxWsH8/7u8YH83ENoxk\nZQGA+19Re/Bb7Ru80pNlV0Sf+206D3WcK0CpPycDQtRpoCBAgfkIvCTKBivM\nBBuAckXQx/l9mNLmQ7t3vl3+5vuqen+P3WCeK2xTdrgrsmhtLD8ujl+KJd8V\naf2kuJ0FrfWR8+fG2nLXshpdkwurp9ubsdkz2Vp2Pf9Zy8n89adX1mGXnC3Y\n2Hpa3wHMQdHPNhDhAii3o3GnpWZdOKIMQgPRAL/n4/EKpET7bX900MhW2vwU\nzkDoMtduBCFVwFn7e1482AOCVLj52Njlvtk9UWv3jRrAFG3vPtcKFbzCtlZK\nf/bibpyzXCYIbDvDLvsNcY0r5OXvyaiQZxOns3BJwZIr8MgPGCVceTolo6X2\nWaj8P8qivmEwdKdXuFPlkCbPxUS7xEtt8wcr2zLc91DaP2oMzM61+07rBN35\nXWutaLwhvCA3P+Glpc3BvbUJEBVM5gLUllHFgwl/4i+tCdLGVVNyKSThYZd7\nAoduI7Akq7GfXIkw/3f9gckNxeFPWXNaSNaTlJh8M+bI3P1yxs6aKPi03ZUZ\nYTnzLhkhiSaA7rQhzPHlpYtad90rCHmYobwNMlKq1S26a/xshEbRyM5teMDY\nV4mp\r\n=K4on\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzm4DsNG55fOEUMM1gjxBj73XYinZLjwC97PEB81aOKwIgDlAUbLbBLIK6pN+HY0j+01qCTkjBcDkj/58R1utq31w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914155617_1536940809475_0.35329424600638926"},"_hasShrinkwrap":false},"1.0.0-master.20180914160741":{"name":"@atomist/sdm","version":"1.0.0-master.20180914160741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"71a6828021e51456d350ae2dfe25e717fe8adb72","_id":"@atomist/sdm@1.0.0-master.20180914160741","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rpg6F5y0lg10NyYGTlqkO4X+R/0gS82P/P3wZKg+gxhenij+tyXdcgZA1pCe2FKLOwroXjOoQ+1SvhvH/8hdog==","shasum":"5b413a422315f0a4125458ebbc16780323d9b7be","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180914160741.tgz","fileCount":702,"unpackedSize":1579107,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbm926CRA9TVsSAnZWagAA/R4P/jdN6eu5aywdqlhAawNe\nPOFDOqhkjuIeSVbYDNerSe2bmglk51SM1bXc3YwLUfeDNqtVjJtw5xOk+Ksf\n2IlXSScLJnfx4Kr5XGTcdAEbF78TuPnwwma+QomXu+X9VoSUBoakQli2dYLP\nPZaXEJWblHJ2q+oXA//5lPqSEEfePaHiiX7DvBchXMZ2rlfHkHzoegNSg1JB\nFTbU29n/gUR7Hdqb77jqXKp6t5QQqKUlwV+6t5TfE6qmqOZP7TsYE1x4d1GF\nQAfejPzorC+NKxr7MFseKs0jGyjG5N5hNIvPI0ENgysQL4aXKZi/btsCCmW0\nOj9Xx5O8dJmqPKhOcA/tvsI6oS3Lp/5Zt/YEcEWq+UVd9G5mdopNqdDu+lVO\nuWcLaEWaV8Wv7dqGa+vGaP2nc+p0U3Pi6MYPsa0OfHjqmNMikSiu17V1+1d5\nhUXRTlEF4F31X1JHFlGXlGhMykQHLVaZgjLORCNh99MsdY/mV0iXSZg8U8DS\nQ0CEEMFXjSueaopt23Ky8NsSwQpBbbSXJrfqM0LGITK830+G4l1YcYfOIjy3\nWX8VMYGT9seMosm8ct2U/kfWUilUE79+8LCVyThdz/uJOAqJ+ojoBA7vloPn\ntIekrbVNaA22LlNvTEOinkDXnxWM3NuA0pZr7rn4MHcncigo2PmUTypKav0L\ntGYx\r\n=NV4M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBRSrf/fm+Ws6zT6KzybU516zzSNN3a7vgM4ZzJ6QVD2AiEAxkp5/N7bjd1vy9RyjSd3DKa8LcGZxjeUY+TQBkPLN9U="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180914160741_1536941497059_0.19354390299209223"},"_hasShrinkwrap":false},"1.0.0-master.20180915075111":{"name":"@atomist/sdm","version":"1.0.0-master.20180915075111","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180914094511","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"04cc8a57eab15ba2ed438df62fe91e7c878929f8","_id":"@atomist/sdm@1.0.0-master.20180915075111","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sBvRLtkt6wSbP5kuJ1r+vRq77UDw2B2TBK+7t9PEMUUHvepwAMQ5xVbvObQ0lgXrNpVriyj//qTzbxyimzsojA==","shasum":"eec1c3356f2caf00aac161bf1fe969730b2b110e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180915075111.tgz","fileCount":702,"unpackedSize":1579257,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnLrOCRA9TVsSAnZWagAA4asP/1ibRPlXW2GezuMTrcw7\nn7JZnWopRkS3T4ma+mN0GT7SwL+6ifQBm1ZaFg9+OnvowgcwUbRr1B3RNmsy\n8sBiWJXbeke6gt9v2TjZH77GQI4BNV/Qf/SdRrSc6qrwJDbTjGG69BYAwkQG\nFzVmwlKV43IqWad6INNAE4ecNZdsKKxj96Ayzq5qB53KI0E+hrwqmts1yekK\nQ0PGjsnYHBTVx4+42CPs+iInBFEPfkceE8oWssSEEEhXYnUVT8mP37TN8MsV\nniNUAPwQ3L24Kmb3ia5aSiGHMhlxnNyziC0Z0jfYOKpXLRxZ12cIIeKhrkw4\nqzM/roZmOnvs5hWmkW8GsqtwgxSwL8sf0K0w+9L+ba654DIliYnWVie+iExw\nMrJumOqWwZZsOLMPmzqVmK/YeI30g9iGPE7qCQog/QY53Nlys/heb91g/SWs\nM4htdR1iijK4jxwiFeQpr2CyifzlG151wJZyt+LDRoLjhYvcwvn0VydlBXFX\nN9zdhRvTlXKF8lIDgD4zusIRPJPRcF2NcSbVCA5Hui8d0Ecen+GHJv5HGdJS\nLK+JNOzKLtwl6+M6Waz/ShejYxHgNAxVKggZGqsIjXqdzEuAKI8Tjv4d7XB2\nzSKhB70+UlA1BD84MJh2XeiIPgsOfGwcGa8WtzIk7J0AzxwoadhoDlTcnDmV\n/1KI\r\n=XNlL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGplcnD3lQiPL8zGi7hpv3UuAM7dye0umN0NsjygogmSAiEAvHhlZm/p6FfoSLQxZdVwLCewhReOC5t2RSURAreIWNo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180915075111_1536998093639_0.032491141280334146"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bce3da95879456bb7ea31bb8c60f6d1aa7a1505d","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-e18uZDxva6FRPT5Gjpdhzij8ByQghbyVqQZmJEAyDj4dM84Ue1mGoY5Y7SIADsD2VAV8JJLX8TTMi3CO+phhlw==","shasum":"108d0161472f9f151fed23e8a01038fc1338b0be","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109.tgz","fileCount":702,"unpackedSize":1579271,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnji2CRA9TVsSAnZWagAAokoP/Ag0Cea2i/gH2wzGCJpN\njuVBWBdvz9znaryQ6M+qS1yjQLPLB91C0k1pIrXzMZkLLS4tkeTTyidGUtaF\nCpMF2FYhbEpunLynDEBaUkVx40x1RL7LmaL+qu539YKEezF9I/RypfgPafgY\ncVznaGSylTngb2o92qP5C+EOUqX73od9PFHi0yx43qg7TOJNiJZFxlU8VRg2\ng07zh9Zq0zrFJyVj5MOhODHl7PI+TjkwrIY8Rs5bdoLtJ+FI+JLLxvimIx+C\nWUZ4ltctuRwACnrnjrmk+rWPkUukWmjrrO9Rrn+tIjEzTYyoiz/pR8zkIODW\n5SnQGi4aJG6uF5USKey08+Tt0asjFdMj97Fv99x25KJmyuWko3aY5eLU+UJ0\noEjyVmDpPklIttd1V3gDzx/upp4yi/KZPlKywaBTdtSXjdkHI19QWcXeDxol\nykfMBT9m0Qe+B37ZQlAsEJ7+wYTWkuLyU0dIOcYlNpEFEh9KBA855ABUDspg\nOfc5r1RJg/W+UdUdZgexTZLN9giHaDTqZD1UghUcAmhJcJMAdaaJ/N2uLNai\nAS/FIy/RPr5EgrcdxYLMSGLZfjParryweP0ZAR5+sed2B3SFSmtGjd1hQ4bx\nzSiHB7FQMsRmN300+8F00vkkTKV9TAyPsC0JZ+x23xWdSVNa3SbReuQ3y09t\nz9A2\r\n=4KwA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjIMDu8GSCLirAKQihFnbRrV210NiQdilgbZQ523MoSAIgdT5IuWDlEzB9g567vcn6Sg1RFpeh8UJ8aIIQaAcszLo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109_1537095861838_0.9209575248198261"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-1537095919244.20180916110528":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-1537095919244.20180916110528","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4a34b37400720cff9b7abb7020860fdb1d83ca78","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-1537095919244.20180916110528","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Hutqvk8VlETNDdGWLyNfS4cVLYOxHfrFlaE5a+Ti0/rtpgRVwv2VtCEXzymR6yGk/ghXK4nXGcxnQkbutzNXaQ==","shasum":"d2c1c6c187719ee49f78cb8cb2530cb60790d5bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-1537095919244.20180916110528.tgz","fileCount":702,"unpackedSize":1579245,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnjnTCRA9TVsSAnZWagAAZHsP+wYEb5uDzdCYZmRsO/A4\nyH0Q43cLNRc4S2InmMbVvIG5ldD78Izh+ulB4kAN0uM/XB1c3JpVWmxTNYjg\nHey7gtxT2E1MdupFGXnBXyeqmltoKLIhZFFIn4pXSNdp2jZCY2v21s544d7Z\nnhsd3rzzhooDSDbWJxKLqTfXT38i7DJ8YZbfyTkjc9opsbWG8qbiHLSUYQsa\nwzgscUEdueFYzm1udeHXUDl4z4YiPmdF6jW+sRst1Qqnx47OcRK9nx78odnR\nlf882O0JnwXRwjn1HDsEarJJAVDE1XLzVal708dOtGiWtgg1uS259rMtxPrW\nk4cnA6wiav9H3zB/3t3MV9I/V28mz45Z8CQgZIVyUsetQ8Hj46y3/0b2R6C9\nR6NbdKw8m3PTZ+XRUnN3N/kJap85cXRKdf6v4B+GJOIvMSluLQON/BMESN8c\nrdKBaqsVMEX6Y0M+Yvb4VzhX93OD7rAO2hwwWWXQsZqqV1KXf0QyTXUxII6i\nD53Y8JsGmQGzjBJ0M32mIeTaAgPBsIh9HjltI4c1iTg1b0+Xoqd7J3XvAYoB\n2P29djq/MHl0oPJoMpuw0q6sQKE3ozgoYjo/xi5LqV5q9xTBy/74NwpTG4N/\nG69rqeT3L+1/F1eGqnGYPaumjNeIsZgtf01i7bbebqil4PtsQQ8whMF5apTT\nGVNz\r\n=beat\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA4WTUBctZt2H315ahvJzdXnLKD2/gRBG9O0xwfhlK6oAiA/7YD8FkBhnZ4eP6U+WnKpunz2bv0skU8Aar0IkNnU1w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-1537095919244.20180916110528_1537096146740_0.7838841542505783"},"_hasShrinkwrap":false},"1.0.0-master.20180916110551":{"name":"@atomist/sdm","version":"1.0.0-master.20180916110551","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6c327876a9b9ce813080b83afbe5e959ccbe70cf","_id":"@atomist/sdm@1.0.0-master.20180916110551","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pxxhRXHGD5yyJqd5+5x8/x076X9AA0bd9iJRtubsFzvzytDOeBpR64m/cdzYsYzgdhko2kCG/kbrrEROkHjlOg==","shasum":"4af395467da50d8c158b00eb76a0a19119b5d9a2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180916110551.tgz","fileCount":702,"unpackedSize":1579035,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnjo0CRA9TVsSAnZWagAAVsAP/20Tajor9Gr7s1LVow4v\n9GrNCq7p2B8EzCwkZiquHHrXUVFslab2FKnBNxlRxi6s/059C0gqSBlvMv3Q\nWJWaW0FkvT0y7x0bXQ7hAAL5z6P9teCgqRepaM/J/FKMgh8lbAO/ICtFVtPI\npH44Tb4v8x+TPP/nKrUn1+mvXgrLhBIENwPTUZblx4QFRXWStWNWX707U9yK\ndDCRP5gYhxkOhHTsqXh5uwhmemItOf8ZhifxjPxRDelXWQltvvs5c9gBwXwh\n6WyqhWmlA2Msiwf/jQT5hqGxfDDe1VAsBtVbVScObG1CNBDrC1d577kJ11/R\ndNxx4gynC64iTboLvSAg5FwTHGKkGhW7dgqSA9rxiZjAX6VYCw/BkWLz5cyW\nnrodZx58Y5mqhFalukIXfjNuuv9K+BqeJIDXy6oKOpAaFNCRg04gVbnqZlFh\nZbMpSZ3CDe03EXEffqRBqUk1ZsYcHM2ZoltbK1nVt1lw8JryRjf2j6DUJXWI\nnGXzkC+2lpoCf+GpZ66ls+hSLLy4IShlQUOmNSE8HRSKgeSf+/vXbwm+Itm9\nPUMnYgemtWOvQ8iE2xWkH3PodZCwDo+JUPBLrGADLVqd4pvygptNt9qRBgi9\noVdfT05B2q1qhfE9UieR6UcfzkUSyWtdglFb+y3v2YZ+TOu7AatYPpKxNxWm\nI1tG\r\n=24Om\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEGIJmOr8SCxoqh4yp134FbbFlGYHQw9/6E2+kEOx+fAIhAPuX91EBlZZ0KBlcSsGWPuoIajRM/mIO7lw/WwnONELQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180916110551_1537096243309_0.5733941753478868"},"_hasShrinkwrap":false},"1.0.0-master.20180916111029":{"name":"@atomist/sdm","version":"1.0.0-master.20180916111029","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bfc85a34995b558ed07d5f864bc071b3be56f015","_id":"@atomist/sdm@1.0.0-master.20180916111029","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nJZP+p67lUvvtKKkIZFHVIaxKCbxx7+V1CJZel/5BaW0BfrGEpdcU7Nx8lMsuuLNtMVWMZUjGwuUVtDkgxZsNA==","shasum":"158b4ce31ec225ec3946d5c8ae87a47c2faa8d67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180916111029.tgz","fileCount":702,"unpackedSize":1578860,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnjs1CRA9TVsSAnZWagAAkmAQAJ0eu/8bXfyiHWj7DNvd\nQaCv8kpZAzSCXle1Ski1CzcXr7TLpZzHemttMo8p5JAE40fgsyvuqUCxAW8H\nhbhUyQcQ6l5gDw8+mOC7GAzU4BL6acQtmTMhBQEPf3e7E3IHKauRYpYQz5Qe\noYd5JfRBcsjBtJ+jEVNZ+mudRhg9gHbVlJr9p3XOvOHhJUK5DRDXi8VlpVes\n1FAoI/+/12z9JGBAaa0wqO3QmLoEWn1lOqoCrb7hejgH+2avMyDu/HUl9qZh\nq/wb5ZrtDmJ5vi29knGfkuYw/DEgIbRwP9Gsx1zl+Jl2pjc1XMsYjioiX3IT\nBCoX6XkfsZNlmDEJkjLt2CU1MIejKlUCo2LNbIbQhvH/VD51kcJyqfV6GX5x\nJn2qDA0WdVXRBwK5hX/kMDu3t0HxIMC/UlKLwupu16WZapuKCmaOb30jaEYm\nlulvyFyQh86zoGt9fzRpVU87ZXxfFYM2dtk9Sd7d+vIzjpBtUaEghgStRWlg\nhar34TSsENywsnoTc6YmDAoXICFXo5i4WNFSvZtso5gnF7C4r0VypUT/JHR1\njJzWUyhwOKpvidXlbmfxQsbhFwGVQ+/UaGYrqfznFMMqWGhPurGvGoKtl4g0\nXZP9Vw0mCluXiX39cDVBjRgivozW2rLU0quG5hPje2872G7WQxhO7lskGZUh\nOTC7\r\n=uiCw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRcyDnsikJi4j4h54vu3jIbDmzxohy4OBxsQLmoiDAFAIhAJkwv/5aZ2eu7akmdbcWAF/zV0ghofYdOSmfuEvhlGcd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180916111029_1537096499852_0.6365355406005173"},"_hasShrinkwrap":false},"1.0.0-M.4":{"name":"@atomist/sdm","version":"1.0.0-M.4","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"gitHead":"bfc85a34995b558ed07d5f864bc071b3be56f015","_id":"@atomist/sdm@1.0.0-M.4","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cCa1bqqBY17aVMo5hCEf0KrjNYgnmY8a7Cg+d//BtTpG1QvDy0n8Bblx8WR1ohd4x1Qg+oyQ+sqpWLCcoaP2xA==","shasum":"abd64b81d5f52d52ad85af4dab27b79095cb7848","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-M.4.tgz","fileCount":702,"unpackedSize":1578842,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbnkHLCRA9TVsSAnZWagAAzKwQAKOfug+SzOMCWQyZsHop\nYbnC4sfGN5xMWjCM3a69MhSxfOG+ALCm5fudQ8o6iP68qj2skIM+8WqZHnpj\nbb8vEh/MmOw1LLznZzK02nCnGAmUl0nU4wuRudMZGqfrRXPORcEc463M/SYb\nE1DqDS4qr/E4CNrX+liQk+ozOsMd6Mltku8iWfyiybm/xYplyo8C01p08Ffx\nTy1zsojIKcnKUcSjTYBNfBQ3V7vcpo3L437tmtZTtcBRBMeXGY7I81/JWOzn\nfi+uBRX+MyTDN7XQGAUw6wGpgGZBOjCPG1FSyzLycTu767B0Q+4rwFjGyRWz\nRCUq8cZjmd3hKJFqFG+tqIQ+wKP04f1c9/9skHBucH82J0TLMc1qAroZRiwI\nl+v+6Yku4rbV5U+t+y+bRjz3dZQf8BH0Epe7ALfcnzHrLG8dyDx6Vy0vv38A\nj3UvP9ooNWub444kwjv8LaeAtZtyfvtHcnwXsuL+1qV9UViqsURDcJCT013q\nTkzCjwWrgZ+y+uJGEpWpVX2L67NFLD46iNtqYpZJxjES86ADNHnby0Oka+M4\nee2xOKLbceC4+kxgPK3WT7kDU4Xh45nmPH9I1EtIb6urJZOGKc2Etk4e+uvZ\nk05sMOS5rWtLX/eFBmNZe5UhqZEiUQ9TeJGb6VaiB57BIFuMuPyRMHgEo5Q6\nAc6U\r\n=GwMm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCl7PhO3mbC9NIRzFx0Ief9nRXl+7N2Qqe/BtWg5LxaQQIgek+0F8bN/c5kpdyES9GHPq00JPSloJS9LH8QcQqG4Dc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-M.4_1537098186951_0.463530284940477"},"_hasShrinkwrap":false},"1.0.0-master.20180918120220":{"name":"@atomist/sdm","version":"1.0.0-master.20180918120220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d83143522a9d3d514938f9ea6d3ab8ac361a9faf","_id":"@atomist/sdm@1.0.0-master.20180918120220","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wvatCWQskoVSdGVDCTlhe4596wdJXEV9JagWDu1yOYL2JpQXzVpwpvFC7Lrq5MUyUU9slx3UadTP7Qcbm89eEA==","shasum":"17e0833cabbf80b9f90ba68192de9127d05604f8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180918120220.tgz","fileCount":702,"unpackedSize":1579604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJboOoXCRA9TVsSAnZWagAAhNkQAJ7YKatkQhlPVKSqUpM3\nxIwH1HeinUB3vmJxVmgAqOi0z7NC9vhcU7CJs4OnC8Yv/Jv7EiHqV2pAwjGf\nL4/Gge8LG7/NFr3teR2cwNj/K0j3+RE653ct+wlWDskFgRhAD5jeOxmP1hCi\nsZmvzAotSy7kt2jAKC6A/+WmdLrGdMu2HifVZFtyqTx90wuvp5sO/WvX4CUS\nCQivMWerBSbB7alhUSyObgp2wiFzHwsysYsvEbA6FegvRQMd6tthmHmA5wJF\nyMhPBrF077uJ2dhinGbfe7OEW7/2CPjGoPXZ+n1T+jJ8OEDqsZZcfQKMNdSu\n+eaAkiT6yV7z+kuxxp2v7kSrAEsKcxYtCts4o89RLRBbAQe2iM2e0ICdN31I\nMoKvVKlh61txYECSttLfhxxmSH5HpChxctYK0FTDpMP1pQYelIVNpSpcuJIO\nrReZFnAZVOlesQ5/nBabhKHAhnZhTcgHM30ZmCOx8JtHS2B/SIRizBY0EPjm\nC8t/MgHVk1Rghx3cKVA7AO0Knnme8m14BsHVHs2fCJc+3hVKnv+NDJnT/ING\n83+2pLbBCXbj+GDgMj1QFZLG+Qj+YzkohcLXo/SVzB7NYow1d37lPfkbYT2O\n2rymFabgj8Not3qLRNInxSVo6V30Mu5gO2lBo54w7pl996m5xpFKAVUlCI1p\nOs1E\r\n=DpkL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC6QJETo1FAklqixwHIhsh2/w3/7nxHrx2e0mzp4X/eZgIhAOmdEVlzHDVdu0LQt0op3QJuD6c4tZsKKAXMfkaVtkKa"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180918120220_1537272343097_0.8438921716193897"},"_hasShrinkwrap":false},"1.0.0-master.20180918121412":{"name":"@atomist/sdm","version":"1.0.0-master.20180918121412","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a2a925f119d9005015db014568f798cc586d1fec","_id":"@atomist/sdm@1.0.0-master.20180918121412","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-miudAXhDMIA2UnndB0bVMT7S4eqCnT6b3lB767lU0K60BPYFoXYE8O8N0l3oAHER/x05yWjq4fta9oKZi5cBYA==","shasum":"32a684a87530215cbe5993d8a86d27ae51fdf1a4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180918121412.tgz","fileCount":702,"unpackedSize":1580258,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJboOzYCRA9TVsSAnZWagAAI0sP/0kfSW9GvVIWMBev2yYw\nd4XIrKWiylXJOn49ZHJ/Xrjsre/OzQcYRXSBGSGExEypXeCzbpoAPr+j9WOr\n9dhOp4P0r/kzkuGK9LhtkofPk8At8ia5sg8avKKmdxmCNPZK2Ouaq2bT+CbL\ntLcu+0/QENMyELKHSj9U5wPiGt/fkUBac/OfdwbXrKQIyB8hMRbfqjeUYBWb\nwXLhVQZBsl6fBAzl7IWcQcaTkmHvgC54rse6NUNJTu3mO1WJT4O6UmQSAj71\n+n3uQ+ytHXla4UVNbegA8DWlByf015sd2ep4uVOnYJSp91C7Ut0gUIzIZhfS\n1m+Q510Rko12JN/WKqJB30tdR7WI2wvOk80iKuIzH5nxlMukhfogqADSRp1/\nYwhK5CFMY8sF2i1UTET0qENVOltvxyhkwvJ7wuzBuVfisS0GPiobNT++b6tZ\ntdp4+13SHyPxIMu+Ahw/hVWndt8K7kpDSLuUDCqk8k6KdFFRKF2wg7wAiuoH\nBOAjHtkMNaI1JMJgLGIBddMNjN/Pve7fOkGSXEnr+wcUX/kooCYHQiugGjYJ\nS5mcLniNhBg5RzNli72ZfQDGL3T9wSU29Jk7seQkWbEPnTM6093HyY2c97Ys\nwc0AXZYm2udoJAvAsnGbOqEBJ/61xo48/8V9FWKSWE8AeGWNKVvbHylLUGuW\nuuCp\r\n=xzyv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB8RT1eFgYO+OCuTwRLROuXcT/v8EjKDqRNjfj5eHcLRAiEAsohEIawoZCV+JkezpJGpji3ALXLymZMGhaLYjb5Q0vs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180918121412_1537273048150_0.6316651955823886"},"_hasShrinkwrap":false},"1.0.0-autoinspect.20180918230735":{"name":"@atomist/sdm","version":"1.0.0-autoinspect.20180918230735","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e0b6a9dd51e78aec08644cef1025e9a9438730e","_id":"@atomist/sdm@1.0.0-autoinspect.20180918230735","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cgAk2m0zj471+6Ch25pks2rBt1WRhQ5OxVeCjObiMlSIxqImhR/9srQUJkXO+2B4N2R2jkWCbamAmZJzg4OYLg==","shasum":"8d9ad58fc05e86f99cbbfb8b62a961f9890ce58a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-autoinspect.20180918230735.tgz","fileCount":702,"unpackedSize":1586260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJboYYBCRA9TVsSAnZWagAAr0EP/jnpBK1lJW6wVmq5vNyS\nW93sSVMMdKfu0uLOG0Z5ene7uEqjLT44uU42U14oKj5ZicHmDHIyUfFJLUDb\now7pj/eCoJRskD3Jc9SAVc53DNtl97msiNtcVvBuOniiZrQJltt24hKG1bg8\n0b69PLmQlKovfvBRYk5IRhiVpVdmcoC8wrgcXOzEzaauIkxYdcjHOIxBJfI1\nQKC2J7cTLs6x5RYoQK70tRyISJ39Q4zlYn+2c+u9fpZWz7/twGXanavulV9y\nf8cg566ocJtVe53lJ3ZgznDaeqh/wNTnz3iglpNa2I7isuw44Cx3qwxcLtPg\nP92Uzeh5hbPrWAa0uUJomd6LmjR0T17xt9AsSnqzZ8ALvq5FO+ZamvpS4wpd\n7FuGZ2EA8Rw26s2Puiveh5pxjxedD1F6v/V7SmqYHkQ+PA/rOdahrhYDJFRL\njzOUuKZl5yLNVTmSp4W0cq00raPH3LOaepRUt+oZLV6zv9V8Gxvm87b4HddD\n3E7KUo/elL9ulxqKIJsSgo0KtyKerjBynxB+GSqrs58dc8GQfGlN5iTQgIwj\nXOZP4jTOc3QGjEYjvD5d6Azhpyr4IKwNBnnLcLA11kYXBbJxb1IElZmL53im\n/o5ZJQe7CMcS14hFNZsZxQxDL3kSKZiBWHtM3UrrxtqpHoHaM9UVKA5By7Vs\n6t/X\r\n=f7QY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDOqSCJS4++DrEc5MG3iHNwTHezyRFsLJI1USypQDBaJAiEAuO1EM13AL0R6JBdu4vdngCYUfHAtUetij+WWC1lvX1g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-autoinspect.20180918230735_1537312256833_0.8995848206686539"},"_hasShrinkwrap":false},"1.0.0-master.20180919001918":{"name":"@atomist/sdm","version":"1.0.0-master.20180919001918","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd0e4a450da5c32aed164a12913db6d726430251","_id":"@atomist/sdm@1.0.0-master.20180919001918","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SnRwcaDEuWJLtmT28xZnmegOhGubohmlvcaxIAC7r+cAwKVdxealQUA6hseRUVI6sas2o7iiZX9sElKlqIf7Wg==","shasum":"621af25e3fb1fece1243beb1a1a8b66118956d18","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180919001918.tgz","fileCount":702,"unpackedSize":1586408,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJboZbPCRA9TVsSAnZWagAArEUQAJNdeFu57KMiejbixMjg\nT6o1T4G+jS/5x1aSeu9DQSEBWMtP36dchGci/6NlIao8JKQUiM0WGQ5Y4cr1\nmKyDcptaQfQ8Bz8kbFx3bJ51LOtxqOy9iYRHr9SpfwdJLvkexKMQzwuiE328\nOvzLJv+MWlCzILdO1SmvbUykhfk5sQEk5q+SvgAg7WnXIVBu8tsUeTFloPHq\nkxi8GhS9I2DA5JEKaXcV5OdjezwrY3fTVKU243kmp22J60i53Vz+LhO13GgJ\ndrei1KBLFGR5a0/MEy1Rg2QIGqBudirIIei1poxiCu+dwSdqzIVIyMpoRSBH\nFv//E0JayHNI4yyD+BMSjhpu+BDFCm00J9WuDJreqhXXFWg+bfhwMbzYkR6a\nlbZiiXjzPt+5sSgZXnKPam/GLFOv7qz3w/aR2oxCy8Ew6i+mN5kINqDULRje\nmRLFHCCXL1BSOAZyK8TE6VTdiJXvSv+r5tVybptUteWeO0axlSuOE9z8xOCw\nGaG47P4Nw7eUm0s97bdEj0lPzX9A38PqAsaeEXHyJoSXRf8FZDybAXFMBDFA\niKCrhvmPuDDIxpuuuehaJlcGuutdFtjCPUfFsCBvleSm0EYVRm4j8xORKOmF\nefOClZYArYRpdHcFh2iASMiWC5+fWNUmr0JyHZfnwydr61p9aLvNYB38XL8n\neVG/\r\n=EeHU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE9nt7fKWPtk2vMEYoFbigqgk84O04t6AtqXFzDQPdXpAiB8SeLEdLoBHiNr8m/hubEiZinzt3wEC/oAwWy3oP6CMQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180919001918_1537316558951_0.727825428376895"},"_hasShrinkwrap":false},"1.0.0-master.20180919030855":{"name":"@atomist/sdm","version":"1.0.0-master.20180919030855","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"08ddb76d9270b0d356f721f4ad31f3d44cca48bf","_id":"@atomist/sdm@1.0.0-master.20180919030855","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fId37uwI79I9F/29YPRBVZh+olIVgJ+0VltpLoN+J0EPD/VO4E/NWCrDBphSVq6uRnTjSADNSU5BD2ZAz5fpVQ==","shasum":"63c4ca5fb2c9e9a937df94e0edb0f47ebed98aed","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180919030855.tgz","fileCount":705,"unpackedSize":1591352,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbob6LCRA9TVsSAnZWagAAWc8P+wRyxrlkZeq+1y58MTUI\nTPBEk52uRrQXz18Aev5XYOF1AWHLMqe5W+JrwjUxcFz96Drer4EkixTGdkm+\n3C6bfiYiSvuIHpSrO8zizZjb1i7VMSv+/x9Z8z58UlfC4QE2NHlTHyRD7COH\nW5m5F63Pa5yn8+tHXPdHBfT1AUW6Z6lq7rYxMYsHQf5eLXNT3ZtKCo/stP/f\nH4me3EYtXJB4DpW7qujn5LuMkCQCvB4j5LFyyyLJBuQB39EG0sB1ZjWsYA9z\nRzEX6KD8GQdcLbfcMdV67pBDVFKRuVcdo1iJ1x54tKM/bDcvjeKEzjrr8E4q\nwOQtekwqoMe4o4w32JwhwGoVYAiU3cXOKxUKzehr5DTW6O0Y04HMVN0L14P0\n3Any84HXzcayyyV4mqid2o9a1AFUdF1NBZJUn9QLVNTfida/wifF7nHz5dlz\nyfr68KrXKhdXnrwNWyUxM7hTPG9yYutnnblTTVoW1Mk5xi2SrkUA0IpAzZci\nnuw5+gKgLIQ7RT8TIbCTNwMGRTqUOcj0MNwsN8LOD/O3p2aPyYBF/M9wrOAA\nFNpvAO5dn6uNaBp+O6YBPSDB83cCN7Fp4HRvam2A7GofSMh8hxhST8b3tUmK\n+yGta72BvPJ+TH0BHPhP8DnQX7ttI+AFzg9WxRzv+jnX1jWKDVnqQnnqc957\nkus5\r\n=yF8a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAfV5OBmfQJcVE4wOZFnZU84zKa9RZyG+5alXsjkTbBuAiAmZ5i+AJ7lfgfdpJL8Xz8RrriuK4jApHKpekFLnFtP5g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180919030855_1537326730490_0.7832318825996434"},"_hasShrinkwrap":false},"1.0.0-master.20180919071807":{"name":"@atomist/sdm","version":"1.0.0-master.20180919071807","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f1ce1eac639c34dd51f9c26553357b72508823f7","_id":"@atomist/sdm@1.0.0-master.20180919071807","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nw+aHn4av+aciMHRl0f5CY4HQufpWfXQ/DlWIu/5nYIcTxzls+jxx3ppOFOSEtcExt0AqkRybGN/IhVBgelojg==","shasum":"d48c350b1779de719ff7c6d25fdaafbf6aba8ba6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180919071807.tgz","fileCount":705,"unpackedSize":1591393,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbofkWCRA9TVsSAnZWagAAsewP/ibg2H4jaGEl/ZJkX3Rt\nGW/W6bmduxkhdcZZlaWLosMderI2R53v87Q+jJYVI7Tgt6FQkL9IpzG/MX7A\nc7vBFRkU3sT8FxVA+NLVPGunhsSPSgvuYmBbPOZgf3XHTEVBKEpGGPjWNDNx\nRlz0DBrWXdocAuJwVQWZ/4NzbT6hxJYzrtta2unNHGR3LKZRa+wdyDpARtY5\nkZrBiJernZ+9+IkWZ1WfOBTrzLiXy3hFvZ3upQbVa71g/s67GCYQijAosi/n\no+yQMt8xNbx/zQoNuR31ycoYxEIy7IlRzA35nSvlJGWGydyHz9ShBb44owcq\nkSGOlGSG56QoKSuGRRRnAjNOLWLg1mshUt6KVL3Pyb9tpfNvhM/N8EN6v9AP\nngAv5wg3SMpoB4Ou0v+r0jTSZuzMdsJh8WQW6z3EmMBC78iYJDUpPkq/g6h8\n9X+beM44eQOaQYZzLBkA3QOWlOn4YTrqAnbVRoJ8xApazBPvgHwi2CMQX93I\n/VUMsfqzkft27jnSXEH6Hl+AIhihZgoTcJKqPHVZvlHaDI9kCbSjcvd9jB36\nAfs11MJh8SlSbZYjDXJhK+ZKknHr9BaZlvKn+/UXiI8gLK5TGxOStDvmDUWc\n+5JZlXXjepqXtKQ5/WjBY/kdAkOiDswY/GtG3B4Q7JR3rkf9dSa6LS+dv+Yw\nONJE\r\n=R65F\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGABtEnC/KrnpQ8PETThLeE1he8ZY1ljlcnZAaaSvKE/AiAGD6w2T2hF3pMZmlhxjk4JEGEypfDYpfpcSmVzoWTIgA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180919071807_1537341717316_0.5657009524569445"},"_hasShrinkwrap":false},"1.0.0-master.20180919072129":{"name":"@atomist/sdm","version":"1.0.0-master.20180919072129","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"36ad08643d2bb25852771280727e4abbdf7ce828","_id":"@atomist/sdm@1.0.0-master.20180919072129","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-q8ExI1mbXxX2584Ixbv4p+IY4UI5aKXoX7FKw6Y69zniiutzUSGgB5WLDT++atrVsn7VrqbKqG0fKSRVNbFkEQ==","shasum":"dbae30f57ed90ba885be2bdedbe43bdac6ba8d36","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180919072129.tgz","fileCount":705,"unpackedSize":1591413,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbofnrCRA9TVsSAnZWagAAOQQP/jYNbEMxLiSVfT1zT7q0\n9t5Jlzb311HRRgj06VjOst1N0e6Rx+zYG2c34CgeFw38/JHhDdyweTwgutsJ\n1Ck/NGakQ5Bk6MeZ7Ue8CffxtMYrIOdQVcun2CqrvI3Sk+FLvofFrqwsWIH6\nJKVhV1KUmSjldQ4gSsZRJCuBwpQjQyYRQk53iX0/yJxJG3AFB16ZfsVkTTia\nEj5+oWXtK+Ai1wekGQrmCuZURffnp73KIqfSTDu8U7ZUzBPdO3svRMeOyzxz\nqPs5/oiQYApMjS855YIBWPECap8HoOs9jw5EEFP6OiMT1gqXOnY4RULtNn0G\nX42+O3T11XxsHr9VBQB+x0v7nXpjkDp5mbH6Ako3QhhgNUoyPfqkQ3SlOjqM\nzdumoyD2q3+hmlO9qRZSmNqB/5JKtsK/HsdB9BeZOJaLodADeffMEHnzYeCZ\nHGIn0Tctbcti38mi8baRuJM53wOmTG/WIOAwQF2HtS0sF3I++KLLtLdzvkEB\nvhG1bIo+USmMFncnUm//XZexUYWGz72H+z99dD+77Szlgs6l660P10kauwN5\ng2LYJ1I4zq+Gt422lCo84qyiX3aVBaY4tmo4w//VD9QCIOxRRY3oTcqCEAQT\noM1GCLxMWhMK5xWHWCWmJCTrAriuL5FPRInIoc/UzpOW6ILHrj+qYJ6rXU+8\nSbMP\r\n=AMXH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEpDzUrgW6xiXZGKz6CJ/Ir5YvCcorgi8GIc0JAwEm58AiAc4JM+QK4610VxtpGIdQU1XwCay2aqR4MBcmNDQ7rcmA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180919072129_1537341930287_0.5772432528665143"},"_hasShrinkwrap":false},"1.0.0-master.20180919081349":{"name":"@atomist/sdm","version":"1.0.0-master.20180919081349","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"28e19cb6e52940760f64c1ac25d6a8d698bcb0d6","_id":"@atomist/sdm@1.0.0-master.20180919081349","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bXtaYeRMkfBPjb3iTA4VazI1TR0oEjXTkh5efPk6dVHjmvehMLxNo5JqTt+dls27UwfGZhRL0Ii1axgWZGrBlw==","shasum":"eb4c2b48403ddb46b6f62cb65d60ef07e76c9320","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180919081349.tgz","fileCount":705,"unpackedSize":1591559,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbogYdCRA9TVsSAnZWagAA0J8QAJeRgtWgGM4TVYZhIj4E\n2BJsly0pTB4/CB5w+Ly0i0wmpF1GmXhTK4kcBjJgf5/DT+k6p19Wn70cYebd\nzoeJPYtOQY3yvsQzklt+TZgE8t19kPtK4yePHszBB2sijIaxMUedXMhUqKEg\nREXNs4veYAc2PxHKq5KbivVndBjotEuheBa/2KbcM6uw7TF1GL2auG47qK00\n4YtcI+zepySdznepKYBkDnyOUXtLG2hgKjUyW3PoEaEa50+ebyCnufmOzwmJ\nIImKdjQuXsqD4+BFWcJENIDmfycdR2+ZNToZ8BCmn+T3SqKJPLXYONnewKBx\nmQCzPAlG6kN+40VSP4vDL0fZxz3GU/z1ZuGKaLfi9NEA/NZR+gsuj1Qua5IM\nVjejh0u5phK5mEhiZVdos0M0azj7u287G9TuPg3/GsT44OES9/+dnfmfLoW/\nQYHzVecMFamEJJk6R5xiHmagngv27NO/IqCJrYmFJENJil9dKB6uLJ42ngmu\nFq89/pSQwt8H/XgD02myAlM7OSKN7FW8PNZ4TR0QBWv8OXcE0T+RVgeJhnUo\n9KgtAbEgy3HHsp2dgpdSDoKd7gebAMYepOXpJWLYy7dzlt78fEJVzJqLVUUY\nIEbQa3NO1slQBJEoCHEsWhh1PD9bHu1xRO7A8Yv6MsWOLSvvDvpikhGdy+RB\nWNEC\r\n=M/kg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICqbwEsbJSI93aWfQYEnMVvyNrqxa3yNLZ/HLs7rKsPiAiEAgzSjsuFERDUMAi1fk2i/k9HVobKEWsXWpCsI4/ZEeXM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180919081349_1537345052619_0.6033277760889817"},"_hasShrinkwrap":false},"1.0.0-new-goal-states.20180920085246":{"name":"@atomist/sdm","version":"1.0.0-new-goal-states.20180920085246","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cdb3712c5a7c879a89b4b6d296c3ba3694d0e3b8","_id":"@atomist/sdm@1.0.0-new-goal-states.20180920085246","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YfxveySV0M5v4FLJ6Wxac9Bho3zyeckG9aNi3TeoY+EwQ+Jr8uSHDDtJitfzZaK8CRwrCnoFuHxgEiS0LAfgnQ==","shasum":"bb3320ccab80464782ca7e5b7ad974e51fdf60b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-new-goal-states.20180920085246.tgz","fileCount":705,"unpackedSize":1593228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo2CrCRA9TVsSAnZWagAAdl4P/RbStR9KTogwvak6pqUg\nfChKQzVZgcssv6XDnsoear+NBx3nDQjsbj6c+ulo0b4caRuuFnXPqx2BEMcm\nBPkII0YE9AvGiCwhihlLpG1qh5bHHm/uv63uxCKriuEV6cKm8kA3gP6meJ4S\nThtx1XSc9gzsOzwUikZ4rK0x8Sq6OJBseYyjJg5yrIYiG8e6UddFLp2/39et\nXgn63YsdXHqUjhG+0+4AoihhZ8Ti840NGrOb92p6P1KpVl5B6PLG3Ldq7L4h\nwkbQ9c232j3hGlu4uJyp5v2a/g7us6SxjrJRuKJQLNRbDNKB4ruwLoMoiJtc\nUZZHoFcCMOpwU5DeoOZxTsn1in6LTqIm/HXZBePvFd7GK47WmzYp67VLAwLN\nynKoWyYruYj4v8XX7y51WE9JBpcrwbrGshE3IjNVhZI+BncXw1a2GmhWsShu\nu3dQqTWCKQwdgoiCo3qPG2KeY8HW19N6yqz3pK7QrDMG/vU6pq87nMoXCFEf\ntuDptiX55qf3eofrUiXQZ49wtv9+/C0j1A/ckKSMQoXbhJU6SazCSw1ePogb\nokZdo5m3RRQQFIvjTleldP5ibx/4Lz61CSE57e0qpX5uwfZu/lpd57zeP5oq\nruEQB6fTNiAi6ge2XutmH93dKdwAiGQZi4SkBUuTDqluQxourj3Zw9aD3jdl\nRBW6\r\n=5s4t\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEatLzD9jZOPJ62JLoeQrnTenawdGyL+tAIo5UxR6bu7AiBeXxn0TnzecYkghod77mzIGbPpwm6LlGiSpX+r8M98mw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-new-goal-states.20180920085246_1537433770285_0.5616490243855405"},"_hasShrinkwrap":false},"1.0.0-new-goal-states.20180920112504":{"name":"@atomist/sdm","version":"1.0.0-new-goal-states.20180920112504","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"80abfab66b448379ec331a91723d4d7bc82cdf8d","_id":"@atomist/sdm@1.0.0-new-goal-states.20180920112504","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Gtzoijyckcbw/mkrIkd1wxwZBI6xaguuZEz74CYPz1F9GGxum8NqQ8iiLmIhKPBqoX5cbFrCm/XRqLjzbasGew==","shasum":"7b00a4082fe2bf73e0656b0ebbc8558c16947c6c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-new-goal-states.20180920112504.tgz","fileCount":705,"unpackedSize":1594566,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo4RcCRA9TVsSAnZWagAAx9QP/3YeYjwVOrFWhrSoLybJ\n/JGRVhPxr53CbRS3X7AtzXAZJ41RPCpRN6cZtVqzFMKS2PUJqkl1T8xSLVom\nFVAc9Qg8XKaUN3yldrvETQehyLDcnih9magLtcmFQoX9YWpgvumRm5WdAk+l\npsf4ZvCoY5aoxeGIQw89/TJ1mK6Iu0Vd4s2DMtN+I/pv0nDJTDOYrcI+oWJf\n+kyxwCU/SeYVkBgmHBI3oGW9R6Q+u/kTj5plHtXunQg/oYhzvx7Evl6tseYD\nOvV7Sa5fmvAGNTRM5JPqf7Smr13SPUX2T8tZB5IVhearcIHbiTVc3uHqDv2p\nYzlZ83h8vQFU50KUyFSzWYu2eGbDa9gPWEE7uiXNsPB1zhQKaAOmJJ8gEyhz\nYvJOILjJ5QRDTA5uGWmKFPn8nD4a4hBgH1E6OjmxT1o9vU0ESlo37wC080Pf\nyOQGPt5i1JnH741+Na/gUypwWAI862M+ZZscV87tbiwHJfGVnfoMHTX1Pqtq\n6srA/uzNpsttrU/slEGtiB6J23VMpYZk+0aF/7SK8U/r0LZGUd7aEpshQ5L0\nXVbbppZkMuEp9fZW9p4DLJK3gvjjqNymqqs21ikI9MNmbBBrnemPUTecaeLF\nuhDo2SAmzdFCvPeHIGwBYy3dqT1bp8L5EPZM5xzEnemNOd/eAsQUXoBAEtsU\nig9X\r\n=Cyv8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrVt40JSOYaBGsYByuAveXYYjLbCVDGo/EtO7FBMhm+AIgURtdusj7ROH6D1LUVck3NUsqkYZM05YnSHk90pUjaIE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-new-goal-states.20180920112504_1537442907832_0.145965104732247"},"_hasShrinkwrap":false},"1.0.0-atomist-update-branch-master-1537443790870.20180920114318":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-branch-master-1537443790870.20180920114318","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"74cfc8a8a2681e645d3d2b73a4a6c90ea93d531e","_id":"@atomist/sdm@1.0.0-atomist-update-branch-master-1537443790870.20180920114318","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JW9PQRiZA5F98uu67hRz1Zx2qwqI+ANu2QmCKcr3A0rwFB9XjFQ9JJ8XON69bxHu2fOo00nipBcTg6JfmMXbqg==","shasum":"5160b63cb2135c244d4a02e702a4bbbbc17145f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-branch-master-1537443790870.20180920114318.tgz","fileCount":705,"unpackedSize":1591635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo4jtCRA9TVsSAnZWagAA1gYP/1ZxdfsZVQGkySSNRGrc\n07TxXGmue3zc7dRHfyTwDDtkkFYUXiHYBau+0EyOUHe8JM0iibuDQQtgKZJs\nt6CBn9eg9AWlMF6uvEuMhp0nd0+C2XJsau5f4XplzOza3ClIDhUSLrEBZ4dL\nJRV1pbfVyhiZ2BiJvIECFPamTDZv8FdyJjRvo5/DYKyVlnglcdMwwo6NPVcb\nJIlBIoFa+GWIWywVLyRuuCxRnjQS1+mpbQRskiNQ9aq7oWTOSXlLWBmkagjf\n3G9bovcAuLtBQlu053ollJgHzf1Ks3PCjAR1wkjcb7OnkKoUOjaLxvzChy4V\ne+FclWAgWCkgIg4RQmDmLALE0k+u29K2Vnzpy9SVgBEopAd/NVUXx8M78rWZ\nOENzbrsB6gq/RffI7TlIgobZ88Og1oINRKTr5NghWiuAfNASq/m4zTyeeNt5\n5uF7Jzds62I+6tDJ3UumXUSL0e6RUWMAi/awY2aox/udmRRAmPJ1A4hl4lAF\nmB2hPIFAUM8KQUBenxI28Hmmz+F8sM1UfnLdIXJJU39Gmsko+gqY5yPmROZp\ngXQ2Rb8+2hVyQQnShfNAcHzrpLZBPjdiubqOBgAbPsNYCtGeGrwQIGp4l8S0\n8+uRJAXkphMzZO8wmrJZeQCE2kcAbHp3fHA3ZI7Xsb0Mpf7ipjSMUGbTHjm4\n+c+o\r\n=rt3+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCF9tJfrfYvUWXM6BGOxXIjBz1vZVtfnQAQTzf9592FDgIhANunDsLc9ycTy9YFVj09aMxwg5blFOZwfKMHQFHkzO42"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-branch-master-1537443790870.20180920114318_1537444076411_0.8781057314845122"},"_hasShrinkwrap":false},"1.0.0-master.20180920114937":{"name":"@atomist/sdm","version":"1.0.0-master.20180920114937","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e0626688d359404e3aa4ee6a53dc81674ef1571a","_id":"@atomist/sdm@1.0.0-master.20180920114937","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BjpOLXdrp3yt0LOjV+iHY/GebzOcoD5HDquB0lEUsQJA9YREb03Pi5BXeCJw9qiwouKYs3Oe8nJZ1nhXNgiv9g==","shasum":"81bcf6695330badd3cf025756cd03bb03d4af7fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180920114937.tgz","fileCount":705,"unpackedSize":1591919,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo4pWCRA9TVsSAnZWagAAntcP/18pCynru05zhC/hRe6e\nTE+fEayBBTiip4mpyBYLFDtJQy5DjIaWxDTJr/ZMGikceWVq04Vv0DWYxkDW\nAqw3R4wpdU181yJtCvUAfJ+eN7NpvR14IYvBGD95uhZLaNR6I0hGPxelBLWs\ndoretAILIYgMd5zr8N0RUDX63FaJARyBxnpXrpG5df6msmTkZjITp58yW0WI\nzs71tG9E8FM2E3Q3MJsjgNVBIl0QNa2NgxSIbSS5XUfNwim07uGa1SvBZjOL\nWKoA/tt5a2QP2QWagGWm9/e/jFxMK/UDxCTYp0CXUTR4Dc+uj/GC+Nsvo+3n\nVuzh4LHwqf2Y5Q9AoPc3+OpmKTPImZWBn2ucLDjFIAlanS2QeTmEcw7YkCHO\n2R5c8tMhOsoo2uIGlUeRgBEOjpuqWaDQ/cSqjaH20xICGhlXLcQQ6aRerr1y\nvXn55Eps6BKjwYBPdqoj83FW0PXaurx78YxXDhWHXvjg9Pj+AFA18hJ5N26h\nK5dLF1rD8b7g/s0XCJNMouqTCBdpKQquWJP1vc574rVAff/I2k+m8ambo5VD\ncleM1278Ox2VgPuGSqpmRZeALOS5Q4Vf7ohxWCdY9RRpfKIaFQXncTwkznaZ\nLEX0K5SRCh/Xm8xotNamIKHzxfftoiPV4JsA7d3BdBN5Q6XOAlku4OSYXUSN\nrxwZ\r\n=HHlH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF5eYb5pErrl1kPkrb6cAGocqXvUz9wGiULNT8wMjAJMAiEA/Kb1NWc4A3cRHQxtHUHJm2XDQDB3mVVZNcskVGVTLNg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180920114937_1537444437753_0.02956525822809919"},"_hasShrinkwrap":false},"1.0.0-new-goal-states.20180920125024":{"name":"@atomist/sdm","version":"1.0.0-new-goal-states.20180920125024","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.4","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bafbcdafb3f13a9d23bc781f5219d41c41d74d2d","_id":"@atomist/sdm@1.0.0-new-goal-states.20180920125024","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IgJ/NBNawsKW0nl4W95uGEUlAJwX1gya4vHpOuPYKPrabdr3lBeGWoTGDJRR3ku9CRN4jYwL0c8e77WuBcuNjA==","shasum":"512c8382d34acfb0d548952a9aba28c7d1a76e30","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-new-goal-states.20180920125024.tgz","fileCount":705,"unpackedSize":1594535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo5hqCRA9TVsSAnZWagAAvXgQAIyLAH/0qHcq8zY1GipR\nSIUNEGTtnTn/VOMzjymz+5S+bwHCJ1bdD2J6A8+n43ZUMcay9Jph1EAncVLA\nHuBq0IngdZH6DKd7e1EVpr6oXUKUjn5wKAAZhmhh0afRNUkK4rrD8o5nYlr0\nvr1tKMhc1xc2gqLx3CGVjzdaZspHj33OoQapJGpFSI23mKr7s1Go1L+3ubn3\nCSXMcNshPSgs/ICOLvQmQwB5kwrCfKqGsKScxkfXmXOvkHhNGM7c6wEp3TuF\n29QrzfUOwxJUuCq6vqykG62D8o2p5rwz2JYB1b2nfrqeR5bYAFeplzjLVmTs\ntyNYGq9RjZA3oFZ1rTvWg0ePAqNsQNon4Ji88l+NOUh8iagJoZYMyzGRggID\nKRJnMfQbeXa2YShJmWm9XQ6r3bnnymB5qE/dx3qZZJ11VAVhJ77WWstW5kHG\nbD7GhTteSC7GRl3CON4Fyv6d//5r6LkccbCdUQGMNvBDwCTWUH/d0iXM+pyL\nBxQM5RhgVwYpaLnZCqvooiRcJZvT3If2MD8Pznv9dGxygrFBSzfZwwyjcFXG\nAbh3yP4IYmKm018Tw02W+Kl3XFbUMZ6LaMrRQnEK7Xh+6jxPIw86ITjj47eN\nA8LoCJtq8+/cEHgU+1IUjGSFOwfpDisKOKNjvKxoycRL+XVMRigsp+l14osk\nf8Tg\r\n=JkLy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwIlo5sp1KfvPscQ1CtAAxQCGkuUBsWW4mLPzgmnAdWgIgHU4vaRYF5SFTsGn6f/osPbVrLF8XQBW/Gmy3/R3BdoA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-new-goal-states.20180920125024_1537448041122_0.2966823078388281"},"_hasShrinkwrap":false},"1.0.0-master.20180920125425":{"name":"@atomist/sdm","version":"1.0.0-master.20180920125425","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a1097375487b87ab621cab61b20b7faea05776db","_id":"@atomist/sdm@1.0.0-master.20180920125425","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4h7tx9PvSyPwAMTUgALOw8uOLzqllIR5Gxc94ozo9ixIDuAv98cFK1NTMzuPBTEL/ZxhgheWWyPDSrTX2gmhHg==","shasum":"6b94f032990ec3b0daa94d7f92ee4867e5839066","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180920125425.tgz","fileCount":705,"unpackedSize":1594886,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo5mKCRA9TVsSAnZWagAAw8IP/jtCbjwNCtqvK4AAMNSf\nUM9L2EQN/n/d+zw/lPuAlRSdXY4hRH4vCjpexzSPcOivpFdROH4pE6ocYMfp\nVCJJ71NGR5EX3xJDQpPIMsRwDPmv5nf/KuO53/pg7HwGlIoTQDvvvXwwPkVg\nmIzOba8xJN9v6McoXbG2TkMyYbd6W2Dq4uk5CSJbafK8hB2llk4F/sWR7tMI\nws7Xona/bmSzQOQTZ9Oyi7UWhfmInefLXKxg43i1DtAp6/96kks4uMh9O2DL\n4vdQ+KPtpcpAG+bbwFXI77oB5NGOf7NrwaTXbciIZxjQ5Igzgty6nWh8O95V\ngrWcwULHgrVO/uesJAbV4aXEjPVP2O3wyV+jB+FWUW8TY428M6lSl2gvpc3V\nGqD25a8hqQtfgz6iHUCtFsrqRXBKlufWRs1upLOmUbc2fTM+y+6VzEwvbMet\nqerUh+Dm5xYbaDcV7Lo7Hgrpl1DqqibLpSqq6l5LZGhvfvPnbqLCcRgE5/fC\no3dhUrxAtQIuzvZKEkkFc5qX0a5uME5oA9Poqorh1xj4NIDIc/XD5LZxzzpd\nstdncIp3k/ID6izEjnykS4Nxz2TlFVJEg6cKxRjrtcA4FQPRrnJnaLHeIDLQ\nZ0pNUNfycLeTzxqTyInEsCLDothRdgV1rEi7HedMGFZXsMtNcDMiVvZPxMoo\nQsGK\r\n=IHQO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCWucq+u67xVFxqBboSUxM75e1jrVn7+cIaLAPbfxHOXwIhAPW+bdaH9GrUcEMv+jDiq6AAmtLnZ0iBNUjfthP6KhDD"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180920125425_1537448329502_0.8081644245107675"},"_hasShrinkwrap":false},"1.0.0-master.20180920130138":{"name":"@atomist/sdm","version":"1.0.0-master.20180920130138","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"61a4438cff90ce1f04e132189aa45ed038f13a13","_id":"@atomist/sdm@1.0.0-master.20180920130138","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Uoonundg1FY2yeJ3gxmEuwOQ1z58xpn1L8hUc2coQmY79g3+Fc6Zf7NLuTEywCwbkpUzUsIVmwDL1XYD44J7GQ==","shasum":"55b3ddb6c0c3ed36b591aaab84bd4b3558376c21","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180920130138.tgz","fileCount":705,"unpackedSize":1594712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo5sYCRA9TVsSAnZWagAAB2IQAJzmhsHYuBrkFHOr0/qT\nHce8wv/xp/V6WInJ93KXPs4B8UW5Kl/V5yqmHbnvmFm80N4pSrfrvSQaTndT\naL1+1MMSy0HmieBFZX/Xn2IEZUDnxHO4WehJBG7oB6v/sSlqoQhcJkAmkROo\ny3/OvMHCyCCjuP6yBaxMAkEiVJ3iydd2i1XjRSSluux0gVOfn1K0JPJ2zyab\nX5SO2TgT04jRHRd83KxKnTAM2cpajqeE/sOFd87V5d08X/Yxrp4rs1qmIAym\n5AZYFyxfOtKjkFN3p7JiwwYkxgpBOCjp3VGzp83vJGbP1WkXTK6bsy3NQAz+\nWG9cLH5wbN61a3ZNG27KL6ZCvC1mm/6P82AQtZeWHE504WfkHDDV+wtHkuAw\noedlRH0t7RlHhnwPlKELs3Mni7znBZASCIyHok4GVv3XJ4aHNyM36aTgozsf\nM5Ia6ip6m7zVSO5xNL+8/UJVVmv9ZMsE2COL54I2M6xq4RCY9BFVyrKJVE7H\nCu21kuYxe3Tv8ayhFo2iSmB98jpKW0OAMJawPmCgxv0Nil6nUrxEUtWHUIwm\nrjwG5tJeeJWHp6miopYbjtI5i3ZG5uSOfiWvFo3UAWyPyjZUeLj1vnlmcSGC\njE4RRbwS11S9owXzprYMxCcP81IyzIuq8wpw4ZR4/7cqF0qrmL3dX5h0j3o4\nVLYY\r\n=dlwW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDfqTTHurHhsDtq/7QYkpiDcadH/k4SRZpy+mWdC+kcGAiAClVJecieFJYKLcseWIuZ+tnMugQliFdEFtKMpbEQ1cQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180920130138_1537448727179_0.07420003998124147"},"_hasShrinkwrap":false},"1.0.0-master.20180920152437":{"name":"@atomist/sdm","version":"1.0.0-master.20180920152437","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2ca0c20ab53a833fc4191556c849a069ddd5e9a5","_id":"@atomist/sdm@1.0.0-master.20180920152437","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LCLhf8MvCiliNNRQK+oadnEngkENwDGHKYeho4mU0gOtDLV3wsZwvJVqy50NhGlK9Ezo9PdFatUAO0a4r6SPKA==","shasum":"215ceb76c18c36d2ed9cf2771474d809506470bb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180920152437.tgz","fileCount":696,"unpackedSize":1581647,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbo7ytCRA9TVsSAnZWagAApi0P/il8w/6mY19bJZZghA5t\nBFh5TlBNF2fSWZDURb4FeJ5iirvAned/YuQwgovP+fuaD1qEKNfeRGuCjOYW\nyuBUSs9ny7WECvOfZP1J6v7HFhglq2qe/6D2a1iyT9fYMlSHiTUwEq6H3opb\nibsFUQwLxRAyKR0D9MYaQC7XCZORA/f51Sn30vlcw+QVzjznDvgDmkgrOxDw\nHYa47o5VcLHqTTZdp99caGmrThrOsWjbEQ3s9djJcJOM6G70vFPM6FHvHGRj\nOgbco/JxWuC0geOrHgr1FVdnvLO1zj7BRBLJpkM+xv8O0qPWL65w22a608Be\nzdm7/mjCZuXqxTa8PjFLLR1OyJV+YJ9pmcr+BP7ed/a591luewwucHaaAPiS\noGYBMNH8VibNfaqXR2mcKKq7orZyp52m/OjKWsPCLFG2xm1XnU/gcKbT3V+V\nzPRWap/kg7146DjgQtA5qlBnHEByNljSx5URxGD6DXioRYwLOOH5LChPU9CM\nuPjLVjI1qcwxeBLCCJDokvZ/p/LGi6/FE2Gp/p0dBLP+9zj5nt0tuGKOcFen\nKQEP0RWv1DcQoOdupscYJF+KTs5ib4QEabUJYG0YuoFka1CrhLuha4mxLgRh\nijJuo30N1oMEf0GfDwQrzP9PqzaYLu7G8EAAidLEUPM6dxwV23emZSqHErJW\nLZct\r\n=UwtE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxYyQE+unZPsECAiV6yp766Dc+z/rkihiS4QxH9/pPlAIhANpE0+9k7xCEN71N2pyHlSSMEWmCzjdXLviJ+Qu9zZ+f"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180920152437_1537457324762_0.17717993215569705"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180922193416":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180922193416","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8d2a0d220c9b691b6133440569db985c93f39fb4","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180922193416","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QTbrp893qnbo4cbuaGDwjCgpYfuvNZDSamqj1VQREgseAXQEbkcdG26ZmSF90AUH8xGABqHIuhyu3vt/Ztbn0A==","shasum":"8fdb03ca87577fbc3ece298699d01a8ec0ff3489","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180922193416.tgz","fileCount":696,"unpackedSize":1586955,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbppoICRA9TVsSAnZWagAAQKUP/3HuOUZvFubU+tr1QzBs\nTN7kzw2wyeOWKuPQVybpFtfYWfLik67/9xpGGN7Kf5wcJlDtNtof63Ujd8gc\n+tI/sHJbGYSgbXB7pWT3Q1nv1It85p1IhakX3265Z4EmoWXce74907BD2BoC\nWwxw4FuVNjCWbLgiaX4ek9zdmxjoyXgkJs1P4OEZ5YPkgm6tGVJlr8S8KqOE\n1ePoR3XTMXAS4dNcTBKvQEGkbbmDqp8mXwB9PAuE9WMoiB1X7yAAg488Ahet\n3kdmkl6HvhmZdc5ZbeL5S8Yo60oOXEI+3QR3ZtCDxVPVuRoByQ+MWJJs57ds\nkNyQJuHQmnVAgLieys6aiNHi22PIvwVP5clcMqnkya6VqTRzDucGHVqCIEyJ\np+zsykCsjYniTA3zbCYrpfC7BA81CfWSwMIlbizKj9NkZPBHHQxMFOrsJFgb\njyFliVWM6/JxZmmHcJcPUr9n7PfgVqY/fLUTL4KFkcXTTZRZSdwOqBSZ6z8b\nAzzaK7E2Ym0iG3NECj4+7y7akVaDvxIUyjDjnyQmNoMnkagTGKKXB0VZW4PT\nXs3QAa/LGGlbknUB+Y6IhJ5/MJJLUR6AXjfHX3hE+PkFkjYi2CXpqUAZ5VYn\nbutCwtXLXKql6Jh/KARGmrYg/KO7AM/7PtyGEvFOjZ14Eky9AiOgXAzNFGzD\nW6Cf\r\n=b+H8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHMx1+XONQXme8pGOU4slNcgTyrGzdDS1j8q44AbRd4UAiB3sXs9RYXASFRnTgCfj21LtGblrei2l5nbSWy19OFQVQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180922193416_1537645063547_0.18965235869953112"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180922200631":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180922200631","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1bb37aeb1cf6bd2eaf38e43afbab23d2be3e6e29","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180922200631","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VZjpk/8y8FWzNSnHaMtl/F+KCS1lNzivbDKhbGBhfNOph7J/KU9pEEBC86rhdS3GdugbtqBei5Pqxy3mFZIOQg==","shasum":"d365a21caffb1db4e1b637741002fdbd3a013f2d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180922200631.tgz","fileCount":696,"unpackedSize":1588052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbpqGACRA9TVsSAnZWagAAjXoP/0yFvsdFMZ8LDezEsHTH\n5t93ynYbD88AN0PTQrsogfuNonlQVVW2fHI9t4hQmefuu5Fvz2faTIBDg9d6\nAyaZxU6Ri+eII1mV04I6aV6pu0Nj3tV1+47fDO3v4GM8XGBTjx/rJWUUV35q\nfKNuywqjUmLjeRq4OXAN5kAzjyiC9BF3MzhUgUalOoo/koCNzY/OVzwpCG5O\nbK9GrUlansbxaAKczeccWxAMYt4wicJ0JgbwGviVC7iAidjSNfplwYIrcYTb\n/YT4ojZXvsjOLFBDSQgLEPqrXHhRd0+eyuGrM1M+TIcbrXwAzIjqv6KYskFj\n/P0XU5vREvCbrOeLLaEbvRnkvmFzilN+OxyUujC5jF+UAMvqaGNNKVZErh9P\nYgEaKNpwd2+EOoeQ9dvNPiKDMPiwq3JHqhi9wrQJKH7s1ThB80di8USuX5LX\nmtfFHVUyp9nXKrm4eKbKtado3qeSZ9sQ9YYKLhWW0dAEavTc/ogFOE3n+g9C\nOdqP7xG6Vjauck+bfLa7tb5Jfq1Namg7JfdOKa5Ze4qlsePGSuw+emFzIPl2\nsdWDft+kwTClFeBAS1tyWTewLKy6pNVvqEpACMoSd6WFe4xjk0QYVOgT8J+Y\ni7HljCUEHE/ZsHnPHLp5qXW+kbFp8ngFbf8UJqMumm7eMp0+FXPCJaxe8pmT\nKzga\r\n=DifS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDv51uDTpIwcGnuV9c8rCZ3B1izUos73mWMZPUcnMOrAwIhAOf3SG4PcU5w4uKPdP8H0P66BqrvmDdIbSdcruDSioKP"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180922200631_1537646975475_0.32167701995784137"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180922210120":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180922210120","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"eff224465e2a7473d7e2d0c980779b825a4ff252","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180922210120","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qR/vhH1Ct++DEYo5cRFCrpJ+XNUW+wXFVEKPJ9mS5tI88Q4CZbJM73ZfGiELnV8mniwZ9H4aQ2qj8DJufuRv2w==","shasum":"d2526e38fe20319a37d947c1db059ff801964d59","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180922210120.tgz","fileCount":696,"unpackedSize":1587258,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbpq5zCRA9TVsSAnZWagAAHFIQAI1wNj1x3Xv/a5yqqD4q\ne+tKU7L2ngDRsAyVmtK3ZWbG1wPX4xlIUhYCbROJGOAH+wYQrFprlewSVvbo\nfaufUZoOx8QtJIH1zj89iiYuRz8EEvBtvYoHOzmPbPtUoJONNXn0eG2fWeWC\nM9fsvP5sfmS3/K+cYeTKw0nwQuSAVFScNX+Fi15UHNU+Y389S8ouCI5/DaAD\nvewx1y6DjfLJA1d1xI/6aWeY6oMSs+nO4mOfFLD9poAH0bo9+/puQPKE7AVs\nfqU3zrcEGpz0Az+pLzX1qMwIcJR6w08iIwSmd4WKKWq5PihLEK9rc100yrtU\n2c7ypD6ckYKydclktPP/mVvDyjAS5sWeNfKJv/elm5XGw11gM2SArDaqsdPX\nbe5wTC/AYurkFxV5Ic16dk6Qs7yjTzlt4tFdjMxdHUWrdALAw2hDw7oToqjf\nYMaVbxTJAEf4wiLrUQsays16Mz15eLXY7TcipUeqKcSQeHFgQRMvoj07eDZb\n6N5KiFxtf3eYGTyqmAouVKKYrMvE/8oH2eVE3MLIU4EIZDkB12SkJ2dvsweg\nOjhlDrVtEXPILxj5S/0Bc56DDFdR/Z7Cd3MVR4BIS0Ys4ElSzUdF8PGRCO8L\nmCx7N6/nau419yB+okNKUcVn2iBS5hYn4sT+gKhHe0p65oIxZGd2MwR407Gx\nawZ8\r\n=jT27\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFlLt31AZBi1sPNkdKWIpPHv84eUkGKtMkUuyyaukbtSAiBCUSpRVYq+M2tKkD35KxIx2PSScoV2Wkq1JeWN/dzeQQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180922210120_1537650290992_0.04316281299440461"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180923150236":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180923150236","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"023a15967f82073f739976f244f3512a9e16d095","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180923150236","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S9qEmQRRUeAMTSEfGB911EtwNGWKKh1/IDPI9tNMcVdeCeblSZxvzUi1TzGii5VHaYLCiFvOkK6aVv8e5/3T3A==","shasum":"b4f47666a0a83617d2a9b9472831208438578355","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180923150236.tgz","fileCount":696,"unpackedSize":1587466,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbp6vACRA9TVsSAnZWagAAoWwP/0o8w8QETBRcd29Xj8C5\ncpQ71IUewtJF9wP1HidPgnwTTnRTtMAfFXAXJetz7i7o7IEFbl2PiBfPC7od\nFLjw7H+KdpcWAKgcSoEUeYCdCafG5PRleOYn9SGI7Kewfi9rbKkzjOy0wdkV\n4Zz1yTdrgDBAPGPcQ61RTyYE4lFhb98ESAsvlhw/6rSo8Ls3FUpOmD21YPHy\nR90uwE+tzR3Vfh1yj03lSFVWigH2kwacmGYk5QvgG7dykHsAfosXiK7cknwD\n20MBH/WqLBBR84/NwgYQ28aSwAeIyNmnLxWj08S2kzcRrrgW4dtUgadNewy+\neCi1qRkqzOq9Mn+XMpvztuelbwYE+AvfmlK0eHePvi0kPhz+SMEeV0XLFjiw\nXx94Ld7drx20FtmjpyHjK65YGjeMFZB5Qns5OqUIxGx6hIEb/nAPVMTbENUr\nhAUX2l5FPLSmJum2Yd6Mn8Fz52BCrLvJIAizuampZs3dnN9vAvPBiJ92uCUK\n9PXjo0Lt6d83HBM1Ox8OaTOujYriht7d8acZsQIvoUujLTLc0tniJ1o+ancV\nlX75F0kpc1uglAaXtQmDH8M6IzaOvmVzoAu/PF2uQXlkXBqVaDMghGXqfKmR\nf0ZzrP/RjAT+9KNMpmLbwm6ocW+VG2XC7z8bcZ/YFfVaJZlpi27JyVEN5yj8\nWaRn\r\n=qwUu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDAig9U6SL6p/Awe3k6lbU2PZuezvTcYxo4HpSYxmJHBgIgBEcEBPpEyPRufiqxzYL55WyBQeSQy47aPTNkgTDMm5c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180923150236_1537715136114_0.15388112979603719"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924063742":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924063742","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924063742","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+ehjHyeFKjpCmMxTQ4T9UGCMCLEKXmMOQdDfwhwldTuUnaUJPcr63yj5j2Z+LjNadzNXU0kO93RUdhG/pGGuTg==","shasum":"ab678c37a52c0a908b086e97413eca22be67f773","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924063742.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqIbfCRA9TVsSAnZWagAAW9YP/RQ2b09abk9KUr5nE7R7\nm5L7f9eGqWdyQ5/OW3HElE1OxVGSfGre1BMbqwfmbryxHKdGUqSwbWXujYda\nCT7JjnyIJkNS3c20Ku0pWD98GlR8juqQrKnuna374aUHKkotazNKf5Y8Kr5E\nxnwUFII31P9EyEilHGI49PPawPQzg1qMucFenvYOdcSVZxVjkFD0vEsaJriF\nqfRybog8awOjHOvaBAwlsPQAvJy4rEUEpSdwq7P3QtWO12fnW3/0t+SyDy+h\npH17l9V+fxul6TGKdt79Y9sP0+kxREwNzMQKe7acAu/KLyWPmpWe+2YEJW/l\n9vDcCHqV1pNvt8r6d8dhEUT1zVmWM9eYUroE3JLEuQBbsfQqglsvmBlgfB2M\ncjoVGD4gYjLCLJGif3IfoGf/h/HmT94Tq9OFgw2hOKnlBL2FwqYds40pnFK7\n6jQcD03mUCb35tRe1DZm7dHaBqigrr7lkzx7nGm2Jp0r9IICcgB/yj8oNCaf\nylWEJvMEXahLUd2p5JRNayFAdA74S3p5/ctvaruSy2eKgFFxKA2yT2sfnpi/\nciFGPrsMyCXr9Q2et9RFyUZB7NmWQWEkYrI4CaV0fcT3NE4gWQmBusIl10Pp\n3hYSlxUjyQACr39fiyCny6MRNkV1HyDOuZpNqgcIaoMisvU6K2/sOzfguU4E\n2e2r\r\n=u50o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTU1UOS1vVt4Y1uCUINDgzrWvBpTtullfpwxtXotb2VwIge3y4t99+SCmYaIn2HrJVIVrsiR7lb9+sPA5ygDf6920="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924063742_1537771230175_0.3314787641485446"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924071955":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924071955","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924071955","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZvBKMifdA+bdfu3rxtWmtf5sI0OAQ0ZtkAiScn3m/4uF3zxlke8EE639EbY3aYWxPsPbblvWvDzvjodkQ4oUBA==","shasum":"54d40f8a78e1e4d49feddc3d50257e6946a356f8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924071955.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqJDfCRA9TVsSAnZWagAA7BcP/iuvCBwA2y0ggQdl0VKP\nH+uAaOcR31Fdg7hS9qGNGnlzuc+05dYu2cXVbW/FcByZ1oy0NPiMSZAmS5Xv\nd6+7m5r2DHhx0QDeZP6/SaEdZZzBDJ84QEtl71/Jd9oLsKQg50MDXK/TcWGQ\nFuf7VI1yauaTd16bWOWOTXJaZWihCJAkcM7tCx8+zvEewwJ9+DQ2I1T+8CIJ\nAHKNubz9+sg3D5RTnNqDHsPYnlZDrUrRsumoznI4zbn0vnb0HYn9GGxlwTv1\nv9K8ypA/ei/UgPxIkOt6B5axNVD2laRukaPyWpYxXhmsxtAqnOybbqgUgyOq\noEeV1jABH0q2tJCuzCIs3lTyQ+KS08KuHMEfgi+S6RWrYmnyYwD+hel6QpKt\n4FTTVf4LMwT+WPqZNJ9ZKh8CP7F6QUC+4ntPeA/Om+J/gDRU2+6oUvVMGwth\n17X8gYUGTy4t32Ph5hgTHCDjT3hAScAfLhGKkbyP26o2yz0u3m47nYuxT8je\nXJY7KvvPGjphCGWB4K1npolmA2IokZz/vULIvlMcZjSUvSwpn7ozU3ASpt/6\nClmwymIV6q7/yRLmEZMjupxoavw85eGM3v8lXnTrmhB0unvCJHraSZ2m1kmn\ncwoPgwL5vb3Vqi+fc8rUKr8/SMSUh2q4hq+2vKZQ6wY8JHdMXvAtBBivbGPs\n9RRT\r\n=A2P1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDcmFH/hoXFAf2YHqDi0C4fdN0nmanJCd7owrKpCaN6zAiEAjnFR47WCJGS2DBxgSQHg1N8Pd5qcz792HMa80P+04gg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924071955_1537773790385_0.8047495619535288"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924072514":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924072514","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924072514","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jTWWqNTC8ZwwN1G4x1NFU9ggrS/gxh4+tGWQj8fPWDOcuUOgtE4lxTJsW5v22tYnRenXT8/gQL4uGrZWdPV0Xw==","shasum":"207b1f429cfa4115d27ee1aab63356a8d56f0aa4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924072514.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqJITCRA9TVsSAnZWagAAA6IQAJs7tfLSBdso4xJBl92o\nUTuKlSJ5sW0t5nfgp4/m5OzInO+taQPQJHmhNkOBCbE91OmuRa1el2+WNksz\nLWJeGypQi1c34EMN1k2xgK1mrHdZAJ6bG7WW1//KeeGqFpxEbiGtQw2koTx5\nbj273YZ3ns1YzjE3Bac3GRwoHhax3ZNSpiYprMJP9hWgjEk5LaaHEiPW2HrP\nuJTlm22QnL5eF01ZxprAMXR8XNqbfKYtsCkJQ4RityQ8rvH2WN/CBPk52lwT\n1E/EBMrzVmLyzYfwsb2OYIQHaQXfGi+LV1tPSJyub7jBIbEkz/sz4Ghs6UMM\npw425RXBw3YGat9O8pJINv62rjgObpIA3tE9S5LovdRPZYy809bxcILW1dNo\nYHszjwuJH7hMfPj+2L/4KnTFppTIm0LQ+M6JftCVqDUGbTe1Fv05+Fo7LAGj\nb8xBOf0rKY2s5XfWRVwpn9MtrU2VBpxkYBy//3G0lNm9ztzejqWquoPtWvUw\np80pG11VtWWfcr3WBrw7jc+8XrxgXNdhf5vPPXJTu8oqg/Q6GWa/09P60HME\nw7qAs9ZmEBDvOgPp9n8szHZN3sxPhvx23cYsOabNaxphrooIUDzE0Lm5d7k9\nyPw9XnzxbLgEFv4GhEXi4jzoUU9CD3SU8uBZkQDH8Oy1hjy27xjdFw2HM6VU\nTJaN\r\n=vrgy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGD2nuvLE5QCKsphDF7wNsbURBOyXbhF4DQXcFxPNj3SAiEA7cQcWLx5tP8+7Cb7SxDMqjVsyBPUySFKXbMe5Wkh/ZI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924072514_1537774098278_0.43078602990147585"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924084015":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924084015","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924084015","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-H4DDSkZSuko6W17DKQsm6Lrzy60yivO4Ovbyo30EMYOMjA0FvAMfR8FuD94oQNo+gnOKP6ttt2kkkj2+T9rWIw==","shasum":"c598dd6aea8f3c783e16e132ebbd8ae6dc2f90aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924084015.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqKO2CRA9TVsSAnZWagAAiB4P/jRLxJ8I9Usx2bRCzzTa\n1yEykgvqBVhq04/TZwh8VpKTp20UqJJL0EB64HX2EmeLT/QZOpm8QLXZFg3f\nBK6GUkyfkqONMikhYC9ks46ibgxhDSVpPcJdrXB1MTo7c05WotuxfEfkmpSH\nOF4OdieVYx67a3qeq3td2BtXXA7yYEs2h5DqQTXEATUUfxVKOzzww01q9jrR\nTNLzjXaT260ZLNjxFpCPOUItEqpbxaSHViIz11yOgy44JUbNZz5Px+7GfTa+\nNOq0c+xfAFz7LEpgk2s/SQvyb5l40Zlzceg+vR+DHS+E8WMfDq1rfEShJbMa\nyf/35ubMbrXwNyuNM+mcQEj0X0HGyhUBPmjoNQfv6NT39g/g83clEZ+2eYnF\nh1jYfmXuc552mDl/gtf3Q/6FLxry2rsCSe0ClWfHzq6yRfi1MrIj6qX33h5O\nIy/GryFgn40Rsx/eo/PItFNTJotVklY8gDIIoheS+XLxk+X1bWe4+qZiw4gm\n2RnT0r8qhIy6heO6oTjA9K8GGaQw/dWd71q1Rwh4sC7qvjsvq//8ji99iqAj\n89ommY2MFMmSaknI9W7oEa5L5gKGGwF6jIuRTscbfzhQlDgEtSKclGwVUK6N\nkEuNVQ2+kT8Dzi4nkbJnTkeq0AjK55FwStU7Hl9UAS0Je8PEg53y5xaKSc4g\n0lsA\r\n=dcM6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFB1Z6dVTnPe64ago8vEA4GrD99nDlyPYfA1cgl4yvE3AiEAgPxEk6CdxsjyqtuTPOda2JE1JXZmGWOzMtS12JWmDzQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924084015_1537778613768_0.7058119861537149"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924084400":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924084400","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924084400","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1KZr6rmBs+H4f3BMLzyZdRyj5E/1KEci2eKZgcWixmXOrtoN8dV9aLY5I7goDmuW76+jaHLxr3gPFliJUza49w==","shasum":"2261a9dd1ac6f69b0d5a014f87089c580b06641f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924084400.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqKR8CRA9TVsSAnZWagAAC5QP/jpzW7to7JXTEdVmYstP\nI5EL/CC+izNO7WJEVJcRqfabdxUHgydyWm+6rIjfO0SWwcP4INaN5HiuIwAm\nGZtA1noDkeiMHWl4T98XM0WhwbvaWNKIrSr+GBopub5PQ8CZQmGpcCdjpCCF\nCC8KdDosy17Yl5/tVJEetQtBHQrYkKMrDFy3RC6DyOZDx5CheknKVb2PZd8K\nLD55vnWJrl8P5G7Dn1+AS7KDlphhmwUvfyOKzFJYobR7/Va3biWiHhOP4v1p\n4zoPe63tAnvfotaecNVBgQlagkC9OnaK6jrIl5f8Jmn63hefuRCcmLkMSVkW\nB71MaujDk+ePoFaegr307Rsxy24oTXQXImNiAPpwovsheCCr5XfgH3O1IGic\nQ6BdmSQI5wU4WDGMFZMDNggHaHRoBctvBZ2WomsRMUzAoD4iLEdjN59UWr6L\n1WIPd9/YrfFBJWz68ZD6VXQyID1yb8u5xVXU5uAdwdW0ly4SOifoDzzz4Yny\nYDdDpHhuvykQZYDG1csNj0Pmy/Z1HKMfmbIlXno7oPaw7YuSBqz4H172E4m5\nkPPKoy2K8zjWlyiGwVohyVbRZ78w/7iPHcmJzb5TZD/cbfF3kWEyieHb0SV0\nprL/gbyiDRSL594bQi/yWFG8UK0nsuKlS6tES9g53P9xX6ZLaDYXubM4va0v\nw2RN\r\n=omij\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFq4ZTPsbomsZpT/dw9/rje0xXp6H/HOUnHqfgAH1nd6AiBlKnQFOuZW7hKwDaa7X8Vv1cO6BN9I+qmEtVgEBZaMQQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924084400_1537778811979_0.388023382254149"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180924085548":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180924085548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"963115c13c9de21971999783af9db0d4866b2831","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180924085548","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UrheFmwYuYcEftkj2YwEDFft2abkpC7wllGi/nmemgk7QzEZX+g3HMSO8Vri41lB4IOwc99ezLOzsUAC4p4TCQ==","shasum":"b434e6d01dafec209ad00c4493c36217c2c31add","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180924085548.tgz","fileCount":696,"unpackedSize":1587144,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqKdLCRA9TVsSAnZWagAAmWcP+gNvFiQbLHTCfJZOwPP6\nwmQxO+BdovgfDi7p7/79sCrdfpRsnO/F+a8ZE9OVf8kskSVtH/ziri3UKyjA\n/+1EA0I69r27nASchFrY/4u3Ab14eFk1HQUPNnWRiUnsZNW+KCy6RNxMg1os\nY2dleSGp4EEwvtLXMx9c6Xu2G39CeraW9KwsVxbFK9oWsbdxAMkKFYWsjvJ5\nVxuH5nind6pxuN5WXpcRh20W+0anD7xqdzejLODyWeW0ZxGipY0FcX8TXYXg\nA7tmPGU6H4aIoLo/iwe/k+CqCwtXelddcKRR/KDkz5qIabp07HnWi9HLUjAC\nMV4px2/9l//dqe95PuKPq0/FWXlHfli1saccttx3zY74SX0SthLJeg5bixrn\nyzyM7Ad2Vnh4TveSe1/yuWA/h0AM8A3TtzuzHF+1fAQq+c2g0JmM5omsFKnW\n+ORMOkWwmcGaojgN5SPFgTmliFBIN7buv0YwsOsjC27hDI1t+e4por2146nF\nsI1POFK++zjcgmxj3k2AfkL4JJI/NUWKVE7gLI+gFfYt+gJAlfV+cFMzLf5t\ngb5wVcqBXScitMlYhD8SYuzF/unCw2Sxd3zYekbRsdUJ/AiEs7O9ozq+bCvU\n8bNH3ecWqNE8q+ofZjCZtlwp6kjaf1XKlge7HmJQOZOq+6lQckKBNVbbwWz+\nBtcM\r\n=5Srh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDi9r10HVbpRYYbQCyCQv09eF1t20URb1kR5Kk4FSm/zwIgchJLHthEGorxVQZfktMZ86LHYRPdmnHOrgOqJUU9tI8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180924085548_1537779531020_0.990653100794161"},"_hasShrinkwrap":false},"1.0.0-atomist-update-branch-master-1537825734574.20180924214903":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-branch-master-1537825734574.20180924214903","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"0fd244f63877272d7b5ceebf738379d20da95827","_id":"@atomist/sdm@1.0.0-atomist-update-branch-master-1537825734574.20180924214903","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kJlX2Nc4JWvKKErKN30KfmgMYD5A5LHtp2qcM1YSDJd3lLXLJ5STN8MMofnLDt64kLa6unqRWb41qSmZiw9mYA==","shasum":"c97a21f377c337fb5a99dff054c2dd75fe2dfa48","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-branch-master-1537825734574.20180924214903.tgz","fileCount":696,"unpackedSize":1581812,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqVyYCRA9TVsSAnZWagAABvUQAJymRCjrrNEI5R2NSzlM\nwiGexIRbminsSCi5QRLeailwWTx6/775/m8hf6pMIvEhsMvljoQShoa/JSQP\nm8zc30zUMAe4O1thy7PCKrshDXtOWxSE4h+BWf05JvBni/0Mqu/GRIjB8V8I\ntJ694Y3sssdnH60EfDzV5Fje8F144O+/WNrnQK5IBulCdEsjXQmvoDPbd4Of\nu6LuhoeD5xoDVWu09uuE/5+Bscg84c1X0256A8m2cz0tc7j761IydOjTXHP3\n/ilmK3+3vDM6qWS0C9Dk5yp+7mNG/lQtOjO3Xjy7W2Q5qXnCKiVXxueonlFN\nLGzSgwNFvBhYupFmCTXkXOs+qrv5KRceF6S4jBF/2tiIMGIuvWszWPbmY39i\n1Dohhqpkd+Oc69LDDLiHWkJQAEbFuI6JZJDwaSH2BDmq/qrRNBENPokn8RWi\nB3TuIM4jsvY3Ijsr7NSoVQcFWDFcPxmvds7kIab6oBapgHG5Sp/vgQvLhM9e\nFR5ESyW/4RaRhltqpYkGpDCPPbgEdYocKm/ITfARtb1ILIpl1hQC4OXEycPS\n0CdZ6hXU5yKn/Hnfh3FKSlAFRUNm0i9y+MpeesK4e0+1lc2n8FE57aFFuN7g\ne48F4i9m3dOl3lslb2ig9gwPxV4m10ecP77nI15H9DefEXGi0NMEYfNRxVHS\ncswB\r\n=dPJu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE1ciHLmZVfBLCuoOutMALq6Bo9jaJpzv05eE0pFnmcmAiAgYYjkvnkdqq6PciRmH7sD+wQplVMhG8dQs94C3CaiVQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-branch-master-1537825734574.20180924214903_1537825944111_0.2279039239388636"},"_hasShrinkwrap":false},"1.0.0-master.20180924215352":{"name":"@atomist/sdm","version":"1.0.0-master.20180924215352","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"5c81484544b23a060339b371127bd85c0ca1e1c6","_id":"@atomist/sdm@1.0.0-master.20180924215352","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vf+gmeOy44VQLqGseMTcbD/Z3vMQgiBYXK2NWW9ar9rND6lYgS+d/3Qn49KESwgWxU0QbVLNcooYt0W/Yw58mA==","shasum":"e16eaf311fda1180c4ca8115bf37cb67cc823f47","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180924215352.tgz","fileCount":696,"unpackedSize":1582002,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqV3MCRA9TVsSAnZWagAAmEYP/iFbytYX0akvpfPF1wyI\nhOPZdY/YKs+ZqDXoodf+vV2wsiVi4AMtHy3GSqChGJrf4agxdqJlfQxzUFIz\nqQOA8ACHVRuApLFHe6sKIV/96ZxYb/dUp1CAebUPcEj6ZEAznCAQ/KNxN4TO\n0U6lgM9mMzMdoM7Xaj2OKMnV9B+IA/wNbr0KZSJfHyNDPM8ggRV+RVs8UEhA\nGDG/WVo7sx2bwtM+IFBHXOOrvCNuo5DIGh0V0Im5MpDPJCMOmLm9F5BUYQis\nijD8AWePs/P4C+vXaeWrBU4hY6deqS46iiBGlYu1NFsG3rN4btWKgc6Pz7By\nvUaKGPwSViuX3WYZYHv3SXeALyB2qJNlYIZWjTbkGUdeJLpCMJ2yyzkVj+nb\nM0K3qV09JZR0Q+PfF/E7bJ44gz2j2WWJA9AygvLUhEb5ejhDK4EinGPsMKCR\nQk67dyBBXQqNHTOGuGWlULuRpUTBPpXSyyPDba551bhlQ2ePUfjjVDBb1B4v\nlgMmTWWjOhxVxIT7zbEC2Wqgfc3URq7EzRk/MOvCp6KU5oZDHKYBfdNKs2ES\nONMCyb4AJkmu6tu/xRsZslOT3HN2Xyj2wNdlsC+ksM0tUlsKQfCj4YCrQUhB\njbtz4QLLmqejq9bMts4VJaCmtqYz8KvBdlnVPul6RhZe6ZYcXicobxqrTiM/\n2qR4\r\n=waxg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9ZI9cRuGTN9dtztRxAcnjU3h3rCyGx38osPjW7+M69wIgFcZOT8naefvF1DkWkNVsfF9DjLR4Mths0GMNQSWjvFM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180924215352_1537826251821_0.8937604398107528"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180925192043":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180925192043","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ca438a0b771db418afe0dc573dd5242b66e9c82e","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180925192043","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8aaXVfrlQu7Aofplhsm+0mdCZaasD7zNw/RmEtIkFXbgwzaGzvlQs5SlCCA6UYivNT6fj3icmF2gXaMcudcD4A==","shasum":"b6b7320c2b7f05b1efb5126685ff09b0cbaadb9e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180925192043.tgz","fileCount":699,"unpackedSize":1593098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqotUCRA9TVsSAnZWagAA5dMP/RQ1xpxgTe6mfRlJJxUp\n9tvnTxRKtdlSf0liIeY3LONdRMvt62PT0RFwAqS65279gFGBDOmB7juUsFkA\nOYs433fgcUP8u5ShX0C/fcaN8oecv7nVwlDZuQp2ltXZoiA+9PC3ku9rQgMe\n2FPsA0t8up6KiJ9m/zrPeBUS/YWVn+7f0oE1XpB22SbIyDibh3lKN39Qf0oK\ntGH6tJDuqLGOkpyO+0FrXrPE0jTf4P0QsZzljHFzawh0Vn6aj39pEVKZtRln\nKFWAAXtEC52yEaw82SOuS4tqrNZvnU/4IsbJe5YjpYRI/noIaRU6ly95/IUS\n1/i/o6TQenxvmrmM6yd8snR3j0ZSKhx0YZUYWDr6F4Rhki8WXvDFT0FTGM14\n4CZLJuez1G9dJ9kJB9OIglqpXpYrEjF8EKB9Soy6Y1cremEc9AOGMUVFUpxF\nEs09u++9DYRj4aD4maqtR8A/sM96gnVJSg7B4Fn+KciB7T0gZE14re9s7PjX\n+1ublnQbvAplzWt7EWoKtPCh2ZfHJyiQ35tc7GBiEQMn8izPGzZ6kZStx4fE\nfsk2TB+fVv3qAjE++kSmZKuqtR7Jk4zHLZmiFcxn9ncs78MY+oGDiY+ODS4N\nQ/jX2GZGClB+W55uwrgYlgL10F/KJTY0po0wetN0vTCF9hx0lqV/bhFNWE+i\nI3hY\r\n=iz85\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcUsgEHcOMyB7xEE0meWaeN7OwYu1b4HKbD8Y2BTBKrAIhAKGpI7VbspMu3BMDd7IHVIyr6bOr+Eh5hYTAjtGdnn4g"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180925192043_1537903443833_0.5421022547968921"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180925195238":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180925195238","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e4c096576a088d4071c3d5979f731f72a675775","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180925195238","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6bFciZ5txzY7dsh7Lzs5Sd7EfPyO1BvqVinjl86sWpLA8sxBZ0GUNd3bvxP39163bC58+POsZWI5tBOyi6PMmw==","shasum":"37390bba3c5f53ba04d8844eef81ad6f0ab7f27f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180925195238.tgz","fileCount":699,"unpackedSize":1593098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqpLSCRA9TVsSAnZWagAAl40P/0fRBsGFGglwqcFUUvMa\nwOGJul4C0q1yU65gqYd/uxcWXFq3MUtKsGWxvgZLaIhe6Oz+0mW4wEVCf0jT\nBGf/BblH98B+OM9Vm2599kzVSzh8XOLRn6R/axYegU6+vPaXcL8YEkdRhgAg\n/eH0cg85poGqc69By362queCqig+KVRDqKxgzUMTZWYOj6n/KElNYOhYtO+H\n2/o3rVulqURjLyKErGo+mxe6lN/lPJDy5bw+se9Zn84kG9CofYP66Y2+WhB0\nN8dBBLG1rWGALD4yXtMrw8xvTAuU7Pw619OKfAorLed1csQiR4VpYZxp3hqn\n28bACqeTFuzKvZrteazletEKGBNRqweub4uCqlN+7cy99hMVbXLfJXRpA7Rx\nevuUEhfxPKW/k1LwTZYg0c43weYJTXjWhB1ahIJL95nDPdO10T5b1o74iOiY\nXYVHus0fU6/7L6Y45QQAygl8YvdvprtSgM3L2QQTghHC7JsxgrOkgX10VFXp\nei9CYdlB56lEH1PFmdRIaNO2/DQtNsiESCVwV9tcpi5f467dLes/MsaInNF0\nuFVmYpLfOYU855Fas0h+Wxb5mHAK99k69DwILBRc6IjWroNlMBNG9lyABav7\nLQd6GaFq0YgzjtQIQ8/+rTo3ZtvBn/oCzujiM6RwRx/AQ648g+kvhJN4SMHY\n1bbU\r\n=mtRn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICJW3J/7wEiUtgPAIeKJAJPjq7MeUxoLAkJD8y5x1GTVAiBwY+UQnoBjMVg7v2kbj5WFFM+3oijzRH3qa6o3ug+AyQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180925195238_1537905361964_0.393090675242767"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180925195558":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180925195558","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ac8575e50c855cc94c33aad14d8a90acd6330390","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180925195558","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0GNRU/+lpaM7sb0kOu4JcHaB3LZiANwuK+74Zk3imnekNsQyF1wMrZCTnXme9qEwqwtsJ3JicMLJlaAQlRaHhQ==","shasum":"220c60e908f3879b988e261c6f6bff3dfdef1086","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180925195558.tgz","fileCount":699,"unpackedSize":1593098,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqpOgCRA9TVsSAnZWagAAjT8P/i9zkM0tgOjUh+e3XRgZ\nd3+TK5uJ9GL8tZ9ll1oOzSg/l4b2y5oZHXqlae4NcfaHMEGxFnUnJ7l470ZJ\n+hqFHNlNfKRnsX8jemvXeqDgZ9ZzE+9SS5O0suwaDgyiG11e5w6zyVWI0tN2\n2wjEwpoilLT1ej4wx7zAxSve2zPgtdlbk3Kj9TrIZC3YvjS8wNj2+5dfWQIO\ng5ILpTC6OzcDaVd5DTTZbDzXpvIFF8NapCX26ZKn3/Zo+Oo8QCTrYGap+mlF\nkLSGx6OmM/4cL3n25knb1R4SUHRfN1MSC1zkio+tlolRfHkNOAHn/R9F8SpQ\nIV/2wxyCBl2qQX3VP2wNJeQDspE5uScjosN8PWD+wmhdgjkm2td9L2jkrqAI\nZSeAT2/8DGf0oMPB7+Jx/6y+mpbAdM69Q0dCucm2DyWmqBd7Y5XDAACYMR7q\nVRNjqq3kYoTr2zvze/DVKltHuJVAHcZk8b+X3ADsRkRFmaHyMpEno1lkavqo\nhzwDnZ5cxB8KwcbKWAII7itX1wuHsk2EKn9Ift885mcTxzgniYtf8Ic3kA+G\nHbB4xXnsFBBVWVI+znkFCvgaAOV27mGTgZwkR/Tf+yvK+lzDjq8ZAvCtHPaa\nyXXQ/vLW97F/EFRo1KyRbY6Ah3T5Lnait8P0g/AX55jizVVOkxAT75Ak8PtE\nZ2kp\r\n=NG7e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDqI6oM3gzj5WgLeANAwYXftQuUUzTPrvEm1hwny1kvowIgJdSQSWy3hvalXEiemjSBWUqMk+g04n1BFjZFkCOM4I4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180925195558_1537905567460_0.6878275963970948"},"_hasShrinkwrap":false},"1.0.0-code-project-hooks.20180925205747":{"name":"@atomist/sdm","version":"1.0.0-code-project-hooks.20180925205747","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"^1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a4092aef9e0e58827628d62ff74c0d5cc59d5735","_id":"@atomist/sdm@1.0.0-code-project-hooks.20180925205747","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l9J9hZKvkHYrkud8gFh4QTRjcw1g7qRHTM2iHbnW9EYPT9Ard099ZShbcFMp90bVPzKN3m0wWTJAGovscpbcYg==","shasum":"f142d71a7553de96bdcfb43ab853d77b9efef809","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-code-project-hooks.20180925205747.tgz","fileCount":699,"unpackedSize":1593097,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqqIICRA9TVsSAnZWagAASMcQAI5IBpuWcGsx0NkBB9A6\nXqXkCI8Yij4PxH99tIIaygt6VZAhpe36UIcmE7JE4UKfsmqZC6JMR9fr2A1K\nbeci91QZeuBweAb6phWg+HWl7swna5XF+b3em66FCJQh/JNC8fRO1BdOLXtj\nq5V0bRWynYCpP3afN1Y5Vk9nO7k8BrLPEHpwIi/VrHgYIbHWW/r3mMq0XC/G\nKLPUw7XkudlbtgIFsw/DxgUQEUGkt4roqCA8YQPUFf/0acGL+oeKOAKHJeMO\nijdMXzNVLhsJ5aPYp9hzHYq9p4g/W2rREyJ5cy2gwPs8jlCHpamKoIs3mcZm\niZ8utbZbAkQCtw9hK9av1hj5Pd+SsuH1ZFcVmA0z40WtYSWauy+zK/CTDOvq\nZ12VYAU0F3xQyFKMgcBmC4U+9FNnhiLsoeeTqySisNsQxoLOb8ogvm/GibM6\no7rULHOMfzt97i1aCyk3LUaTwSU18jKqsyWNkipEdUdRlhMTp7FPNgx+EKVb\nMB2olpzxB7ko9I3LnN91VfHb/roeGVXP5YZ9jOWUMlqfu2LrZ/IcVEUwDBlz\nkm5OAUDi+zlDwnNHc9aEhxxWZkSKV8678mXcdYdhggaL2X1WX9NdbcrZRFTb\ny7AbASEzdRmJ19T7u7CUqcQSrS2N7iykJvPo/lmZ5kqWUU7qtILvLPWcsCQ0\niBZQ\r\n=oMrC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXraimiHmighiG50Y2RoHZqFLkwsSVvpz9o9KJ/otWZwIhAJzebN0gxUSaM/OrdjfGxZcI/6Bm8XaLssWP+dSbnYCw"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-code-project-hooks.20180925205747_1537909255631_0.6158230319609632"},"_hasShrinkwrap":false},"1.0.0-pre-approval.20180925210943":{"name":"@atomist/sdm","version":"1.0.0-pre-approval.20180925210943","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"8cc277e6ca9cd0c874f33a646edb290e27165f1e","_id":"@atomist/sdm@1.0.0-pre-approval.20180925210943","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nnU6WSZBtEfqK+2rZu/bb+b2znj6KJMGfr3yli1LJNet3A4G7sB1VbLoV4ipO1fHh2w+HJ0Q5h/9CTg+zhj0Uw==","shasum":"ac41ff5f2a8e38242f8da30cd9228eb15656a06e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pre-approval.20180925210943.tgz","fileCount":696,"unpackedSize":1583110,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqqTICRA9TVsSAnZWagAAnUIP/1VPU/HxPs7cwztnloHP\nxBDWcLYB8YqmJ2zsQVQ7U4eFQxveqBenetUWuZig96e59HoPX/1+qs5CKfL+\nmHvA5Hzk7fkVGWSyshSJBXiEbGcn26TTFOBLxSYO593purOka6JQcxUQQfag\n78WHxqJ4bqTY0uOzAwoS8WHESuXReZXWvCFhJez5zzPnGh38Yurs0v900WaO\nO6GEuY3z+yc/cQHabjfQ6gSxc9WwNyRDhqZltzirmA7/Fs2Lb3AuSxpMG/fR\ngzUwYyHgKMmsoS/jQAPGIWyvbCbVABfGa1YUBrrOIjp/tuFB+gUEERbvN+Jk\nxDkqp7eA8cFRA26OUCbfxx77xnaXXEabiYCp7T/8QvgOSPGZLGKYdAeTaG7q\n861R4m14CpF48pDjKU3GO9Bc5tC5O/t7HfB5Hr6zojqu46kX1DWuh+7fAFLY\n0C3CYIMGotEwyBQ+fBTHDR1lyfNcDXQaINpIPdy+Jlx3SQPDO0i7eRd/rydP\nN+k4RzK6Pd5dYoK+OVpynCBKnrJHa1/aDsIilWLhGPIrTYQzGhWyFU9cvqso\nLFg2d6FO8h3TcO1Gr2tosqUC0Iv9E0LgFE42cZpHXyUbPqXK2LmXlNEp6yJU\nNVIMknSzOZWGjoGk8/cOPuzLz3C8MBaJ7wMlKKnwhFcil0PZ6Kl4trSADrAo\ntM7T\r\n=P8uU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAu9Te66nFWSribBcymZ/WMidIaULZOHEaDEgtbSVh6KAiEAtvt6yZt03cgzdehUeWOGFVyjuzyljKk3RXGtY8aJQo8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pre-approval.20180925210943_1537909959550_0.10367859202717078"},"_hasShrinkwrap":false},"1.0.0-pre-approval.20180925211450":{"name":"@atomist/sdm","version":"1.0.0-pre-approval.20180925211450","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"384b84fc5855ea97dceb6ce2df3c37a92b4a9bce","_id":"@atomist/sdm@1.0.0-pre-approval.20180925211450","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EbGEZ3ZreI0U4Ri0KkLvCFmiAYUeRWGVXs46uw1hhCrCW9mB8EP0s2cE3/V7c0ui7H0IpQ96WHz6Xvc6fW+h+g==","shasum":"e07d68783eb1576df198eb0ee12b6964a007e1b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pre-approval.20180925211450.tgz","fileCount":696,"unpackedSize":1583178,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqqXzCRA9TVsSAnZWagAAUUkQAIno7yHmYWS3/Yl6sBqW\nlXvwsASEQpZYCJbsdWweMR8b1Oas8KXxAKSylax+bqNS8ag1TZih3SuerLTN\noFuwlWYgzteJijU8Z5fZ0wddlz6Ijh/r6zLJCoXUICUO8QsyxZ5t1eMBBPJx\nGwLSIWFMzuZsAKzgO2v/83pRKDksQ3NlVMhsxl7DE7rBZoIFo6KtQQE2fvfV\n0vYBVCOxiM6l7/2obwJJkwWx5k6C97fQ/QPy4W7GLVthMNZZtiIDg/EsY9CH\nFSB8y/subaPQ3ktX9SX1RG350u7W46gzfVfSG8NnhOg4HPM6WNOks8JKiCQn\nh7t6SYhcj7F+41KdxHwLgtX6GQIj1ha8EgzEH42DAMYQVMznJRk/sRP6BQ1M\nYmSZOl8T0kJvv12TMbC+r/Rwa6u+qrgYG8jpuFKN5wMulJqvnLSFnEFt2OgI\nWAfYTe3lm8AiFZE/kdgmm8DH/0TzeKfjqUsy1vzbNEDZT+1LFRQ52RCNx0QX\nmCen6lbfvzXG1+6g5oBMRFxFOdjTJCBayZuCb88dYJmPxi3YopEXQxvwwuZz\n1h7dnXAr6+iybv8CfXRb+B8KBM7gr4frP6ANj43sg2ECy9THwclEQkFNqU0z\nKAY2X2yM2raP+2Uj4MbE2h0dtwesWaPQoX4WUlqhnMunvmLuEcld/OTjbudD\nQbVu\r\n=VNKI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBPsR7t0usr3JbMAfanFowAPSFX8FyGvU14UnyPPLvNNAiAP9EpG+cMdSb27i4NvQrlKmmm48G75NdzKbGcn13Ndhg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pre-approval.20180925211450_1537910259008_0.10044025013005009"},"_hasShrinkwrap":false},"1.0.0-pre-approval.20180926013753":{"name":"@atomist/sdm","version":"1.0.0-pre-approval.20180926013753","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"cae204d44769e1a63a81f83970c7cf935480bd19","_id":"@atomist/sdm@1.0.0-pre-approval.20180926013753","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GrcpZfbLCM5lu1eGpnOmQyks+yoqnM4Ui1TunAZ+Y7xpJhR3EmXKgwtdst/4CakBTj7BpUomGVxPg0IOl3reEg==","shasum":"c1b7ff764888991bbcd37040e4cd54618f461ef7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pre-approval.20180926013753.tgz","fileCount":696,"unpackedSize":1583673,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbquOvCRA9TVsSAnZWagAAi34P/1kAHxq/e/0kBzix42ei\nf6o1TqFxM0KBrg6X9FHsRTQFmQex8R1tCf8np+DKpc6LdOsaDl77y6oxluSd\nM1hFNBwVZrYs/xGZH5975LMfHLXpp1fLhtrLnQJFHInTI1VvkfWlsmtiO86r\na+/4y0zv/yB1c8+fEQKiQjfOJkfbe+loMCQ7B/CebbAKY+b7zuv/WFmOo5UP\nleJ8lQIJ1w6fCAxOM3usGncMOhWMuA6a2aMDawNbs+95eOi7N3zZ1SPo/ZCL\nnPmX1Z6U1agUtMkqmXZBUn68mRrq8kBeq/ynbQMfcp8dHUy9jGqRBthvL9GC\n5/KZJb0ZXND2xG9CzpypkAzLsnJK31xwTHFDSR3JpqsHakmjGpYqrQv2RXf8\nfZEZiYtE8ky/epQ9JdwXj5aCVEVLQ2+jxUPXXPWT5dzJXmPsKSAbQV96o74l\nK+Qoc8icZJPVb6y5Zoht0OQyL6Sk24svTGagdqvlaXWBUCp3TrjPoPNcpFJz\ngBujf+vJp79GbW3u4DidMdRCMDae7fDqqp7oewHV7X2cAZP6h6eG5eC5g6Sx\n+P/iPNBXgklg75E2QJPqabaC0hudFKAktxIlWvrpo39zGCHgXyjQNmkrYRoI\nDZ+zG5ugXc8xaYmB8asDrk1B87VTXxWGFjHyXvj7ZXkGWQtX4gKCPUYdIaT6\n2w+d\r\n=q4LE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF2FxsXBBtVhD/6x2xvXYBZHb2lvmLPa+tMJJPFXSxQLAiBhfZPSzBnui0k9mpwaBGmtOT5BKrtwF7sPxGjw93Qxyg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pre-approval.20180926013753_1537926062434_0.3681252392775525"},"_hasShrinkwrap":false},"1.0.0-pre-approval.20180926021752":{"name":"@atomist/sdm","version":"1.0.0-pre-approval.20180926021752","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"4449f94af30029649d95c425e1d25b43c122563c","_id":"@atomist/sdm@1.0.0-pre-approval.20180926021752","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OMnZzpXWOot8G7dafHTht9Pwu0xFL35R/Hzcc0c8WiouQxTY9v4R+Aubqr2lIeHuTOsWOIfk0nL+zQt+gkSQ+Q==","shasum":"847554f96fbbaacb79117568be3416d2b75c6de4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pre-approval.20180926021752.tgz","fileCount":696,"unpackedSize":1584196,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbqu0WCRA9TVsSAnZWagAAA40P/j+3nsxGHWhstEBXVkia\n5VuJvRtFCnc9mUfpldDTuErEDPzuyPum5qFHXLZM5Xo5XBuwuTIB27decrWf\n7nQPeHr4ivZo9amE6FuHrUyy9uyx9AhhIx4zJ0VyNhHfdlzpcNNTftsHfnmV\ns3MCF7Nawr/2VW7SPaC0IV/I1KtuIA5MKpeHznHvEqv4ewjpDL+Il2Ua9Z4K\nxSG7pAEravCOqJp2AIZBcGxGeogOTpXGDjnFyd74qKVtEWaqVIaOirfIPRZP\n2+V1SazGNPBt0MwhBKZuCqQF/mjD3IKyj+tXuLbobWL6imPVKRKEdm4/hmHv\nubPhrTrrvoeEm4uIE5clPpuB/U8PJ6qFlHx1p1Dq2OYm04KKfdHbcVRe4N1Q\nyfBZeqBIktQaJhJOkmgiXz8Tw26KO9SvWv4t9avpmUUguTsKtww4f7QlQSZ3\n1GM+lRQfXAXdE7jusD/FWcsPxgxcN2KsSUQjsRbonsO33EthEYf25WggHpcu\nVpqEQNep35OFFnhRsFCNL2CH6QnhXNUIMhf1zt0cuq1nY/k61KnYTnwOuMB9\n7nfmirrSeiYtXrQmEVIXQbCFbES73yrI+T9QcCaAoe76xFvG3TEgc2oJELOA\nKDx9a+yR0vNkMoUYAB1EXtVGGJFupzcncWkZj87Shria9NE72dcvAO6wAwPH\nv8/G\r\n=vWhj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEMCCmOsKxcd2O+EiwyLQdlCiRi179yT14kviBER//aAAiBRDWD7arz3g9jaEV0jkS546Re/DWFFDxQd9+UGU+GusQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pre-approval.20180926021752_1537928470084_0.03043113335983727"},"_hasShrinkwrap":false},"1.0.0-master.20180926142938":{"name":"@atomist/sdm","version":"1.0.0-master.20180926142938","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"fc267f7a53fc82eab6288476295b74e29ca9c146","_id":"@atomist/sdm@1.0.0-master.20180926142938","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7qtP0YA9X1dQgweB2ME3KLtEd43Bp5Eib3V71/rHOLd4uBp+MXgpyMTMp6A+Iz2PvjdNUvQt3UkW21H0RgIwNw==","shasum":"ce3124557e6dae91ac63a518deb89fb9f6da7740","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926142938.tgz","fileCount":699,"unpackedSize":1593532,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq5iMCRA9TVsSAnZWagAAC9sP/RMLnGXKYoDRarIeC9l3\npkPas6WT0h53jok7aUlJnslb/PKukF2fOSj2ySkjIpX3X5l+0lNRio16RCru\n2590EYzIh7dqS8bfCvJrtyl3+nhurA/GkA2Ut2uKcHLxLLYCESSMmFIVTG0Q\njtqGP9t8Et1V2yZj9YTMlM9pHowwKwoRkHoU9Iybaio2ufPZqWMu+XxhFzLY\nNeOCoDTrHvASMzLlDlvjC5KGrtDozfNNxDZDxfsVmaFHBlF6XhGJmBj+u07I\nLlyAsx80/OlkgXfovOhszBF1/zHl6bWKVSTjfDmjdOA0WuKeG+IxZzB1z/UV\nny+++omYpsj2sXfcEbBw5IOwByUCaEmh4g3fk7wK3OUNWcEx4zCa/xo4dZPh\nmiKCFIuadMtRTmtMDCdSoSMXSeiNDeD09ir0buw3WLhHX1o+m2YRf1IvI074\nbgpnEn2f10iv/YIZQUlXIpPr/iAkeiMQdQbFKKtyYV0vzSux5Y5kvdV+NEU/\nBVGI5/dIcKdD4yDkzbORt34y/oqeN5KlxEsFjWa7hDIUyHYRjevgWVCw65kc\nmv32NrxDL0VoF1lYekcvwH9ltPeoFhuMWMsQzzSKoPzdL8r3ko4/rSQq5fyQ\n4JlLjsY75sY8mr8T9txNXHYQQP9AnCCck2Kt1qNtK99gHMY+ceD26pukGXXu\n0KcO\r\n=q7BC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMKsW6ml2NQt13sT1nh/xh9JW9653yN/n0ASaKN0zQ4gIhAM5OlNFIXVvfE0SuZ9lMZoFMngfVcGj8ci6w/eZfOI/i"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926142938_1537972363334_0.654920288661299"},"_hasShrinkwrap":false},"1.0.0-master.20180926150603":{"name":"@atomist/sdm","version":"1.0.0-master.20180926150603","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"015638646d320147898925ec165ebd092b84d954","_id":"@atomist/sdm@1.0.0-master.20180926150603","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FxUfvseyPtvjckuc6bde1WW0NBIiWKqZS5J+nyzyYQU3uimqgQbz1T0CEPOgNT0OvP8JwMXO5QePykNVaTphFQ==","shasum":"5df73425111b5f288b0a2ff3ed6543dadd7b1ef6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926150603.tgz","fileCount":699,"unpackedSize":1593540,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq6EtCRA9TVsSAnZWagAAuPQP/1UwE+p2Uk4Z7HmGqJaS\nZfdLjeU1OEi2IsJv2BLdkZeEUvmx4+RnNQ/L+ePjuRPpBRSVlFvAFjx2ynB3\nlWRof03L6u2oC1VcweoTltkT77zDNQC4d6l1azAAvIbaab9H6Pd/VE6J8gMp\n1ZbF25suVjgBi6Rvc3kqMLJ/6HA2l2aVSJQhVBH5jHOA7ygHmZI7LMEalIAK\n9lI4BHJaVf7gowF0WXZwU2C534a1MqipjKcnmqI/mzKBgnKHiLW8Lxyw/Xct\n+SMvK/SME9+X6vSgS/vMYDzjFwPmLY57Y0SR6/qsw1l8PfO6e4lL+5rrXKKd\ndpkK1T4jHsN+dzLKAu8aotYVGTU9mhYP7cIazJWRlBmu0CN+8l2+faX8AW/J\ncZdd3J2eb/dvieitXOjkEL7EF2Gtgtst1d/Jbgewvkdy72EfRz+jjnxhFOHN\nLntKJ2A/nZ5r1FF/393ILl3EVlRXyFqVumDCHofsLhyC0PgSZcODMDr61Yah\ntxbua5fdtVWbWWdAIF9cvzl0bQW3I7YYprzOOiiz8RYYU1VbCBSOz9PYU4lt\n1gHcBHWAhGLYSF5XoOpSxjRdIjPVYoauVG2WeEXFBcqWKFxwe2sBcLjuav8G\n9Pd6LmNxClGwnIHM4waOZC4dUdisMnJVGQjvNZKXCus52MGItowo2LwNfUEp\nWHfj\r\n=5FGZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICCiXdv3wm6vOyEH7VdXcxS2i45LhSinQoUN9D62FZOTAiBPZTn/+xehuTvuEzXL1+q3h1s5BDixBK0xYL1+afB4bQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926150603_1537974572527_0.4947016961296702"},"_hasShrinkwrap":false},"1.0.0-master.20180926153733":{"name":"@atomist/sdm","version":"1.0.0-master.20180926153733","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"0e6b8bc407c9ad1f706d6cfe12a22ec8ae41fc38","_id":"@atomist/sdm@1.0.0-master.20180926153733","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Bo80YyieDtXHj+41UQ6baa4z+wmu1TCHgam4dZtk2BjlX18IK2Af5tVMr/E8u/Rr2iSArMbXFNoyjVWcjvQmhA==","shasum":"1dcda0b21f4203755f57563bd28e77f992df8196","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926153733.tgz","fileCount":699,"unpackedSize":1593574,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq6nrCRA9TVsSAnZWagAACFgQAJgCReHGVxX7O4M/grlI\ndCBpYsjDgKgBljsKjccgK5RpU1AiAl5scXQQ9XTybLJJCT1PUyU2t/iRe12y\nAWbS7TMJTKS6t7uHbFtEp/JMT7xO/oYnfVatV4ktMtJXimsc0FxJFjAhpkde\n7+voIK2FLbvT/g+J69/PqSchuz5IShUGNnSNTJ4L6mex/6YXcnIpwYAEuqoB\nIC901cZrRxpHqdkXpUb32Xl9KLZxj8qHiFRu80dFodt1V2NkUD9mfxiCUSOb\nA2Jk0FxH+pqjJLybFJlkv+899wCByMNRtArEYwLb25p3fnkAzkW2jAsXsPcJ\nGwMMSlZ/vyoEgRgvZvoBPDIeCVev2EHBhvsaOrbdgpZKnJNrA2EFLFEpOoaw\npwxileGApOFXJ2fI2nhZu2Tyv2NdHmNgz7xBuAEjTkzWcGltax9/k5veHbki\n9tNq8auToNYyvhbfapZe5BNjv7DHgaRyQ6tX8CmvuQ526leGAWVjBmiqaxFB\nuZ++4Rfw0kDDDDo49MMflttpLAACCePOag0uPMV5xe94HDG38dGMDhOr1eIl\nJ27/CvNXFMjQlxo5uJdbA5AU5GUK9/bPIAUyLYANcYOyilkCx4ux3tSx/bX0\nzJuVubXYvutZpcRQko/YMyqQdsMB7Z10iAr1YVhRVcvh45MkkmNphLz/wDtv\n+T+P\r\n=/uUq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFfGL4YUKbIWnbEfbItIZ7dh1tGabmlBynSoZLWQR9TVAiBDVBI0RJp/zy1gBpqKDQBA9uF+dxINIt/oLJp5czxwbw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926153733_1537976809965_0.3549778528548646"},"_hasShrinkwrap":false},"1.0.0-pre-approval.20180926162300":{"name":"@atomist/sdm","version":"1.0.0-pre-approval.20180926162300","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"4213aab472101f98de58e2fe47b99ad302430e90","_id":"@atomist/sdm@1.0.0-pre-approval.20180926162300","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XvW7hORiLZfqZHPaoF1nWfNmqDeBL0WNVFMZfXc4L/RnMlgZXBdx9t7Bqq974QgPxZt4qvliO1Z5Pw6gO6ms2w==","shasum":"d4c59a72b964a132329144322a585f46284afdf3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pre-approval.20180926162300.tgz","fileCount":696,"unpackedSize":1584204,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq7M9CRA9TVsSAnZWagAAVMQP/ixiWm9hEYVN3zI2YGRr\nqRqwkdbhfQx2+Zl9jLNA+y7FHW0KLaTJZ/AkghcBLjKuvpKG6KgrUE4//UDN\nf4bXGuGW1YmeAlupvDRx3JM9nccNcrrPSPZWhkVzu6NZsUDd++pukT309Uah\n+Fefl3yuoOpcVSawscqYGXN/vDWCmCbxAiJ7TToxC5Ww2Z2o8qSPZ1mfA/jG\nOBRORd9Uj4Q5XgKfS2ONeKv3EQHvw/awqin7Qk7XZKdSL2Sy4Nd7O6EUNRA1\ngmPCZvR7ts/Modjo6RWAebvRkg3Yw52OqNZMWzJRfAVt6rDKAje9kn+kRwO/\n+I+/FFyJtnLUjAw76Yuck6FObKF4kmLDXaQBfhHTIg+53d2EH4xi5FLPbHo9\ni9DNcT0Q4MfkeYDrDwRWy4i27g5rW9qEtCCnJpzX4IJ/AF3WXC7Uj/+QSBGr\nVMV+At/OOHUcqRlkIaIUMc8DYjQRo8zm3SPx5N6vhK4f8d5fO60PzhJZNOlo\nTB83fI2z2Yn+aVhJWZ0bUAdd5FHzvuKISKHsPAH8C+SYH2OLhDEvyD5xRmhF\nCacdyGYf0RV13gvMABHYnJA4bo2dUsfjOxTwOOgHbrGnHRTn1tUr/DDJhrqV\nGvt24Sv9UXgUl4WKA5PNeoSLJX1IBWQIeJpc42KcV01VK4A/1SKdUu1sB2DR\ndRKV\r\n=yRxp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDBReQjv3qw7jJKY9g0cTAjhoj9p618jBZ6DWmdr6mqdQIgBWWLTgm9m+w5VxxlP0QcmQj+0n9IPd0ep+NL29sFEG8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pre-approval.20180926162300_1537979196729_0.1010517102306343"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"0d95ebbec66078a697c17e08b28fb5ce130bc389","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2pqfO72iKy5Ft/Om46H9A8lIL5+zpi6/PTNT0QDFnVL8EqkbtapiIi1ltWWivpYcB2qlmDIaAVCbpql/fp+UGA==","shasum":"ef5a6807e7e67bc36a667177b8b190bb1a3c7a8a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133.tgz","fileCount":699,"unpackedSize":1593588,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq9PyCRA9TVsSAnZWagAAEUEP/RL0hF1V9+dX8TTTxLzi\n9yFv0nRfFafSnVkArgxI0dT5pTLBmUcO/bMaYbZXCRMOu92mJPiDtAqobffT\n9/6tQBU8Qf8TIx5KA5aNInFXs8qhDv3oQMiE479kZ9T32+nJauJ+BAAkIv5h\n2R0G/hw2BFKxqwG6MH6MiezG06/ft4p+3z3kVzlYR4RZq/i5jCdq6jiBC/+Q\nILIN4A1czFcfxmUfyrurF0INvqgPos2X6l65a76Ogy0iqnKBJmqyaTVVl5O6\nKmOfUWk6moa2e7mwuMPtyv6kII3QgW+FUafNZLfwXd6PstHfL8l9R6HNTGdN\nyhv7i04UrEHJX1zMbG/wSxNcKAzZBb8fFo93ypiFeHKqYvJTeDNn6SjbXvkF\nUYpCUuA31lHZIFJbCaiaxxCXeuT/bIpw9kUYZYTW2dBxd4vW7Ci5XlK88JUL\nFb2FxX44X5wa+fieWAZmWUCQ21hw/dwmW7H9jaCLsi7VDNkJOTZGOd/XmKCb\ncBYEbDaqM1FLtS/D6mwJEI+1U3vP+/Gv8G2s7eWtui5bLLoAMMIqP3/AXVPw\npMh1sEH17kjLyY34MZKhDwRloEOdtFd62b+tnBQrPYBQEzDdksq0NiuGnvp4\nxeW1fd7IW6nTe9LFlhd3cXcfj2XSOxGxq2QP/oJMfgt/CWU4mJ/5gRlDL6ly\nD9Hv\r\n=58zt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFC6CAKgboqmTSCbGnlewJbtY7PwvpFFuMk3whh0PkfwIhAPHOX1gEp2wefqdzfVX7/+ZkWk8GvsOVDOIAFKW616KG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133_1537987569334_0.9348998513586133"},"_hasShrinkwrap":false},"1.0.0-master.20180926184253":{"name":"@atomist/sdm","version":"1.0.0-master.20180926184253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20180918193639","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"2e0d7295cb2b7d3978d541eb0c52d6b095fa6358","_id":"@atomist/sdm@1.0.0-master.20180926184253","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4xfPXstfQiIkOcyhXpYqhd7wT+IWKfdjRFWtHQo/PFNACOLUfwiylFQKvgK/MNJFZu5fNahJkowd7yBplOnVSQ==","shasum":"63b62843a9198beaf696572b5d6e706d304be66e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926184253.tgz","fileCount":699,"unpackedSize":1595851,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq9QiCRA9TVsSAnZWagAA+JcQAKCZydvhaWQOYe7+O9Mi\n+k/zzyKrWA9OOiGEmp/TtBt0r2t5PTNSz5b6hSzQEe+WznHKjjd0aA6o3q1o\nH09Xh62EB89iW0+05mEloXsamzfxTTlxuDMADi2ODE95LC4CF8ZxSsBwp4gn\np/6u3T7tYtXaYo/Z+4fdCQWAsmtkOeUVV9ejYqnjzPEUOoNZcgITthfluGtp\nBBJbq2BlMH8JhXFQizF+sTUZsIjP0CN7Irv9yvOWqRAVgrrC11CYryco69gj\nAkdjrgO6zx+wdo2ZuFItCMKLt3R+va1zwR5JiVDuHdqwrvRjzXo1kzaxlX+9\naSTASB3d72joDnpMzDGR2KzPpVsRiGYqeq7haVdQX3/WLnQA0LSb4iArMbkc\naWqjYnMZaT59Wc+BFRRUpw7UyayxTaiaXHH8eR3s44EJ7bEHwTSDrppcvt7/\nBkdoXJ6/aK7frKQNwQMhSFoyZOl4lNYtB/fKmbVwVDv4HDkRHBiRIKkgKL9s\nSFhClKrTMKofBNCRip5yFtvrJb9tfxdlS9cMxIQVvKMAgT0GzYxS3tj9eIFs\n91t3utDt+6k4Kio7ZGl5ed/Hx2COR64mRwuldslicb1WofDfMwKyIJQRk4NU\nlxPLeSqwX1gRn/GPuAZQoGnG1U/49HLzkkw/jqH9W+kqxP6z/cXs5zPKrhKS\n1cgv\r\n=ycLW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfQ4zPrONfLmljzBhCVR5Z+NJv1SweZhSx/5dQvih36AIgHIKsRohdJt/JSrbphtONkXf5M9/5xr+Po5NAAXOii38="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926184253_1537987617720_0.6848094110781697"},"_hasShrinkwrap":false},"1.0.0-master.20180926184746":{"name":"@atomist/sdm","version":"1.0.0-master.20180926184746","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.1-master.20180830190736","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"5fae2d8fff52520d5ad85c4375f96c4d34f6e09c","_id":"@atomist/sdm@1.0.0-master.20180926184746","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fGou1XODh+Ps88COjmSLLeA/xPwaLKtBVh2mujDvshlJ/T02k1MR5uy20d8Y92+nDb/7fXja5lF9r7vkcxre4Q==","shasum":"0ad7ab0041897880b71eb29f99ea7b0ee42715c0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926184746.tgz","fileCount":699,"unpackedSize":1595790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq9UoCRA9TVsSAnZWagAA/ugQAJG5DOVszqFc/ImtcQbZ\njbeSgnFQS/eo6XyP3XfVC63ixsT5f/rrluJfvRx1cPCqFAQ6Q753Eljji6Oy\nCCLMjEk8i2EEZt91crgWhOjKlFz/gfhVwZVc0icunc76PezhSYp6jg+u4weh\nH/7GVjVSIANb2pDRiC9Sz/rSFacckeZeVul/H8JUXxI5eYXTzYwefxvX/+Hq\nHj5IpnA0M925MMyq9EcPCk/vgWA6X6MTxe29+oQi8p0v/DGyvVzO5mTkOzGW\n4lEE3yvUts+sqq7jqFnkmmLn+OBeCzetoVV8+YiykUg5x9ru4SIEGhp3SzOU\nEvnv8T8DzipF8pTLKR3o+sJOqiW5EvcFqf6TeqYLnI2ucqKxqvDtlThzT3pp\nd0lqbwt+NhScQX1dG84I4SkOnGEakCmis2k2lv0g+V7CrC+eJNEAVBpssg2L\nB/PZcYPS1qv1WTONYzILc06ax7wf8BEQNhLCV/sfN/cW3NYdYd0q6iAhALGB\nl+/+7MchEKqutqOy8SvNXGEa/69im9yplYjNgEkElFviIV0RjdInndnENwHm\n6Hwc3cY5whBVyaWHDzZMdum89UAnHnENTaGvJ8ULjLdLp0igiwnt7uh6Zy8f\ngku8qp+K3rZA3luH1iejJzo6DfXgOyTG+b6COeWAY/eK5EqQQo4Kp4sdx+sd\nFjHk\r\n=4eWu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDpoXJEJbfpuaHg6pmnNoy1UPy9WQDZ/ULcvYmaY8RYYAiBhpNmJIGNhSutPyjMEPkzmiFvRWfQlJsL9/ij26oryig=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926184746_1537987879621_0.15736956358815024"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-1537989599984.20180926192011":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-1537989599984.20180926192011","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"bd7de032b96639ac2c549760600630bc5300c052","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-1537989599984.20180926192011","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WD2EfjfOiileppPqnsOc0ONROGc+Zw/4S/J6QveUR/HUpjCjB+6f4YtkGFT/RWA/wuj14PQawPzuxkz+x57vVw==","shasum":"a4c78cf22d928b6125e5051de3289139dfc4bf2a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-1537989599984.20180926192011.tgz","fileCount":699,"unpackedSize":1595796,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq9zQCRA9TVsSAnZWagAA/CcP/2FCWWpY7V8NgMFwN4xF\ngAE4/A96dWsvW2JKn+u+4lPTHZ9gIvO2z4hIuoEvs0ao7v0w+9YRmWrXYSOz\nBsf3WoTW+BDqUIkxlE/gSWtOto0webFxqrxhyUK/K0WbU+/ZEd/cQfPcxcJY\nqUXQmaZgEq52WXyS2V4RPrWlUKDZpf4OpwwTSXjyXKDt39rafZhsCNp6cBEu\nUZos7Dt2yY4NZYML6AJ/IuOb71JFOUCtHeeQA0TVjUKEx644CP8gGSBUMnZ2\nKBzU7l6yBtp8Q/QjVK/gToLY31aHPzmDSumbmJ4hZF8Bn8R4LVvJdURQR3cT\nHUf7JLWi0u4bS937vAcZxebmdqeBs7t0/QslfeGAqWx89GIhQ96hVESuUil6\nbaWNIJyPtfa+MivBR3eQXeezgS0xez9fV3ppFOW/VTi+rUlDzmpaK7rYMi95\nPrcIQi0NeK9/WFqfMznpMGloJvQU2wGqaCpc8z9aReUNxqNc1pnxJqg0Idf/\npv8ZOjt/Ml0G/44g0WwKV7zgDLs5KxLAjVb7k0jM+fUX9frYDvyRWwYVWMtv\n1KzvFiMCylhtGBHf52QIzp83KvgDFlFE7sI1ZHQ/eT/XJFtWnSvn5Wbigi0D\n/0TY0twomRWpHu+IxbGyh2uhfrqQc99NKllUqpq56TG9AkARAWfNr7NFhXlq\nNJHm\r\n=wcYv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBTVquIF11xSbqBaKcSNXzAIW0xcm5vKXKI5DCHiBSoyAiBboz5e0vXd2qjFZc92m7sSFArzOupHKXM9Fjj0yd+3gQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-1537989599984.20180926192011_1537989839302_0.44067303884573605"},"_hasShrinkwrap":false},"1.0.0-master.20180926192524":{"name":"@atomist/sdm","version":"1.0.0-master.20180926192524","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"a074538e83c7bc86325b8c25507ac8beb7b2f11c","_id":"@atomist/sdm@1.0.0-master.20180926192524","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3fm2Dj7TudBAikeOTJ+tXGTFFFT9UzyBAeamRHeEulAo7zV9Jhmd8zBZCb/7/ozt6C4kV1/NO35BTKK/m3eI5g==","shasum":"1426a9f48716892b46812a95b5f0aca8ea036545","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180926192524.tgz","fileCount":699,"unpackedSize":1595615,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq94HCRA9TVsSAnZWagAAsFYP/3dZSdhapdZtN2HBzNpy\nK14pxeF8xvto8vsf8AMgKS8J1DNkjoTO9BZZaQ2UlJrCcCAaq70hVtkA72DG\n6jk0HaAlIXwuhU9CJclEieLUdlL9c1Fqz2tOdllYLkXYZxvxFz6ZHFc9gFYE\nM317jygOe0iqff8z+/GGBoEy7/pOdApL1vdp+D4PYeiSa5OrrZYa27FLe6Py\nxm6FZdVFqMbtrZhlg4nRwOaerDEFPwRa6W+YKpJVUOWcyBFYdxLB/WcGZ4ER\nByzgcKxizJHGqSQEUacXxTvuLIX8hqCYXjV4MXVGfe7pHXkrM+SZtdj4tFfb\nR25BSoWagxtKTHPpfEX9AyUhAhUkjrDANOtXbJ51v6n/sWD1rjpZUg6q6wBi\nxnuQoNa2KBQiLVgrNAFP/zevlSJrpNrxfVWs4KraNNJWSD5YMMyw+bqrRhv/\nLlt502NiCN1oDyLWwg7ItqKkT31g6vZjwOZDFQRVF5GreJK/Fywzw+R7G/vs\nFRCVXoKejmyUf/rV1uQ2gAI0gx1SqlkyD9SHTmgcnVo+pbs8MHvuBVPF/QCw\ndDO4hXIXRnvfRHIRZVoJJHzBQ0Bj7GWw84dJ6jpLrMU1en3zfKzbGQwf7MvE\nFDdbSHzBvGL/+kGsI2kdT49HDnu4bDrtDFdY6XOfO48IGz0i7fsyo8myUiQb\nfaPd\r\n=qrc8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDLtPe7OEg/7rxmZf8dbpKEFL3liMeki+ItO/8I8+c4+AiEAuxouB3upGahlzG2stmksMEp6d5C5EfS8bizNz8ThrKc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180926192524_1537990150256_0.10966824465377356"},"_hasShrinkwrap":false},"1.0.0-M.5":{"name":"@atomist/sdm","version":"1.0.0-M.5","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"gitHead":"a074538e83c7bc86325b8c25507ac8beb7b2f11c","_id":"@atomist/sdm@1.0.0-M.5","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w5AA/9sA/an11FqaDNBdAg9njV7E89tHSfUNRb21Ktkgx6Zh+YNpix04u5ouM4yBhdi9papjQgnBgOYGAmUmwg==","shasum":"1c7d91c5629e5679dd9836d67cf62c9478e901a2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-M.5.tgz","fileCount":699,"unpackedSize":1595597,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbq+IdCRA9TVsSAnZWagAA5uIP/3He/wwQtgaE8D47Gx7L\nKARYbB1cFnwwVyhFDC6v4uoAdQCEe/Rskt5XnqtYalzEwxLxaZGI2rBW2xeR\n6wHawBv3bUodvGpmFEbROgdcQrMVZywgZtvU4jq0kHOLcwVBDupRm9lfjqVM\nOvwQvWGmqz4KhIxZcfFXEYSMmOY4BwkWsc/Bk9wfAV/cMOVlmpjymTPrGHsJ\nspNepBLjMPlOz19WUxAr7zokSM4sAlfI3ef+IJN7RrIvPy1OjQI3Z3/BcaK1\nEHVLZn0SKOlYoGjy18CLfy76r9IPNDzEeV7EEZNPRR12X9SWH8O/G9255jwg\neBli1xiRsK3i7vgGIFCxIXzKMEZmny0gXEd2uhXXcMVTHGVO/P21Bn88xx4D\nZv9Q7ZwolCUy0HMqI7teGKA1n0SsX/gkrb7hi8O5VtH2Z4ORZlWqokn7ysIe\njpmxbtc2QPrJrPjEgoH9UrmTJnA/zJbmXN1tkBv9ANhuqVToI0DWwRJJDYh7\nTQ6c+jF4afDyCTqFOASKlqislgK78EvlrhYa4lZP75juljOstHLnpu1XeuKq\nxQXLE00MaTZZas4EkOQ08yOP6otLQ28s8vxWOSakkTdzO06wDu0rHcof0Rbu\nWEueb1HLbFq1l93RGgcR2mBEZ0eaaXIj+SicLJRDSRI0+0+tT2ZPNbvhap5e\ngjix\r\n=tET5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDtxPhQwpPOx/daLtbAsEYgqZuKdPaP+5PsAjjaNzIFwIhAPZBQuOqtuqUd6iwC2r6ZR2g3IYxfJSJDHKMTfnWR5i+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-M.5_1537991196804_0.9518622152201812"},"_hasShrinkwrap":false},"1.0.0-master.20180927040520":{"name":"@atomist/sdm","version":"1.0.0-master.20180927040520","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"2658234f4b58ab36e0feb1dec945cacd071bc46e","_id":"@atomist/sdm@1.0.0-master.20180927040520","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8qlWKexAHBSaQof7CZcmcThd2cwr7SHfY6wvWn3art0SwbcLLlxn3DaeiBKBFYNEM1upgSnfLwVNgyaowEZiTg==","shasum":"d570b365ee1a4d62030fad194a296e57ff34c5cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180927040520.tgz","fileCount":699,"unpackedSize":1595758,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrFfpCRA9TVsSAnZWagAASyAQAKGLdAr8ySVX1BHRPqXQ\nt+eAWaympia2u5eNDsCYkGkUNHhC3Ej5xpVkD2i9uO1lYdCZk7Sj5O95jTo7\nVzs+J7fHcfOYMHX4bK2fzJmZ0KHYiL1YfPRqDDbh3tOlKzs1gswen4S/nGcC\nAp+crPD5aOhqQ6UdA1JKDBP9vzp44F7vq6nZwyU8+12iBTVKULEVjUn0ZrcG\nFHe+R9QVHF2+66vlNJpt+mvSsT84C2Aw6Rt1uDjSxXF7sWTwvqv+DhIyvsrs\nZfUyXjIbIfcUSFRY+UvUddBMhrL5R8jOVYlPnc1wL0p9PR8jWuDnufOuTB7T\n2OxRMeVg4Vdoyyf0sQuNf/yxw4aTWBS+UvuGpvs8h5spFRKvPJ4mfEMbAjCZ\n0gHvI076QQ2y8eVwXQhpKnpEO3pL8334Pv84TeArVt5l+r7iqcVHbKg9QjcO\nsc7jMzbANLCumiwaZzdwydI+GipvFUp9O/egzPHV0CtABBHdff5jCpUuyMil\nTW0wX9Qf/TEVvMAOizD0YlDjmZgSpnp0nPjiOPMVWpxt0rT+2BlQ/DMOdQU6\nsiW4rTr7nScshtsV1Gi667wVg3xn9TK+HpN49AwLEURL+rcRam9mf3aQELKP\nRX88cyU7McDefaSXpDUvS3RyeEwU7jcy4vI3evHJCJZ8CqXUetSP870cuN+w\n7QWF\r\n=MWTL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHMeBCLuH/HcmlvmORX09jVI8urCR4VchXbomXP4FFecAiB7fr47mi1e1c3IvuAT8bl129qPXVA4xu1hyG7v/fa1+g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180927040520_1538021352405_0.1764321861098721"},"_hasShrinkwrap":false},"1.0.0-master.20180927202548":{"name":"@atomist/sdm","version":"1.0.0-master.20180927202548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"2658234f4b58ab36e0feb1dec945cacd071bc46e","_id":"@atomist/sdm@1.0.0-master.20180927202548","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mactMcWocqEouBAjMYzNIcsGJOQ/ChPQxb+yJX8UjapCdstt5KjP5HCaeew80M1HyO88Gn8kh4vzqZ7y/KhSAg==","shasum":"449c06c4a61197c5aa09bc82c3e7917d1a74dbba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180927202548.tgz","fileCount":699,"unpackedSize":1595758,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrT2UCRA9TVsSAnZWagAAM6wP/1g8b1nUKctNGGawRCkS\n6aSsIh+SwqG1XgEz5ImcelVFPNMPnbNQepIoYI/QyAEXUieBidScX7xUqMek\nA/d/WDtLehAaD53gUE99dYazNBT3U9Dg7ewXoYCEXW601SRz/Md0C0gwhdIw\nDNo45TzoZ8wWU2mNrPvOgMHaKuIe4wI0yi+QmN28qFyrzRTdIXQRVEuIQYO9\nvNV2P4R2CtmywEvMoK3E5dorgsHHH4HGO0CZ/omD30b0nbr3yUC2GfFW4NOm\njg7q1iH/bHQzIC9zUirjwrJ/+PPMtja264JFMfvPfe9+5TY0t9OphaOJTlrw\n9HnlJCSKAlJzPe+awulWP8V9L6LeFdyIhKwxZq0OBgytcTJd98XypTm2NPFo\nsZowKEB0oR3ZdoDlyoosJq4lJ0kzNGZOMiMlDzy7R/olyuFIXo78KMTzk4FC\nVjHQmTR7nNc77UinOLoJuMQo+6giKz2h4VFElcNdfI23BUIdsn9QSGioPd1v\ngon+dabPITGRewqbblfiQvQ3+z3gNYqGNm0XluU9wFYULeOqMBebvpa24ACf\nFpOmD20pEHBM9olTscgKO9PzqPXJGEz8LvbbY2g02A1k5MmBJDUl6Wd6xsZd\nH299picN4F5DXKKQlput6R4P4f/Rd7rgHRgSL1r8XImwQNmcluAi8uiuIfWR\n7k0f\r\n=l1pw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClQNgrw4+xvA5bhzdxE6HObztQk+wV3ZpXU9ls6W+w/gIhAMwq47ZtPZhwmqa3ljzfMtrt1lcygghLGFVwBeTm9NDH"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180927202548_1538080147360_0.08447463993405147"},"_hasShrinkwrap":false},"1.0.0-master.20180927204822":{"name":"@atomist/sdm","version":"1.0.0-master.20180927204822","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"6d63255f3a1f5923b33aa7156f5db4fd863df891","_id":"@atomist/sdm@1.0.0-master.20180927204822","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1Oq2t1Z6Yu0VpTEFD39+RS2p+HKHXb1lPxae1h7pqdq1AXS9Ul7apea117gK31AEVI07C05HwIUORoAwP18cmA==","shasum":"a9832eb9b0adb30ce0a722d227b42aecd9c82451","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180927204822.tgz","fileCount":699,"unpackedSize":1595842,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrULgCRA9TVsSAnZWagAASiQP/1BQwgGiIe32JQKd5Fpl\nvqlN5kNzN4iJs0P45Hzv219+6xEZ8bvaOUDxUUfz0tYjZLmLVED/qvuS1O7V\n8Ml22XSgPZS2ZRhE1lOgrvJwMH7MdWtwOiMNuZ03Nvlvryaa8BH8aTFs5OQo\nM69NypTJCiMuzQMlnxbXrFu+fcNChS94M7WIMLmru5JGG894kaJ976kjp/7C\noacpPJ1crYJbWOmBsZRTztVvRdeqiNVviR/p1K6VR53qaZIpeOCksKeP1YFN\nFupeeQ6lM38l470h9xeR6kI5n+/I0oa2uRxw7I+ugjfd8NarmEasOzhl3iob\nHJqaw5sFoO0ztJ2HpGFmkHkqsWylaVtJyxh4QXwhMk+w/0dyPr+1RK2sfaSR\nvagaUqm3kkP+HyjxHUDojnrSNOH07QE0SqOM25CChFVtHHSsX/ZEUHTiYJeR\npH2O1IZDDs0QuVOMv0Ca10/U+YfgEs2KPRuff/HeSQbbcYE6rv8UxW12EEix\n9jseaIacWJfh6DH7F0b9vXbCLPbi4Nk1/DgYk5XHP5xd44zTCFMvPKNFlZKc\nmaYIqoptN2KGEIFWeJYMDOIK3lEFq8mgP7cmpRZwwwR19WS6eh/G35lWl228\nv3LqPYj1nt4XCIrdE9CwH7xrLwnRARLD0+wILJl9qHqIU/UgHWmp0U1q/xW9\nIo8K\r\n=oVGj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZDeWXUIF0MCs9sDxgft8rp9hRQ/kWtFryUnvECOjhBgIgD33m/jpbi2JAh57rJX4B3/9vvAXRcnt4136RQvDp7nU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180927204822_1538081503285_0.2662129714250716"},"_hasShrinkwrap":false},"1.0.0-master.20180927211538":{"name":"@atomist/sdm","version":"1.0.0-master.20180927211538","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"1.0.0-M.5","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"3422b1feed64f1976a573f2784847174fe1e438a","_id":"@atomist/sdm@1.0.0-master.20180927211538","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RMc/sAfAHJBgC6O6O/jVa6RGn1ORsf85AuciGErJ/pwyYSx3dizwqXSrkPgveL+D6pJyYCqT1tA8OSdo9bvjdg==","shasum":"9b28f38edc3f6f8ed319c6881d718d368ea417fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180927211538.tgz","fileCount":699,"unpackedSize":1595727,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrUlCCRA9TVsSAnZWagAAU3IQAJxv4eoJZFcPefSGR0Nl\n5yl660hp0BGgVRyl7x/rC46CV2Bt8OHtMhDI9tEsjSK/Je2Tfxl72GhEjJ5Q\nKAT3jnghbY6apgCyM8tIsxDpi23Odd8KTlYhSj9IQdUM63IdySug1gLsONEk\nIFMCCJ/EjbzONMyoqEPbNeDV6DeigpOi1IrVwVpKvAlgBDQqx8uNpjacKItz\nz3wNze1GeLp/ZDJXw4k/i2fdFZKnKu1ZlLpgynlb+edZuEHoe9/qaYHd0yIK\nttGyBbbfOK4azjfOzKBD+bC2Cf5dOUbXd85fBldbCFLf0Pl00/mPceP3o+w7\naFcInuTJYuJySxqQka5mGI+dBd8rrWhWbAWeHRYw1ew4zVdfP4BgliqrBYI0\n+Brgru+YF/jsOEIudQMjv01YZr5YXC3U29kMBXMffwVqu+sny7hmSsaudA5T\nRy5gf7vtkRlDlz4bYxcJ4VfDeziCzrCUtqis2Lyed0UuEtcRemjb6AChe3NE\n+nWViUkHZ6H/cjOg2AcIldm1OCSWuh8uDeNDVOMrV9Xg0lIlH5gZvW0H0XqD\nKuqVRJQE3yOvFFc+fZukYg3UqbAy0U/zJy9OaBP8QOChOqLtaaEmfZAWfTpK\n8UY8C8T1l9H7bSpE/UEo7BdSSNe48iE+6toD4gJwvn0PY9UvTsrcG3naWtgJ\nw8uw\r\n=wtVt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDamzIbu/iakpDL33BcwreMkXKpUsDKICaYezMm2etgGAiEAxwfAiir00U0BhLp+s10qojy9+ZqF0otMvkEFS5CtsPw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180927211538_1538083137816_0.08490977202921735"},"_hasShrinkwrap":false},"1.0.0-master.20180928134252":{"name":"@atomist/sdm","version":"1.0.0-master.20180928134252","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"4b360273144fb0e828358db059c775f95e29b8b0","_id":"@atomist/sdm@1.0.0-master.20180928134252","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s1rfGOPU8O9IqeKclwlO1KjgrabW5oxMpLEA0/ZyIAJp66PtHdaY6QCM9C/K+b0ziyyR93AZ+GPtHCGLumuHeA==","shasum":"7fae63d6a945813caadc67f97f789cd933574595","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180928134252.tgz","fileCount":699,"unpackedSize":1595889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrjCCCRA9TVsSAnZWagAAfeMP/RTLGg3mhWQLH/j/8ig2\ntCXP4fiukVhtPttDBhAILjqwmWoAX6py5VLby6GXFTGXrENsvWAkm0jk/q8y\nEYLa+7EydEhFbsDzZe+0nkXxWtjELQCAzNryBBNgz/tL3ArTw0diOVgfq4La\n2nCoIKGsA5yftS16ia3w3UQLJ4rHKjfzvHYneaLLsmF4ulur0mCU35wE75CG\ne7jCdm/Ft/iuJYWah9tJlMNrKaDRAsq65WMPkrrWm4Ly+CrS/gXIQNoNteyU\na+ySjX/ku+fH/ZGEYN2s/GdAole+IJvCiLwsF8riWWeTLha7HmvoE46JPLlC\nNPRxqvcdKzl7Q5hV9O52+k5b1DqMAAEIsakoNPFlznICEhyiNrus4vzobA6a\nwtlQVofGlAHGIg63Ay+pjoNs768PWUJTyOLEzEarauUQph/tT4/MNTdcXe4I\nwKEZ/1B9C3tBhU7ildgWn+K68ya0zvTA0+/Ff3Dw+2v5yImjg5mU60EMhWaj\nu4abFKic5WvszXWPq4Ws3GdR++q2E6bwUUhwvTCTpBYMSnwnjMmWrJl/Mv39\nbxwuRm1CgZv3WYjsIb8h64h7IW6LXkN90YboTEaZlUHlZEpwdNeBbKCkBhC/\nOxvvsZ0sQ5c8/RgZRAuMreSIKQCyA10xsDuhxXTOIC0V61372h1K/o/l/Iw9\ncr5S\r\n=Kj0G\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICEi7iqjodEcL2P6KsYQgpk+DUf15QpM6XHvdwnW5w8ZAiA08fWIUudM21pmduGqk8jhBqHQuLRyK8+jPM6vzaRw1Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180928134252_1538142337650_0.6231807741981783"},"_hasShrinkwrap":false},"1.0.0-callback-goal.20180928182322":{"name":"@atomist/sdm","version":"1.0.0-callback-goal.20180928182322","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"d7303853ceeeb74db2c5018c782cfb3c80ecdab0","_id":"@atomist/sdm@1.0.0-callback-goal.20180928182322","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5TreFLkLla3CbGlIyt5GQLTbpR/SaNdPeCKoV64SqR8aPc1CoeChf7VlANxl/LrHT0Dv9QQpbrmxyHU2qGDiMw==","shasum":"5100fb6d668bf018bc2b0e58b961e4f484fc25b6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-callback-goal.20180928182322.tgz","fileCount":705,"unpackedSize":1602674,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbrnJSCRA9TVsSAnZWagAACgUP/RH3vHmckxN3XT2tS4Jm\niBBhyBpc0aRAFgoCdRsTg+7Mrxceq0QnJ2rD+UY+nQqAuS3G/1bBoXiej0aE\nP7Zj+95W1uOykt5y9+WRnDLAMrmwfDbtwSyajmjkGyc2jtuER0QTd0LJFzuk\nhlKvTYnUriRHc36vpzOtPUK3UqEwp934+QA7iA1rxO/V7s8bR4XXEmxJhe18\nZ3SfqT7miaHiiWEzCuo7/Pr6FlNXG/cGZKDQ09t/49e/+JRjfV3J02vBe2qf\ncdtJHC9Pb8Wp7+5qQpDDTVVD7SW4MIox3nx6bu+VfDVxxmBijLKoiMHmcr3H\nIV7/xaOLDjwMMJyr+MfeUwvYJncRM/m4rsblo7Xy4ivo7j6Isa+F2HSCFpFn\nU6vZcLhGvbyHUiHyDN6R/SmKwNGV2DBNuV0BC4Gqm+WMTcYQxCA0QEU2Pixe\nXBOCPhqhAmdiej80vImXZAe5lgl/n4+Dls/91kDDhQaLr1I74iNgQaN5zl49\nIgtMHQu421PpQpxByzZaT9rzdIwEHlsc3pBCdxP/V6F63/N623r5D4RJHdvJ\n7rAq07H8dalVuvmA/kls5Ze4mj+tjkX9utG1AVzaEtkGetuxlBnf5/cJayzU\ngq4WgYRH8dxtYJ5j1Tjt0UjY1lysqz7LL8FbYFiClgOd+bXzYnk4C3kTdXK7\n3AMQ\r\n=VlFF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCUv+KG1kinl1y0m/Stue/DzWMgzVlbo6unXyLW/q1UNQIhAND1I+16ZfCBLOWvl2mKIYe25uxrQFxFbm1xhOYHp8Y+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-callback-goal.20180928182322_1538159185320_0.6327003628160968"},"_hasShrinkwrap":false},"1.0.0-callback-goal.20180929193408":{"name":"@atomist/sdm","version":"1.0.0-callback-goal.20180929193408","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"b3a2c22f61967e7daedae85c9adba2735016f565","_id":"@atomist/sdm@1.0.0-callback-goal.20180929193408","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6emU5au/XVZ3g+OP8xAzPxptzTLMuzJZe9ndCaqHlo6Zx4f8uyfPM6ArXPfqqVdoE0UtnrMtlJdnveloYLgO4Q==","shasum":"3761d6dd15596aaa08c9773f3ef8b3e1766bcb9c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-callback-goal.20180929193408.tgz","fileCount":705,"unpackedSize":1602952,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbr9RRCRA9TVsSAnZWagAAlxcQAIWvtCHSjD9Cxn2gd+Ns\nN2aU0BwPjjnX3kGur6PyEYpisLfUyFMHYxY4GNTOtu2SzGLtnJ3BXO/psRl9\nc2IJuxzSH+ggNJh9E55QO+6ul4mBhU8NgOSz9lJzQj/DGWHtAkvVubxg/VAO\niepWbk17rO50XYqRxiNcIVfEFkzEDh6O6z+UoXeRVrzAI3mSLdSiZ6jweDB8\ngd/YjbCYsEShsT1EAnemYFWuSIBrqeQZB5SShZ4OxQFi4SNE59xwl0dqDY60\nkkB2G0+8JEAxGycnnV4npGGqfZDOxcvf/YYAgLkFgnzfTu2Nm3yjRCIy6icq\n4OHcqBx4XF4vR9Sb4nEw2NkjrPaB6Ca8C7612tfWJN9eF3TuI4pHl4RWVNTp\nNuX7BZlr5fgXHsgbBWrwfRUzg7aW2PpKadBXs6WlsfXt9BpfW3BWbnUlSuZS\ni+JcvSXCoasCux1uj05rEiISwFEIpBj7gYSIo0KQc/uxxBlIo5GOt9TeFKdM\nvrO1gG85CZrcEkLfLszjvpXAy3ZbZa9D/yLetJzLqSFteXxHKAoNoewKU/8x\nC8QbepFnfHmgrMNC8eInA78AqWjDagGJEGFzFKhd16T5Eyfu9s6rY40jsFMT\nY6D2LMHIcTi3fTmvDIYFz2KDv6xbONTCmbPm6tXofCvWnxX6qDF9VlOG6qkr\n5Hmu\r\n=UHIl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE9pM7He2PgNqJqaRV6HvZ6snrKjvEC8VdeYPSKcALasAiAS9IfzvhsP5YdU+o9jv2DTMsm8chXPgKwjQ+UF3NwX8w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-callback-goal.20180929193408_1538249808404_0.8521070584575339"},"_hasShrinkwrap":false},"1.0.0-callback-goal.20180929200616":{"name":"@atomist/sdm","version":"1.0.0-callback-goal.20180929200616","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"ebfcc78140f63f90b0caeaac8c2ebd41fdf89ba8","_id":"@atomist/sdm@1.0.0-callback-goal.20180929200616","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-K1O+lDyWU1SGLD7vkepekCT7Sdt/8Tx9pn+bYSdO+D3Fl96HEN0bYYfpaYK4UuKmXWQ9/FYObeB8+bhrFIwdWg==","shasum":"44b14c12e3a915183f3ad5a46715b900366bf58a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-callback-goal.20180929200616.tgz","fileCount":705,"unpackedSize":1601332,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbr9vpCRA9TVsSAnZWagAAQjIP/1O375gH5MvEp4cWX83Q\nURIZpefNL9leELSuT8chGmp1lAF5ztXMFFCcOXRZqBRZ5SQhYwFLiv1o+sdc\nIq44qdp+qaQEDdeIVJv82MkYCJs1kF+2zwJQTUmfhVNsXWzr6wcHb4AiCYUS\nhPbfe0JK1huM+aAMDMG6m83FHv2+ON39A+FvouAQmJ4FG1X91+be2ziyuxFx\ndfi+BlsJTzjJasJKX/+QxCNJ4OTD5BpX1MUlkYy/4jsM70Vr7cJJP8rK7zm+\nWjZPWt+mOziPXVfK8jOmUGlXraGL3BR4KcEanby90v9d0SuEOawkTgsHyo7k\nyeo7nOovIQjmeCEXgtSkb7xVazk96HMjjsm6CRz0qjyTWs4aWPnm/L2KbvlB\nmWidwryM1fIJHsvM9lMs3S4og6nlKejoMsgvY3hhteAeeJjeOEJTAALMSsR4\nCJPoZv7UEeoHrISXdaoAjW4CFK6CvGq05k4oHeKCZF5YysBh2xdoKCznpAOQ\n7hN3QwIvC/3KJhEo0gMKzZZOrsivXDD1VEjeM6ugKfOHVzh0sUDMTyNzbJbs\narFBGnttodzMP5yirP/gDW1cQbsPKNY0XO85HDJgixNdwv/Zyrei/mp1s5pg\n8BjzUaK8L22qKkfuS0Nkqp944o57l7vHDpx/jhlxaWJJ9NyCG9YPKj9gv0QE\n3X9l\r\n=jLqy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKuuFCTiy/TMoSt4lqVnuFPE40rcm6EPvAVStK5qQUegIgQ1jKvmpJdA1ecKWKA9riAWVX1q3en4DNcDt5risodPA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-callback-goal.20180929200616_1538251752836_0.0884190182917306"},"_hasShrinkwrap":false},"1.0.0-master.20180929201807":{"name":"@atomist/sdm","version":"1.0.0-master.20180929201807","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"b775cce6a2cdfbeb377bc45e37272de1537e90a6","_id":"@atomist/sdm@1.0.0-master.20180929201807","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jwOAiwOmHJjxbxh1G2n/F4IFct1r6jzXAYf8yqkGx0pICFs0FL3yhHcMwBWcbMqtoXLTXkVxV1nELDBV8d854g==","shasum":"1b7f2764de34d56bed1ece409da8b9d7e46c0a48","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180929201807.tgz","fileCount":705,"unpackedSize":1601347,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbr96+CRA9TVsSAnZWagAAv44P+wb25TnKfVVfhpluiGgO\nB30z/ooYkJh4DM2NBW9WEQlQ1qxrWcrWkxbTxHycpLN3Ajo3ENrRDtzUunYX\n7jV96JeUejMbDtN0PnZvtsreFhWoLJeeTIAKLVAKJ2ujuYcRCCjYkkWwpPBx\np6NVZetLkYdoveY/YHBSfpHYbX9xdD1cdr8UKFm1gU0pm3DRwUPH3kAhlor1\nYH0McMfoF/8jxj97hRl9wHDCWCC9hTKYQ/KMJkH3L2bjJZ4SgTZ7WY4SMmIj\nzknhTH4jdfAaCr8fBxrQ2gIxoOJrqQQwiEZ59H6SnLzPdc8GBnQizQ2Ne/XP\nz46Snz5WYwGncRHd/qakD5nHOem+AtbTmVDNIWeuq1TuS+u86WXwnE6ljbvN\n1F+RhnHTViK82BupnICrO4VGFi+lq193qxTVL1oqBmVmzQR/mqNz3qQRTapS\nF2rJQ3My5agGdrSMZHHS4GF891T3P7lLr5ZJtjVx1o4BwMovSxkn/S4zpVcS\nzoHABZV/g26fjKnH5gyiyqdiDCSblQGkKcbwvWh/DKkc3PlHIwYTR/ZEXfCe\nCLGAfzF3METvkV34HsKSgIwXlhVtcMxfudhGWD+p3rKOJ6gMOERA67EBk8pQ\nQ8ZQAK55/859fLO4oAlF4QWz0k5VffxMtXQTyxv+rGMvItoKV0iNcOS6hfRx\njrj6\r\n=8FJg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEsXbSnKSaDAaPaiQn/Hiund+lXa0uLhLx8kRlM+xziaAiAJBRiRYf+YAhcHWk0G6ym2Gm0WOHstpmd/v8/bFEAUBg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180929201807_1538252477890_0.97219459152241"},"_hasShrinkwrap":false},"1.0.0-remove-well-known-goals.20180929204153":{"name":"@atomist/sdm","version":"1.0.0-remove-well-known-goals.20180929204153","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"63b3d7c982f786eb293d57ee2ac55a8f8e2f76be","_id":"@atomist/sdm@1.0.0-remove-well-known-goals.20180929204153","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fkbNxdWrH4boqrT5m3+Qn5p1BK/nhLV8urXBuouaXXDcZ07w+K/MtjR+ND2M+y1Xu3N/6cOLnZxEmz1EFugvmw==","shasum":"2c963acd9feec423bcf02a260b3bc2634d739925","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-remove-well-known-goals.20180929204153.tgz","fileCount":693,"unpackedSize":1587496,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbr+Q4CRA9TVsSAnZWagAAwa0P/2Eu6iXxomZdjJESoV5y\n9GWxqdfLMiGFZmBu2au8mlCRrNCxE8E8Y+BPRHlfWEmIfmxLLmRb+Tlvbgtt\nurXn5y5PQmzhAPoVOLqguwr1KeeOU9iUlzK+yObUf/UM7HEGjmAOQqCN9d1/\nihrw8alSMi+ZrYT222tNZ0n+pUj7q7+ND8PGCbcG3RUf6I8fEdVxzsdXvu3z\nQfL/4RSqAHJUTRGt/oyx4dnajgD6E45Uz4/NUmk7gLBHpg0O58WjZ1gfGsDj\nS361IvpCmyElyInZIV/ekOlWgrlhKc4PWFb8OGSMZSs0Zmfs+63T+s+F8h3D\njpgBkDH7FmxgPNVLuUdFWN7tF7KeLW3BOMo8aXMVsvhkolRQzb/iIR0xP4oG\nSSmcBvfBFqnDYswJCm1yLThCJxH51jBXMnGIWAEMsUIKu4uZVlvYsHmP5XIb\ng5xOTPdO7jS8ViD/P9l1nBvofbeEFfU6y3BTQJDllF0e0rqVaI/ijMuuXcwx\n+l5OBaLDpl8wvLIyDzdkYdb91BqpjNAb40+VkJswVUp3BdyF3rDzZadMO8zw\nN7jv0bF8uaWMr+YaihVJy1kGbdfjjUJw9YVeectEMPABfAd3Rw0fdVt19keE\nSkNSrTugIWCw6Z8zrR887ePrcMYthOfi04tVswqWk5G0jp9ToLq8yR9A0y0x\n2YUd\r\n=OPJc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH/yvkydpkdr0m5CzrUusruE6QpzsE0hIbBcAgtBo2MlAiEA9EDX4kGzq4LUkWlJ+yVTKxP+bQBv3UYDtoxKAYgH220="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-remove-well-known-goals.20180929204153_1538253879179_0.10031591627161718"},"_hasShrinkwrap":false},"1.0.0-master.20180930191921":{"name":"@atomist/sdm","version":"1.0.0-master.20180930191921","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"9929cecc5654e2578904971faca6e7c45c789935","_id":"@atomist/sdm@1.0.0-master.20180930191921","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DvJiF7zaGa8hDj/OMTQ2+pXedXkbpfRpJxQVA0LxwneKAk5rDXBwC51D+oWC2DCtr286eFGq6dISxehlfCfzsQ==","shasum":"bdb6ebc42435134e6bccd95329738badd47785a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180930191921.tgz","fileCount":693,"unpackedSize":1587585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsSJqCRA9TVsSAnZWagAAIUcP/2zsafBg2wYxcViC0amx\nW15ASwD9AefUjr19d5/J4xWkf4k6UkOEVTEgzUt4RzOdOGbr4EPdjyCsWVSQ\n14/1GJCueMWbwFV8/BMb0PVq3dkii8vElTAAtMJOjzyuvGOVABFj5l6OeaP4\n2hhbkiqunc1uyr5OMHwgDhc4ED8OSKIFYe57FdJ6xTytaNQhsUzfagDsJYMe\nEzY/5IIBWfkymRCg/b1xstON+mmH9R7LrDS6ujr/j8u72b4Wny+n0pvTQnAq\nqcwIF3QK7351g5HJ2Y6hKE2kfzrg6ZhZGxbN+m8ThQ+wKqUy5aW2jv/dbSZu\nUmqIUANh0Gl1LVgnLyl8l+Fm49piRgBnizrQHS0IitY+aDcpMeHri2rMBOiL\nJy9/ZNWupSkRWux6u4Y9Py6QgfkrF2t9zqgrEeaRV2UweTrQx9Ug88+StItz\np9oJ0JzbEYSSZYoa3zdMb7KKr4SJPQhRzT3W+x5QiMOTwvzjtADAV1swLbx+\ncw7cLLBDNVsEgPD2ZeN+JyXnz0/Vnp09xIIwLKruyIORZyByCyO1E4WNr8Gj\nS4Op/tX9ZgNibKgx5OQGhf45uVlGXqPPrRd93REpG70PruikR9dnnxFKXr81\n6kp2ei2hEpdiCCmRXCLvzbTbHCxEQ3bcIKcOMvMuSQG/fDoQvWvQOnUrBXVa\n7EaA\r\n=9SPb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID744yeh5WWTNSU/atMKj6RTcHbCAk3skLZEWyWOwRcdAiBz6FhQLS7n1D5zNTXcq34ihteug9VO43BWuKfYVFGTcA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180930191921_1538335337146_0.41316364886148915"},"_hasShrinkwrap":false},"1.0.0-master.20180930194455":{"name":"@atomist/sdm","version":"1.0.0-master.20180930194455","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"8750fc41d5c52d442e0f0b3d7f67c5b364ae1529","_id":"@atomist/sdm@1.0.0-master.20180930194455","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ik18xgIPc3iDBprZAWvEiVlF7bfGa5uNW2RK9V4jkHn5h4JD/jux1/F7GGFnajO2nTQrXckg4WkQjx2AgijXKw==","shasum":"26805884cb7bf0f8318a0bfe1d9fd7270c9c3bf2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180930194455.tgz","fileCount":693,"unpackedSize":1587455,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsShvCRA9TVsSAnZWagAAEfQP/0W17A512XlNXzt2aGGo\nMMEb6ieAMFTeTZCaWTMZi5DOMWk+U1W/D7cJmrfyaMmTW6HKGsTt+N5yXJxI\noQF4M5zweWbvTx3Nnwh2FKwotDgwkFX6EzTBsTA+sx6yYFL5R4n1bLOeo4GG\nCm3955SRWp3afeqv0PBsWQAB3vj6CIGoLdXE38GvoCWcG4gBkeRfTWSHfssO\nKOIWceSADm57nyHKepUriiR2KZKv1VUVMRaiPkiAqCbyQhCubJ0L//IxoMIZ\nmSfLyCINr3BbZcgqz+CsKp6c80hUKqoRorK+rVCSvQwr7E6OR/VYaKF3G9jI\npQj0i/x24TFrE+PmqNmvPS2H6k/bqh/seLgCcO8iLze/1f7yoI5e/l49ffvm\ndCHsO8g/Fwak5FT4yYC1CVy//00tZ4PhePZOWFNuSBdu/69cg2hFnz7g6zzG\nlGbw45eRJm2hLbhpVhpuhcGJ3r0Op4ApSV3SK/lp7Q+HOGvK38OiKZTKZa/j\nDdW3VZsJoAAj2i31CHPOO4O3bWkFx210z3xiWoD962VBk6+4BZPDAIayGO5k\ni70mna63o0io7XnrIZ6Sfe1i1vNpOIMFCHq9qzbp+mGsv8GZGw9VAyepf1BQ\nFVg5pvGdWeBTR85kHRZgy8uRIg9FaVHh98Nue4Mg+Z7nDSnbIH5bFFzgHEW7\n7pU5\r\n=LpI0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDgAyZiu2EbDzUJVsUdhYVgQL4pqmXT+s2WTmSMFaWWHwIhALGW6HYAqbqPqRKS4PMBPIXGcnZwEOYdjAo3NKLW6hxw"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180930194455_1538336878332_0.6561359223838314"},"_hasShrinkwrap":false},"1.0.0-master.20180930195234":{"name":"@atomist/sdm","version":"1.0.0-master.20180930195234","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"b5728447a36bc835f11ad2dd07197ed3f31a9928","_id":"@atomist/sdm@1.0.0-master.20180930195234","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RUJEJy7vilA8zR+I+AlhRbEt/QBWJ5pWjA7732/vUtlkwqY+ehfC9VVMBFm807QThQG4PvXCD7CbIRns6p8F0w==","shasum":"3a37258498a3d2eb9a3f1d082fd87476de6698d9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20180930195234.tgz","fileCount":693,"unpackedSize":1587485,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsSo8CRA9TVsSAnZWagAA5ZEP/0Y3Gzdw7kaR7tzO6cPD\n4vSxx2oVuy6yhO0Nx8J9w1hok5tEdFr7NAA8RyBWth68dRNwQNkAn9VTb9YP\nrkFah6M8R4fjyAgcuE/2M55FLINh5wbLcVYUCo04r4x4CWTsezxtHFQj4oD/\nMRf7fiAPrcda74qW3+ZBCM/7IDOxuwB7BhKKREJMXI2bteYXxsxw/3+lx6t1\n4UCAUuu8Ex9J7C490GQXSWFH19hjN9MnIu9SHMXNfbOzAS07LMEagde8+KL2\n6N38SkHsbKTgp5v4pSPcuzpFc9F3p2/IvU+C+3or3UjKeqwg+vTh1GBO3I1K\nLsON31Q49pOmxLVMbIaXTlew8dIV3v9j+X2PM5/eAIlD+bXRR3uHFJ8106tP\nOXp/jE60vJQSN+sHHbyYhITDdxgOoI5AkE8MAP9QtKJip0VCrtXnf3ufjWZe\nHu9m0DmER4CRzYDleAHlJYNyrlljIv/+/cBVI4M2yYm93VTibH4XBJ4QKTrk\nw7Kc/O2dJLjM+MLF3f4P48Ma4bvipBOHL/bBiRQ0qYa6hEYmSvnH2KMwHkb3\ni6lyWDYS/plFeygM0Hs4f4vPkEHs1h9lCwrh+H9sD3TxN0T03TS0hop4KcnC\nUWAISCqZzdTmPfRBmLBzXsd8J2UvVRYxU2KC9VBtG7i4dTrulHMJ6eSndBMx\npG0j\r\n=IZoH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAiinFymJU3gh51Hu/z0tg3LBTg/VJkF2wVeDR4MBQYcAiEAjOsXvBpOj0I9oOY1gAWUem0xu9IrUn9WC6EJA1VUA18="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20180930195234_1538337340187_0.0031688934247926515"},"_hasShrinkwrap":false},"1.0.0-master.20181001033214":{"name":"@atomist/sdm","version":"1.0.0-master.20181001033214","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"e3e8b05363c0717e9992798b08882401e2770317","_id":"@atomist/sdm@1.0.0-master.20181001033214","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TZ3QLB2F51Oyer7QzzHtaWUcqd12o7U29HpOxShDqJ+vmhZou1nGK0ttDYHASSSLoodVH3FvkaDYaHsgFg3tiA==","shasum":"3d69b0f3cb020f02e0c0f17fd513802932923e5d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181001033214.tgz","fileCount":693,"unpackedSize":1587705,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsZXtCRA9TVsSAnZWagAAtWsQAJL2JeSGskFLgS9F0EuQ\nf5y3BAPNaV18VKNA5Dxjjp2RCRSiW0egGvp7EfvzOSBKUMepuK0ytCNMQdcM\n2BcySW5PVy0/GaSeNaCW7QKMyhrVixQp4kfRLAPw7haDdtVqBPkHn+40G+9k\n6q/AuOvScvgq+DBFiCqOnbKNvqmzFQNNe4g2M8WmyP/jiKXP3JmBZfiomPlj\nVeGoQsnmm2LgyFsWPws2MM42gjUw2yGGmUV/XUvq4j2Xeybda7QiJC5hK0Y3\nJ0l1JaFbfVtATuS7VYmVj/uVxVuy1XrCFMmHJbTHBdsv0KFrD6bYl5481Q5P\nS1F27ugD5n+SUn5Su8QGWGJXVS9YHQw4dnTWRPBl1JwCxWXlEwUk32H3sb9w\nD5H8lRPfftGmwOOUCLbCvbfIolX+fcFo02lleQzDDsXkSa9ZORGhV+6jUm1n\nOshlCSJV2ZDdNflV0H2lKN2eRM5fGHwtJVMROqQDvGUdjLi1iFsTMx5Lo/Ic\nODrNBD/wfhkR6MMGcByJugrKjOB/XgfnCG20FpuKaEGTpuJX9P4hTs44NiF+\nN2PFU8yDaK4wkLH+e7Jlscn9/weopmiY06pax81Iwm5A3LLSQK4MiFSxC6vN\nISx/P0G80s7kdnkpDX4qi2JOcEpneA1TAspC3vjGlGpoN3RJMgyMvSFj8MV6\nJvfJ\r\n=OMPu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDST5p7K7Rxv7NoHGNKAi2ea2+e1Vu19ipHjOWg7zEkLAIhAMp81DtkwcWaXusPsNrZB09V1tFfAx0iAqJc5Es86QsA"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181001033214_1538364908878_0.25132430658962535"},"_hasShrinkwrap":false},"1.0.0-master.20181001142547":{"name":"@atomist/sdm","version":"1.0.0-master.20181001142547","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"92c4c79a6d92b3baab9840656184a9572886163d","_id":"@atomist/sdm@1.0.0-master.20181001142547","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F7yisXzdz8e3wd5P2sHSRQvDl8jCwakAb/AauDlrt+5DF7iEnrd6ZCzs3RBeewnusZDk4LUi9f8PLSCBG3yiEw==","shasum":"93160ef06003f5a833408c3b70ca85691e75fd9e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181001142547.tgz","fileCount":687,"unpackedSize":1580551,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsi8oCRA9TVsSAnZWagAAQvEP/jBID0AIBlfzZQooNGxZ\nV+oj+KMaNl4RlC0aga3xqsdRoTJqi5HmEZMEESfTWxMxjBQ1NhMlvFKH2YK6\nNUkCnK9h3i6FAqo6PgbRCO4zgMXZNMD8A0/HU8TIpYm4zvA7Taz00decVUXe\nEikJU/ZtofMGMq3hSbWhj4zeWbiLe43lW1vGhy/cd1PnpEhdOLUr8f/QPIOe\nziUKQWd31CS6jwkkWghhbd39vPBOD2xhm9Nh8rVsnC50KnB8GsgMtkZp0cyY\n4zonVf74yN9V8d8gU2nCiqP9xg6NBE0bBKGcsdPoPHonCNuJAyS2mQyNLk7P\nHLDQP5Y6jyp7xwlgkRo716kfo6WC8VgERngWgB76wruAJOenwf85d3VkIvUz\nYxasxmuaJPqqgxppjPpR9zsZdEQdF3kXeDSiUi36V+8PzTuYit/AS7caFGwj\nxMBp8RkIf0LR9FDArNmHjBbLg5bsp2SFSrtYDlD8aU1ZWpoaM7eTFpk5kgZ2\nzJ70u69ODw0k8OAtEXAjKSTozZO0+CCQPLakqH86iRUDjPYiAM7DYAxPFMMY\nT17vX+FbqXo4dZ1wT5XHbd4r5thEFHUDYHMWv2mhv5E+IdNyeK4vBZBu97Md\nkAtjr9KiCfHVn78zZmTVlz1b4wj274wB2o9FwNMgErbGzxWviCnbC4TGVobJ\nqRbS\r\n=9uCc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICPFCQILV4fI11yUBLablcgEko1wX6+57iItHsRseqjfAiEAn4M/ZMzddT6MEdge7jxbo88FA9EUEcm/zXxGM9k/MHI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181001142547_1538404135607_0.5162832494151675"},"_hasShrinkwrap":false},"1.0.0-master.20181001170815":{"name":"@atomist/sdm","version":"1.0.0-master.20181001170815","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"55288cec7b794b323db1644c881ecf4bf45ed98d","_id":"@atomist/sdm@1.0.0-master.20181001170815","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-QbioUGRD8eNLWp6hUcmHe1IQJTOgW8k+0+aUp1kRRGgdXNHrVUAZgXhIRQfPVAOM8ZRPcA1AvcrZ7y1F6ChnQQ==","shasum":"d3473048206aa40bd2ad0ed890bf2f5b0bc468b3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181001170815.tgz","fileCount":687,"unpackedSize":1580847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbslUyCRA9TVsSAnZWagAA/vgP/RfxQwLztPqZoa53gx7h\n7a+e7BPFv1maImXYHcfiVoJFCfIHV5XP8lh5wN4cmao0upmCQ0K8wBiy/o9q\nLHvpX5zIs/h8HTZwvp/658ELSTSBMwz1krH6Ye8ZYSx+Ky5DyzKgIjueZpDp\ngJsnUNu4yeLnyC4Lj7uNcdExUWslRO+sglHigsHI3SSN5cfE0nrkEHy9BIRK\nTxPsq45Mcj0gVG2EMWiAbR3nn/7NYbMZMa2+d/iKHC46+EXxTB4WYKI+1mAp\nmGKySv6aFDvqwLh+TnSaQXBgNP3sn05vK8q5UFHNov6mT7DBI8/YjXOblx8G\nuWtm+qATYJ2hH2Nu/eF1mJZG+rWQMTA55j5aJrb4JgXZH0l96p+hkpu3jiNr\n8abK+OREF6S9QOp96oRUbqkGjKsI6X3v03w9xCptoYt+Yq6OLJHByanhBWCb\noBQbWdI4xooOlKx5qAIsdX2V5/pOl5zHXSHHtAS8PRvBYxwSiybcy9BhSDV0\ngPuWFcWcHUnp4Go/l6Qb2/1uXLQPdSnK5+nkQ6MWHlg3UX3nPBF2AEZZS/Ds\nwAA4XNkqAvRNBe+cRs6F4/+27uhSC+r7fa5LD8kvqxI4NWyP7IflCXQR1DIV\n1FYiZhL7Mv0mDKmgkQ5pOzPHmbSDs36mRh8749UFEqZt8Vj3W5U1++yr3jO7\n2lEv\r\n=xEPS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChCmJfMSKYP7Vwh4Qr4Q5bXlIIToLYqCJsNX7P7agW0AIgLA/O2lYgrIwGz4rMj7mvsS1MuPkOZcSavVzs0mXhDDU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181001170815_1538413873433_0.9807648497486983"},"_hasShrinkwrap":false},"1.0.0-master.20181001172107":{"name":"@atomist/sdm","version":"1.0.0-master.20181001172107","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"06be85f5cd3eddf545fea17c215d9a4c7b983eda","_id":"@atomist/sdm@1.0.0-master.20181001172107","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-u2n/maQcDkrDESYcbWieJ5QQ4Z4FD0DsIYXIOP50Jfvf1w6cPqf6oWqs6rbbVpUsc0ViQjgM+u13dxXkNDF/zQ==","shasum":"5fd4d76cc0e5487ac94e342fea204403147c4532","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181001172107.tgz","fileCount":687,"unpackedSize":1581713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbslg2CRA9TVsSAnZWagAAgjQP/0od+4AN9gsOGE8kw7tx\n+uL76qydL/8kg65ABL0dUr9tU5thANiJbrYmPv/iYf3WEfh/tOifiYREYD3F\nNKbQJxIMBKzHcNHqXvL+Ighk0wH9eZAqsBTn1kOSL6vyLmXbT7iAK3z8REvz\nHKiRK14U2p1l8jia4fJPP7hUZWznXsebsG31YUcFHmERKtJNy5kw7MtJA8Yj\nror6Xxqw/JoXHXyexFLV5bznKFe7jkkgTls9da4XmpqY0OKeb44WQ2R/1mXh\nz8Jz2FBs5OQQliK0/xWsPa2Tf23wEWqS9UPZw4oA5yX4Pzp4eLyRk+tppiBf\nAzLaul/7TEwdWZc924jVm9H1l+Hhgde1h1hSftgOMFEXibhjN+8sKxpHAIgc\nr+pHcz6P17QuzZAKwgIqU2AO4qM/d0HHdRxsJzU/hC3Bc9I8M25K3mVZ1xgw\nykngvZR0Woc3ROh/dpPU4poYvPqaEqKQKp9i3BtoYHVf94/hlI7/O1eXVcD2\nu8J+fiAVEd0mzgGktzo3DKtrhfCFf2/nvWilpuESu9aeY+NlbDC4X3MuUaiS\nRENu+5nyNEqWLT4RmvB+llLh1U+hmH2X4byAFSp6bi9RenrceZyKoGth2VEe\nz/2qaRBEhf1qM7C0BwF0/SUKY+GQ4JgHDFwd5DDhCEtGIAxlM6nF+zp4I2pH\nRFAM\r\n=7ObA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCg1s8dPHtag2bM9M9o901epc85Isp2cgXZv1qehI2SbgIhAPsPaYsZpfyBLewJG8WyBRNQ7jk/G0VV4s4k+wnp4Y8/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181001172107_1538414645834_0.6664200634849735"},"_hasShrinkwrap":false},"1.0.0-master.20181001194516":{"name":"@atomist/sdm","version":"1.0.0-master.20181001194516","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\nAPI docs on this library: [@atomist/sdm TypeDoc][typedoc].\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"da9cb7c86a8bc9ccca49aca8314ac7d83ddb0ee2","_id":"@atomist/sdm@1.0.0-master.20181001194516","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lKqQD5ioM0TlfAj5oCPjd66BsXWtdBFyzPM0OBzmYmYBVzKALptA4lJJNIYh6wJEsK+GFdH/w3KW69a2RxOBPw==","shasum":"59e077aa7124af8bd9ba930037d854fe511a16be","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181001194516.tgz","fileCount":690,"unpackedSize":1584293,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbsnoCCRA9TVsSAnZWagAAoXEP/1+cUoo//aYkIocen/Qp\nP+TVGFRAFAL9SOB00so/YDjx8Rps/WSjsW5UzZ/G2tJ3Hxdto5MYO7dnsEC6\n1jAsXCmM+yYK5Igqkr4ZAMPV0PRgB1o66VjMHx+Wmy9nhW3vkLfO458aMADi\n3yE414PZkGl/jSozeONxklFCe2jWlw1XN3hhpFk0Cz9jwn9iKKqN4L9035Db\neBDWeo+/kWoIbZoMlPaeKI/cT0BJvMPwPyxnhTvjGFUa4LKTSXxteWoy6j2Y\nVD3IrDkUg9nU5SWFgAueKhleKnE474bNfiEsWKg91xH4R5AS+G7YEd0B89+E\nGHFevkkxVYVqBqhhHvG693Jwze65pISBo32+4Ad50jlaDgne0f4LqFZ5/RUI\nZPiEa/1Jv7MemaR54HyJE/SXxRKjP2FaIDXCRZHLETMuf1vaksz6jICyUi6Z\n3CkzcUtbZCTuKDrzFnHJmf7YEhQFCuSUOudOxn87XbU/ekvwx6jXV9kRjxwU\nnZkxp18slQgagE7vEIBno9PrWcZSHvorCLHGDeltBPZ9lDow52nHXNCpZCNX\ngI9r1RNKkXQg0a77jEVC1rGgrs78gjYe54F9eibsIF/q2MhwEYU2PSnpX2OP\n5yuDDFbD0uAEh8seadKaUc9zBotJIWt9IDXZhXkbjW69PMXMUaVC3XGs/s7E\n83ai\r\n=OfuQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC81PzzX/V0hK4m75XZGJX6cXwfSn+HpYltodcAiHnpvAiEAwUpUFr89fjnfOg5A/MpJWVh0haYFwSzeYVaBe9hCEtI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181001194516_1538423297011_0.4497615105397792"},"_hasShrinkwrap":false},"1.0.0-master.20181002152828":{"name":"@atomist/sdm","version":"1.0.0-master.20181002152828","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"acbdd5971962145bb798bd969834e9fa047429b5","_id":"@atomist/sdm@1.0.0-master.20181002152828","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5r5Fw1yhRZwAX7Hr5MtppfGvlblZuwvzKWXWPfsF8d7kffW3b9cNv/IWgE3k+bAPobBLAdwVZ28aqoYOnDJ/Sw==","shasum":"cb8dab86c1d93a43b7f258bc8b9e1a8e0d48d698","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181002152828.tgz","fileCount":690,"unpackedSize":1586572,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbs49oCRA9TVsSAnZWagAAh74P/jbw2nq1bPDB+DGKL2kO\n3tvPg99+ZJco6uXZaIJ2k7xRSrQbG4LlrSgdEiwAMOKAsDyc3PTTKfK03aZA\nsQrs8TxEstX6tromwM0pqEiT48p9jHdJC1HHTl1c8anbs0Raht+6PbKAmvoJ\nY2WlvCh5GBpjMhceQTXWjH9Wn5O4iBZT0tIZrksf7M2AjWqgc33jbWWAGhw7\nED8eoGNUR8hwJ3EL9ghordUJSHAU04qIGCrNnqIBAP7MbBLGN88KQ4urqgC4\n3bhybtth/BhTdmtZF2EEJHUKQ/xn1nd49a6BtM6JI8JM95Af0Qt2cH2o2S43\n/H0unM8DpWxrmVU3CMY8FWmXeyF8tIT/PY1S5Uk8ZmlP18v926UiIZGFHFtn\nJQMAkRSmhBpgDLypc9g8s0BBCfeUcyEw4MJ4UJ/W1/e28IOyFkNMfL0Xzjgh\n/t2wVT3UDg7GjbTPLfgKZCb216HJQR4qRPBGGklpiMXHM1K9kODyd0HGH1+5\nozIvrwMmooIa5jaemcM/PTry1CloDsYqRgXM9aYTzsA8gSBAG4HxDKpIuSpm\nZKClPN/AwMa1x6H/psOBe0k7r5SLErIznKt4+eVDZLEbttsoCY+2sB3IXLPz\ntzhz0ve07QtqcAeRd8g774VFjcEkxD1VaVl4Pc+MK2FvIY21KTM6ufmyOc3V\nWuv6\r\n=lK91\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCoBNf0wSeRv1sQiXJiRVIPj1lTlnKrJW+XEnTaXWhcgQIhAIh+gs/OzU+9HupOIPkdm5fPeBZRLSL7JWizya3qUOgC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181002152828_1538494310818_0.06336816176948501"},"_hasShrinkwrap":false},"1.0.0-issue-542.20181002222106":{"name":"@atomist/sdm","version":"1.0.0-issue-542.20181002222106","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"bfd72252cde1f54bb127034642fc48f5ec8f0ba1","_id":"@atomist/sdm@1.0.0-issue-542.20181002222106","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C0Mg32gBbXDDZtelGThuKTtgnNNq3VhH2dkPlmv9Hcysd26i5VGgFIjyWEn8LvarGlAqFhRby3WO2HkUxirB6w==","shasum":"08f913ecdcdb7bdab78d49fc93b139c9586ee796","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-issue-542.20181002222106.tgz","fileCount":690,"unpackedSize":1586949,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbs/AcCRA9TVsSAnZWagAAeFYP/22fRGoKaOasjT6XnCy8\nrADVdvudgkugNF3QaJzAdK53G4EePkopG7aE5/+H+IqUer8II0JV+AHXD9lQ\novIo3d+I5M4a04q9AlkGdr+LDCy1Bal5ieOha9Aq1KT7NDCqVHA1KNhdpyi2\nZssosnUqcWjA10sfmHtLqUmzZwXCimPwGnoHL5j4kBnnSuVObPD5F8clQkQC\nPkcBJo6UG53LvxDgN0ixVXbqmohvBbw1645DJ0mmLMmpHIOxREXOYhrxn7i+\nT4yCXtmLnpVvUzltZQI3z7OCD3+AODi/yVKHwDQNkjyRNj+fKjnP6Rs4Cjp4\nh13+cx5FmADJmMl+6UgYQyYFvx4YRXnJAoeMk7Z35iDuAxi62+LPiUzHIF7k\n4Fejx8AHn/RdoRCpw6Fd4VsvArvGH0UiADHPsaOWWxHCjtY5lzS50EXvrWmH\n1sOVisPEyBYL0pBm58etCUWOSgvHOcv0V/TGaFfLYPIe8ajVweEgsHc0NVrb\ndue35KcuR4LGfY2MvKgGyGtmGN1X76LjDMSubgX/4uGZykymNrHOG1w8gHmy\nCt0m/Yue8bVuA49y2kc7qBhZSFdV7C911YqElNReluDzXvdaGPK4iAwbtU1+\njehSpinU/K11meTpcF+Nlaidjv4sCQjQiGYvTtX6i9anWomX45atRqd63AiN\n0GP5\r\n=/218\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBeAej0lqiABV0f2frMOYp4VaRfRbyK/QQvseZz++cygIhAMAsp62Dh/yIVWnhI8tP2TBv61T1AH6lgK01MoqksWIo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-issue-542.20181002222106_1538519068053_0.9686879769343923"},"_hasShrinkwrap":false},"1.0.0-master.20181002231555":{"name":"@atomist/sdm","version":"1.0.0-master.20181002231555","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"a8b812cc0555b85e58bf7d8571a342e0f0f1901e","_id":"@atomist/sdm@1.0.0-master.20181002231555","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3O1dr2bqtdaF503oK/NN4qqW+HZhAjG8Uea9jLJeGAJTHNZpjujwVEOMwnoBDJO+8KMnVz0P/lnvnm3uC7f6Bg==","shasum":"493e21c671ee3f613b61d09e5e55b9806b4d7d3f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181002231555.tgz","fileCount":690,"unpackedSize":1587048,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbs/ztCRA9TVsSAnZWagAAIugP/iIpOi7yaII4S/+BWzat\nxZSF1EfNUiYlO2BXftsrBdPZEUhEG4QqF/sNP9I0oeT5azXbm78yYLFj+m+H\nM/tV+v96wHcYKDzuUJ3AYRyW5cD3oGqrA2V32NPi6p510dTn7+vzaFMO7bZy\nGea6klkdwQ96e/7MuCtLYfbPWu8HZTq1s4HqmCoD2yM4olV3z8elFKOZrqU9\nTWJf4lqk2YzMFmqtqjY4qDGfxBpBytp/0BAZOR+zO6S1FxdFq6aFPnTWahM+\nbSxgdylyaDAgdZ+xUbx3xg7x13QMOmh7EADwXWVz7is0KHoOJfoUEyqJegwF\neL8TWAouMnNvz3pV5wZ9v8TJpyCI2jpjsUN99XnIK4ACB6JsYaYd83Tqm7Vi\nyoY94IdcPUZyYeuc+vmveICVplYHqKQmlnuSoF2c35wZei8E5KHthBkIukEM\n+m3Jxq8M58Dw+i66U7Itq/vhICEFGTjzyDVDChu8o9u+3A0hx6yOlOBqSxwv\nuyKAzOHvDpQflJqSe6+IGvJSjt2gRKU0W9ItNWZ3tr5u/jIjccZHDiWGRBmZ\nNB4KGR6INDLYlZPGBRqLdbW4LU3qUKGIiT5IR5rZVDwy/z1mNhdCQO/83/B0\nqdIRT+Sh6K2VcF570ZEQN/Os5EUA73orAO0bhF9XKk95iXGCVQRCWFI9K4p5\nVbcj\r\n=XSrK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGyi8F51fotXfokIaUzq5vjBXhsSkE7LJegjhwGqFfnrAiBbzbn4qM2Sx3jfW0xo7FN+vooWyhTL8wr73oz9TZnahg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181002231555_1538522348320_0.34617607849633414"},"_hasShrinkwrap":false},"1.0.0-master.20181003213948":{"name":"@atomist/sdm","version":"1.0.0-master.20181003213948","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"6af3047c7740db209f3fb6b6761d0b6ba27db99b","_id":"@atomist/sdm@1.0.0-master.20181003213948","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-r9MHt1MKBFPfUy6pTWA4/xnwKz9wvJA457vVdkblg3h4tC8Fk2n2tTwjIEpAzL9+mt5ResLVnxzjz6GUJGRiCg==","shasum":"160925e580bafa7e3040e3635b127d1384280d17","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181003213948.tgz","fileCount":690,"unpackedSize":1587646,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbtTfjCRA9TVsSAnZWagAANRgQAIZQhnGGt79IRiuHqy5f\nr99g2do67bGv0K3Y7zvJS48Ui7AQURu6QgFGG/bNm7MPMkOgSgqJh3rl/HbD\nONRYVdT9yJI2l+vHA2gpLFHRToNy8h1dzTmlPqNFlkJqauX83YhOM905o+LB\nKPmVwf4Qh8vNMfdOPxTz+2AGMoPR6bWHhSGTpwwypMiHPRzepJK/3jSehPyc\nK6ccyTRJFRcRIqyTVBvwpF/DrpTpmYJRzr6Xf/RUPxuRj8O5RAF2YfqT5qgg\nKGwnAdMEVegzHGdt5LuT0G+dpEcmjEqycVRUuqnTsfbuPadOhe8EgAOmzhj8\nSmp/64r8E06ri11XGl6VkntXKcb97MCDBaGf5Nxhs4bMRbOMbD3/QlwvoP6M\nK2tlm+rvXdRjoYKI47hkZ82x045ZxpdHIwNRvJ7N2UZuusFy+kme2sWQHhvh\na/GddSmx+x04XWxnUv4CZclIUGBav8zbFJusRMYv1B3AVHH6/tgfygoJ8dQt\nPZbOCsBG7mlHl2ZTf3wOgminzaoaHXH4VL6C3c/VvDOQC8ClhUZsSx6zpkdA\nb2UcRFzfchOIJOFMgBDkCvp1fA8//O3oPZnAJSO68GxHEP6TS4eopIZSRGvo\nnJ1tKGphsnG22aBFYxgSvcjGqsxYVGR8CzFAXBocAnsaTFfi1+7HGofQBQ8t\ndgL5\r\n=ULwL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHQ0qPE8xTbuNP1XxO/kZ91I4qxsrWYS9/Ad8SxCQTRvAiEAuIvRJ0N12oiToJnGX+iuWXSsig48m67HMgwOPm8e3uA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181003213948_1538602978216_0.14585784652367617"},"_hasShrinkwrap":false},"1.0.0-master.20181004153227":{"name":"@atomist/sdm","version":"1.0.0-master.20181004153227","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"831d5d0733c51391b6d7b2430083d7ac4a44f03b","_id":"@atomist/sdm@1.0.0-master.20181004153227","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2iPYQT4LiQSFEHMn900AECGMK7E5g4esdLta3emsTazSTPIhzht6AYD+eqGLW26XMJk5ttuARYn1meCxYPj4mw==","shasum":"9fa44c73a20b465089796d6feaff22e7161184e0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181004153227.tgz","fileCount":690,"unpackedSize":1587722,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbtjN0CRA9TVsSAnZWagAAQGkP+waWfNvBoglQo2uMg2yD\njwBv8zzQsLxDaHJ1/tRHzYhiMUmt58xY0E1z5Xs090ZgG/ofQDjSnIAaxrqp\noFjoNXK5roAMIu5PjxfiBrXdyYyymEUp2sPeYOsLjAjJDCufv7/9DMrRafG3\n5bta98YvPajkjYweJgajgJ9drkHYjkB/fW3TpNCLe8TDsXCJv0JcS1jHsohv\nHRO5koZyfh/SQM8IAvDzNNqjtIqr+PNXCSxvmDVEXS5MTXci+m1+Jjazl7zc\nmyYYokzgnPXbvZ4oAL92hmw8f+UsilcX56TN56xtgy7i4EfD554YEZhN+WBX\nwDGuIeujy9kiQxka4tckS+pYkgHRjwF94XmGLrIi5f6X+Q63DAQfjxThb9K9\nT8CJ6VH4ZK+vn7Ibfp6ZJqRtuhYHPfXjIA8hCZCdTcW0cl4V+aT3M6xDEj7I\nbhDS9ENMjij5OD3lM457FivY9YgLlTvSbvbDDBqOKkOZNZhWCQUe9rwSaAZJ\nPbEd5On7r5ZFWn1RkG10A8xgsr+nUdyStrQFNBiBYoOE2gRf8mA30SoF8QEJ\nbtLYCUhJweV8QeCjX2/1vdPqyfcSuAW/m5LtbRyfca9X7/LkUDiW/wo7mSWc\nfRm3SET1YtJNQkxLVK7x8290EJ8K0/eGIKlT4nflW1hVKovHBYDsuvJJGX8o\nQ2F8\r\n=GAOL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBKizzZlUFM50NancPqRarejcFMPlPdMY5Zd3Kxg1konAiBPzLOT6DaJ8mzoMuIL3MtD6VPvW/wXdYImomuB3EYjRw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181004153227_1538667379829_0.884788232666949"},"_hasShrinkwrap":false},"1.0.0-remove-global.20181006052454":{"name":"@atomist/sdm","version":"1.0.0-remove-global.20181006052454","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"9ca21dd987da8e337a8c8ec262d4570fcae08505","_id":"@atomist/sdm@1.0.0-remove-global.20181006052454","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YnVYGl+rJyIv0FCpia5r5x3bGKqEJxlj9DttqTBlFtC/yr8cj1ryGUiCvipf2D8qARbh2hpjEe3JukFU7/5bzQ==","shasum":"9216db607c806ea198c3ef5abacfb53888ef2b92","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-remove-global.20181006052454.tgz","fileCount":690,"unpackedSize":1578960,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbuEfuCRA9TVsSAnZWagAAQEcQAKJZezgFXJ5YCoYjawv3\n7nCAFEL2aHInF87s8g5RBbQzBL+YmqNa0xHq3CBfNPqKaUScO2QSQRLf4AqH\n1I2aqcu0zYD8N36ui3ySCaVF9vt3SDUnAwIqXI3kTC3gibSMP4+oDj0CRmxv\n+phGj/EEYqazCsxlWuIi05eiRw4uPc/VFkjc2SiAyOgU4D+aQ9WdATLejQE+\n2WJnYghPO9qRKrLjeDkjOc80CxoFHODhJDOeWNswj2u0SCs6tiA/7jZ1qoI9\nQBCRoUy2WXlhoxbB4ZzwPdm9o8wUmNaGHUEIZN9L0jPrxLdNgm9jO4cCUssd\nsmDcE6kJx64HJxIY5I170WswpKnf5wx0JFvxhao0nqTQMw6Sztghy3Vy6Zan\nSJZsUuSk6ES3610MAZNO9iibbLcWfifq3RFVOnr1U8wILatNxXGB4gdS+c5O\nhf+DraYcaY+/OrGBvuuS7ScOz8U3u1MbXhg8UW2JAYOSuYnUq9y/xHXBdBJ7\nyNBKBypsSvrTu+9f4ge8tqUjLteGCaBwHgtwmRnhAtz6YUcKG5seBMQ+NL+E\n3Eoijoh09j0Zg8/4WKKKJANoEa1j2FmXENJU/OP5wD80b8+sYOh8aDV5ov9a\nJhcmXLAFwb1sktF/ggGXFzNM6ozWWjCglCCXI5LkAtpGBo88Iajkmgs0f6re\nPk3H\r\n=XfSA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnw3FHz5wew9O38W6kCQCMJ40Yyb+7dtQi5KmKrK2g0QIhAP//EzKxzoI+l3vwNnzAfl46yfGur5BtKChfXGaSFhH/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-remove-global.20181006052454_1538803694158_0.9153194666414615"},"_hasShrinkwrap":false},"1.0.0-master.20181006081131":{"name":"@atomist/sdm","version":"1.0.0-master.20181006081131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"2cb9c513361d43639cb8fb86aebbbc287bcfde52","_id":"@atomist/sdm@1.0.0-master.20181006081131","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-362C0YnhEjPc5PWIYJNZiG289tF8Sds14fUUWpJslsZNdMB/ZgAiH59ZvPBEL2dN0nrS8xSrWKvuYqqPQFSd1w==","shasum":"429a47c20f07c4d6b948ee2e197b137b255dd29f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181006081131.tgz","fileCount":690,"unpackedSize":1579051,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbuG75CRA9TVsSAnZWagAAJzoQAJL2l8l+Wf85aQFVEz58\nlwTOG05xm6yg+Jfl/s1XZG7Vko53043v2C2KvJHMzb85XwkyIronMvlFtX0m\nQDiP+w3e00QR/52uUw+U26LAGxVYKWpOdgiBDV4PlLEeOUjwuu5xcmZ1LAGT\nMUwgGHyibzJyddmWOaH4hsoV6zyS+q8hRaFddbw6osw0Eefx/YlWBo3di36H\nWjz15hye4bDJjxWRdAmBUkWOFPcT3/kAXfyFQtlFfLrir6nvVvUSORJlr4F7\nqXUWPljqbcmuTCkYbSwUqpkzf6FkT7VeznuIOFJZRsLbCRie6oszCL27+Wvu\nAik/YxyY9LiCNO4HNCZeT19CfEml84BtCaUJ5h2iY6V96U0649NmEfoGSBC1\nU6HuXe0rTEHxn8/707mBwnyJZ8W9KxRyv4PFJPiiiOriQ3kqGYxR2hKAP2UU\noIBrLraUqhzdupHEvoeVzNSkWsG35TwZbVNBt4VfH3UDjT+xfBEi6JgKUi4e\nkbjIyFeWY2ZMqR1AXnqKeXlL4x0mTfICvDzCNuJCS18NmaFgOnkQqHAzrGF4\nPaZQqT//oIueYWETyQcwMuX61S6S0lOsgxzwzX+03qCFJQo+etdAkIhO+Hn4\nLx9kAhBXJN0RYwVp8nHm0vxJfh/D66jJUem1pW0yRnpu6qRiURvxmKX1adSK\nOdyR\r\n=9VkP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDMA3LkXwROf1iANKxgIKiNFhLBqx9/JJcISLsAhvcvXAIgMbD618pMos8kIYe0CL2pfRGQu+C03tCWyhlMoFobkeo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181006081131_1538813688861_0.2012875504767484"},"_hasShrinkwrap":false},"1.0.0-master.20181006094538":{"name":"@atomist/sdm","version":"1.0.0-master.20181006094538","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"6d385b82549a3f90198a6192079061be733bc2b6","_id":"@atomist/sdm@1.0.0-master.20181006094538","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YO2ErlFdkDzx054aOqE98Qjg2KUlbPMcaUoY3AWmm5MQtu2LBCmQ07xeCeCeRUk4yfltSxI9nLCnhMMuT9UmZA==","shasum":"f365e5bda8cead1ef28771400c6494afa97c95d6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181006094538.tgz","fileCount":684,"unpackedSize":1576285,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbuIUaCRA9TVsSAnZWagAA5zcP/Apny0cNcCbmGIeGuoNZ\n4+JdmPXtCuTizADkAgUlmr8t1OWCqAWA6mhfpWcc2lBx30mf6YzzNkuxOz+7\n8Vl6TiZOYgGguG+WNAG9k9l4+iIvcSuDNhLQFVOhaxBb1nMkG1HfV8WZvbq/\nzqB7Rg5kvxC3vulzrYVhj3yR1/0oi5FAeyxXsYvZYrBpua4KiFNXV82/8eJO\ncDaevV1bWXvIdHP065XcpDVXKAJpW9b/G4k2hTQ9Y9LAxrkIGMj03YRymZnu\nwk3oQh0mswrQGoUY8eUDPoZpACw2gQhkcP3Mg0Yb3iGB9Ccn4nrD+zfrUSgA\nPXdoEJCxOaiLzRj8UnZd0RdEhFDXptjiNty9flDVSwwzJOTT+Jc7f8qvS/Kx\nUUG5WH6JwpD3lij72RTEPJ1Imwg7PmhJdzLQskmYdns4rmNmW+g0rRuJGvMV\nog6u1fUNd8oS6VntRlAPQUgSE2Edn0ISh8wtdEFiO0cgDioz2Yrn5d+ui+IJ\nqPDb+AItsQ+gjv6OQsGmozVPDO6vNnH1N/pg5B7PaELSmmk3ayUbhys5fXi6\nImxnMglDF3qlO7SryZi7uBHHE58P+7XqX34F8HFh0SIOfcVeE5bxiUMmN+ef\niJLbNNVPsg/e4p+i73m75WOBr/pctUj5qbZv6tBzfRtcNKpTkWl3w/Edq+KP\n5oTM\r\n=cStB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFfM3DUjIktKzkWTznuHRpeTeGhcWU2xKkgwq8tsG+ZQIhAMg7OLFkaA9UHl9zii6/IhIgYA3m+a7MKwo9D8xIVOeV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181006094538_1538819353469_0.6635916707921392"},"_hasShrinkwrap":false},"1.0.0-master.20181006100238":{"name":"@atomist/sdm","version":"1.0.0-master.20181006100238","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"623b60251528784f0f4c60e46249c20e0a5a2795","_id":"@atomist/sdm@1.0.0-master.20181006100238","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qq6dcFhBpxIH1+0ZV/un+Dfkz4x6U1DllZ1d1SKYX2e55AbdH7kLzisfEWEHzrWk3OPisWwd6/vIsOMu2sj0VQ==","shasum":"ea6a4cc966a045c6680e849c308b012d7b44939e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181006100238.tgz","fileCount":684,"unpackedSize":1577675,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbuIkGCRA9TVsSAnZWagAAcaYQAJWarkSfnQ7XTLjQugJj\nhw/hP0yDS/O5blwpLwcqO59SydtfTkfWXcT/2tknWc0vQLg3m+NcuuZlLnT0\nvdZ3/vCwOzO1d3fb0vrKzgJPOCgc/KYyC5OQV/D/3wmnG2K6QpbKmo2xUCQK\nglbcpzJQ0lvbJPTU5KjvsEv39HmskXhNhaKdVMFNbucxK58ky9tmx22vqlrW\nuIgmfLHeT9ZHHX8utTBc/wOC3qQoW1pQ8TpvWp5xpKyTKo2zlVLW3W/+B5gi\nGvRSCW5FHwUuuGgIr7puYFFWMjLMl4Fz7SBQG+wfS4bKlbZg5EBMlrWNfR4o\nCAKK/5IVbTkdHrbrMAWCo7AzXUfLxN5nJgc1jvVzdeuWnqnRRpRn2WGeMzhB\nVAg3qKOOAdRkefOAUuPHPP2DYVCGmqJW/5nYcwotWIRjR3ZJrczYCrnu3jUH\nW6WDTXWLvMEACdBz7SE1SvU03jg22XDLDqECqL4Qojnqvphfe75NA1tgz0fu\n7sqTnRj2xcaVGB8O/z7cFL28j+3Jd9mByXDiVk3cw1JPMSPQOlMR6Ut3mGfU\nMa8TCN0rhF5sskjxAytikiUrqX/Q2r888AbhgyuynOmGzG9qNu298NOb4O0V\n2rbdH9BMcf20wrB9x6Jeer3IF1u7k0s2iDx2vAvKztdjKBeiCPuYqX7lLsgr\nsiWN\r\n=6QN7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBAaMftW27w/B5PpTSMnt865+4FmkLsBbmtdUOZkO1k+AiAmiZwzkQWdB3uSBwREVZr34CJJa0jvE+e5HshR3eThdQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181006100238_1538820358167_0.6056397400129607"},"_hasShrinkwrap":false},"1.0.0-master.20181007092752":{"name":"@atomist/sdm","version":"1.0.0-master.20181007092752","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"1cefd7ff6068e89985eb7bbc605908bdb3fd99c6","_id":"@atomist/sdm@1.0.0-master.20181007092752","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-71hIZ0f35jyl8CvJcCXx1ub4fZ5cnPsz3IU8lWF54p023yFIjKDxKHizWvVH4zYrIHJ05zbOehDIzv7f098ZMQ==","shasum":"59dc52fca5e35b598f73d8a344c8ae429e2965a5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181007092752.tgz","fileCount":684,"unpackedSize":1578557,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbudJfCRA9TVsSAnZWagAA+ewP/38RoSVtBtr8vRjVj22S\nnJKHqxZHLzPPPR/RPYuqpsr+9rHwjFNLw+A1bcIZFw7OKpqZi8wtwo63dwTY\nHk8PEI4HtdcDgjLJnsOPM0/x6K68VsrC0hPe5fGaLEccBKl40ccYip143W0a\nDRQmL4OEMpH6DsizlefiaO/QbaEcR1CbyHsJbEwa5DWnXOXX4eOPY7nw3EH8\n5NeHEbzeGxUQnMBUD01uCdkFoVqaL1Ac6J2fNTXAVSvDHHJ98lbJU2WndyfO\n4Q1kqKWETSkKqcgZKr5owLZQnr+8PkEbuqFPfoJPsAoWKZGgcq4KajinTnSP\noLozuu5cPJ8ccQypsjnHOzZjU5bFLB3NPaLUvmIuFhQiF3a98bEUNfhY9ZZG\nABOP9x4rtYVdZHD5dq/XVDyBNJKCFm+yGKeBfzLSeJ9vymYh861hkdtYZqvl\nz0wWV9oW7TuYdAaKJL5WuW5dm7CvSnJ1nCBie6Nbckn01LHU+is/PfI7bI5i\nfvy4do4YjkJRrvaN/jSuzhmJLfXeFi/aBr6RhPHw9rTLZpJa/qwwikt7yoUG\nsJdZvu8MEgK3OLzkUIUNU+llXU7ou6ggprphDi+rbGFNoho5XMVyyAvzhxOs\nF0boGG61sqrDILmBKfVwRZjp/xZHjKDk9g5UCZwMNZVrAJL51xcYQo3lPPi1\n3MJs\r\n=Nw2U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGafmJcXtA4/n5Fw8u2kd6mPBC9oWDEHqhx5mO4Oz7qOAiBdGRDMMDvZ+5dmGD5/bskJcJPooDGfIPGxMuiTimPFGg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181007092752_1538904670848_0.6791794231906785"},"_hasShrinkwrap":false},"1.0.0-master.20181007193107":{"name":"@atomist/sdm","version":"1.0.0-master.20181007193107","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"c8feb8a8c5163eb36a9673c5ba696b32a108e889","_id":"@atomist/sdm@1.0.0-master.20181007193107","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3Erd4OFpxunFQ9OUxFJvA6DJawoniQ1AOuIhhZata6LWfK6qgYHFmri7UH6hQmUvhCCLmcJqExVuDNarEws1vg==","shasum":"bb5f18ebaf0fc173018b8783681523c6035db335","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181007193107.tgz","fileCount":684,"unpackedSize":1578523,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbul/MCRA9TVsSAnZWagAA8OsP/0ssQt8olsUz+GcPjpFl\nryfn0YnGbKTxsTBxWcBlJsw4dOcynQAGj3nuBgb6DyE0QLWrTBKbjNnZCoVU\nbaFCdxHzjybdcwCL+n/C9iis1fknh3O5k8J5jxnjZKhm8Mt+jGnJSDN2oNy0\n+Hp7MzJbUpMJ984LKW9nf1tY4Lv2BAxiw7FJI8LhGHpVemxBHNe+VPpD4RLQ\nIQbDxQASDKV5RypMoMCvWODTFqI75fYFEnMl5C7TVHRfH30OpCL5gKzyu++S\nCNrH3qVLu9cnWw4HybAbpi0Si4SDyyxMa160uuFnCvYDQeOxMU9mzB17vpOU\nVWI9EWs7c0HJ7aGvVsbUmCW9wodkflrXw0UOnZ2N19FO3GsAiW7Gct4jrd6K\nz/j5wpjyaEsmgeUkQkxdUxuFxcJK9XUP6mH6tfyE+2BnYYx43Vc7ytVS7vnL\nBFsVL3ffFt8+MyU+pRvQbDjOau44VjYltXPE2gwGhQ+0C9364hxIzGuDX7J6\nRAck4p/qyOh7U0EuipM1SXt4wOwbLUAtO4lpAcmQkMYZE2RHEAJ9dPOwvcZ0\noytKjfqBcgZF6Do+JUi9iSGKOKU8pw9YKCBOE/BHzoj27zNSeX7rUERe6ZyM\nJU8jwVvtYpKXzi/SH4StRxovG2UIrzmryNzXJ8xOiDnojDYxtQ8YRNF2d5eK\ndhzy\r\n=FPV3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbLSFkUW2AV2QTW0RneXu528yP+/lFYYzrPZrnw3F1yQIhAL7sb9kPGnlFF1Oaoav+D8o3I0KQq3MP12COxOzRYpxa"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181007193107_1538940875521_0.8933922514471142"},"_hasShrinkwrap":false},"1.0.0-master.20181007213741":{"name":"@atomist/sdm","version":"1.0.0-master.20181007213741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"1f7c7106112b71adc34926fac8980d6b84936e7b","_id":"@atomist/sdm@1.0.0-master.20181007213741","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-19TqrIBuO+dm3AOccGX9/YLzPPQ0IlqKR4cp1JWyBaIwnX30ntoOGX1pCDhh73buw3jNlAgQGyruUa0pR0qt7w==","shasum":"f15c100fe0536523675fd37b0851aecb5c5eea02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181007213741.tgz","fileCount":684,"unpackedSize":1578507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbun1cCRA9TVsSAnZWagAABq0P/3j/NyhqwyS4pdNuIrsH\nb6EGi4vQ6zwgAjB0bE5PPryLSYofxOGDCSphEGecR3Zo8Eph1epRgUFLBGwI\nv3xJ0m5XQJUPfEdBVhPPqe4xkIb0OGXnzi7XruWm7sn5dEKkF8Sp1mr9Xrau\nSphHlTuSL6Khsl54roN75rXHaU1vwGN85VRxVACreP/mE76PDnH7qysqen3I\np5KkIhILyatsiY9GQjHk1YSv7kViJip13Ba7L5eeg3QXKKPzGaj7wmo27s9u\nIYTFZfXRPCwcpA/AyFkQlmD6X0H6nxIoeJuBUAR2ZkOzpdLZDJv1GpqxXvmz\n3t3Q9qOFvrXx7BIrIjKokwGvQ0L2StqK44WpAnN5sFkWxMqjFRw4ZG1u6Itl\njJF8WumsyqkNhL1Oe/E6RuVbn52pLT9dcjSWt57Lx/QZzQEoPfCy+OOdMogI\n1jcO6soNfCUVa7Mf9WkO5PWcNQF6e903cOfL9KhYjl/aP1RpS1+Z/20DEWcB\nleceoU9kTqqe0XF83i0I8tSXdWyqz2LoX1anZZEwKlGPJW4po8P5ccFbCDbB\nvVMOp+dtHZ5FP164u2KnAmJ3Q9eJzBhtojl2d2PqYHynElE40jaGimOK8sMP\nD1V3tUg6/TPUy0KFUJn6HISXbg2+Z2h1V9LOrniumAurNuVCpr9tz6N9MDQ8\nSPcg\r\n=sQBz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDiy7NJZZwFPel9P2SiedomiF/CNUj24XDD1P0gyUVoogIhAI/eFKcosSwgPgGO6QMIPQal3fuKjX282DJ4GMeLmZ5h"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181007213741_1538948443490_0.5469245709828638"},"_hasShrinkwrap":false},"1.0.0-update-master.20181008123217":{"name":"@atomist/sdm","version":"1.0.0-update-master.20181008123217","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"^1.0.0-master.20181008113855","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"ab3b5f227095a260c3747b8885554302ac708390","_id":"@atomist/sdm@1.0.0-update-master.20181008123217","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dBDcF6aCyjBR0d3vI9RYYqh9yiZOEjQ5aaU8IYiYg1sGluwd4ZErC1r/RW34IDdkPjfqutiEFcFAyyawcgpEjA==","shasum":"191b6e9d3c9302f219336b588514d4ecadf45084","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-update-master.20181008123217.tgz","fileCount":684,"unpackedSize":1578486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu08HCRA9TVsSAnZWagAAKooQAIkO29Cj59qoXJCLxYoW\nwCAb5qDTUstSbUPwW+WtJpr6in6LqJyxEn2xVLq7nwEV3W/X+7Xsb13jnAZx\n/n+9RojAuBnD+qlq9434SaCxxiBsFN77ECq8fd8EjwWMPOVDWS0/2qV35+I7\na4U+1lTrZbIi9SDXMnYkstVhbDB/QTUqWjqUD3FfZcTkLN9NXznNJ4CPtxmT\nXIQqBRLSHujIOEZco+xV0FuLI1wMlT0i4AzbmAh3IdYaKIocIeXTaKyFtdPd\n4z7OGQInyryl+jQbPI0gPDrKtz52NI+trN7W8z7UXqCXnMLyUB0J1phYTjZ1\nn1DuLx3M5tRHRmUHsghVbsn/YsCWfDvHV4ny3Af0iWvMGzxGJBpH4iq7avMe\nv4ecpQr7rNI2cAHjKUuN/WZd0f1B0XRiWqujbzsvbKGeqmPPwbKuKXLUd08+\nSV8nBEA1TJlvBmlexGYEJlJXMIY9atBCMfoY67oIKOffbONTrxAmew31Xw/7\nr94UpbM+cN8zYNi0uJoAXp7eDrVUrURWBfkOXwUMqXVJVmeuMBS6eTzNMGS5\n7AG9IfA4F4UYzwXkeSX9x220+VTCRx+h3c4QMqIj0Bf4J0REZIJSy242AVHH\nPUyFqYW5TZsa2rt623gypjPydo6p+uHvpU4KDVijWY3lGrkpcv/GY8rrUFsV\ngLc4\r\n=VMbd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGCvt+545lQFhJmbJfnXIk50h5K1SS3v7W52X3w5E/PxAiATB3o7VAJcmTjwHt7aLhTyF3Ayh6NotDcN5ElS9ZWS/w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-update-master.20181008123217_1539002118661_0.2768023136082569"},"_hasShrinkwrap":false},"1.0.0-automation-client-export-544.20181008132305":{"name":"@atomist/sdm","version":"1.0.0-automation-client-export-544.20181008132305","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"devDependencies":{"@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"b0860769f08ccb6789f2c52252b19d4cd7722141","_id":"@atomist/sdm@1.0.0-automation-client-export-544.20181008132305","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2CDz/t56nH88Ue1pyOW5OJk5sE3URGJ7WJrj5BjPrARWkfb34wejmu8zEKAwutx6QfnRRhf+7ONjiWI3zE2R2w==","shasum":"c163014050dff799dfd262afb5427589bfae6834","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-automation-client-export-544.20181008132305.tgz","fileCount":678,"unpackedSize":1585851,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu1rnCRA9TVsSAnZWagAA/mYQAJuS1dAknddbItzrOskW\n34cywoh8YqgZ7P1R1JHAPCnY45WrwVKWAZqJogFtfJnUEqpCIURZy59WvmhP\nzwmtia7QgMHqeI2NkzQ4GUQD1eRaybqLJd4Hg3dqQyRvp8iT41SxFVl7f/nx\n+AG02R/VbchScB8o2si2Cr05pdWVw/7ojRHyWz0YWsVwnOIu7/BUA5tBQaG9\nNkOgiN8fkrjFJ4HGdB7SqfTw9vNQx5SekuYtDK9WgKGEO98ATLgNcmPSEEvg\nKHSEe1jce38ky34ZP9KqWtnvmeSkMSZ5kQlfZQWAMKOt8iwB7R2d3hM02Nft\nibgMmzUtHmrawLyqLFsBF3oh+uuch3byq4BY1LSaq4wuvTuPVeKvPTLQlDkI\nWcMW7BHii5hyYJizj11nDdZOw4PYRVwgOrQ/NdL26BWtQGWRdZHcVKBhzB5E\ndw/8VCCA32eVdEdzmLbnNNJOXlQ0MQT1Xa/UilDFi8b7vQoCsiPL9nh3tis7\nNZzdsxPtBnk+H/Jh49yLLEdpNUORHQj4PtmyxW+qTbPsCb7cJFjDXF2EAKfh\nHibylxqCZn36z4G1AwqR+cMEiwEp+t0m/j7XRz5Jod9jcXn8A7WCLlKc7DBW\nQMQyU6bYBBCVP2w4I/LP67h+dcfeAmaMZX+wPExV7bAKfjg2RlcHqMcJkvuc\nIzMK\r\n=6dEL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTfLnbsK7b2HnD6igJ4aBbOjLVHnL8IoCtkTI/aLP4UAIgM40uEf66PXnl/4xucCB3Z4D1h71IjpcxkUOrko0UCFw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-automation-client-export-544.20181008132305_1539005158744_0.3210482274156643"},"_hasShrinkwrap":false},"1.0.0-automation-client-export-544.20181008132956":{"name":"@atomist/sdm","version":"1.0.0-automation-client-export-544.20181008132956","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"devDependencies":{"@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"cd6a66bc8dbabeb0ceccddf17a42a7234dda9a3b","_id":"@atomist/sdm@1.0.0-automation-client-export-544.20181008132956","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gkcysivq0CXEGoqvWlKrVfTvvS/m87xEFVyUgQEVR8/DYXhComndRdLO/7wniOQVL2mG0yuNgRkHlkf7ptQApg==","shasum":"f8ec5e51acf9fb9839a7c3e62e27714459a73780","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-automation-client-export-544.20181008132956.tgz","fileCount":678,"unpackedSize":1585840,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu1yYCRA9TVsSAnZWagAAibsQAIO3WFR7Ke7kg9/20iLD\ngwDiCYr+gzkGUGPTQF2uZDZFc/usatuP1aN+Bg7k3rdQMVgZ1R+Ohqe1EIuL\nX7e9ZQ4aq3yAI5vV+gPoE5/GnueM+f0y1J1QkkqnPqJaEKmMuZTAZfkUMOsf\nYc+v/QLS15gGobVfqb1xn4OtARPS1GlvSZJh9ywHdoIvJVEomg68gDTNqGqO\nxcJSKP6FM/y7CBalbPtBqfezBhaZcT9RsE/wFYloU1dI6taIwheCptL3+RnJ\ndEtnEmJbvZmFDvD9igwqBP91nTmgeIGk0+F4eEjd00spbpcUszijoTDJo64X\nhod6CbknQHV4A2/oT8saVs+m153YmFCR0ujMP/LRX3qqAsKeLKvHVObrds2t\n1u8w4YEgwlYNQOvcDCt+HPtciXGqbxqc0BYYtEBbowRlCeyJfuneK2vVp2E/\nIyP9bPYRe2CbiFHrwbFTvkQjeYVjNfYTvJ24nNkv7EtGeP74gW5HEN9K93Te\nHu7TM/NeEo8WWAf73fWiWBzhZyRtU9YKOQFJsjMR9eHKLa57XVnQvwnbt01X\nfc7ogfZSboXDNmxaTeC66Mlz5YRigqMBdHlTpk3fzcCdP/9SyR1vBObZjywO\nv+J4yWgVG7EXAYMNt960BCM1zyr+SL1/2Lu4huLcSgHQrpgfuI6V9NgGRT/j\ntORI\r\n=duMO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFv87lY6Zscupfe8RJz3SrdI7uPY8ufhPjI+gBEOxa/DAiAD5LFDYtkS1ZwKmWV+r1chfwI+PP6nbzGfSAqpKKYNhg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-automation-client-export-544.20181008132956_1539005591467_0.1041673788828621"},"_hasShrinkwrap":false},"1.0.0-remove-add-goal-sideeffect.20181008163725":{"name":"@atomist/sdm","version":"1.0.0-remove-add-goal-sideeffect.20181008163725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"89110e6fb4737b029d99c217a0dc17365df97a45","_id":"@atomist/sdm@1.0.0-remove-add-goal-sideeffect.20181008163725","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-C+Um8Cq98kRTF4Z65tGLByEeaDULCvQ7sP6mJNoUG5FxGtuNGYOAFfjl9UP7JruI1bHS4beIhgefVzNw5UUE1A==","shasum":"bf1427b1ee085a785cf714f274e33790e99f0d4e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-remove-add-goal-sideeffect.20181008163725.tgz","fileCount":684,"unpackedSize":1567853,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu4iWCRA9TVsSAnZWagAAP60P/Aj8e7sB0MJlW6SO0xuD\nkLxuHFIp/lyNUhUAVK2mfxdgY3bC/VT8ElueNBVl6Gqn9wodpchb6KVPB4bN\nkxQIFT2HG0lh25fa+NSg8Tr/qO58Gn7FA3xVkcxp9REWKZ06Xkv8kgeEha2O\nBiV7OnADmdjakNDsua4nogkJDLriAecj48os0EbSD6tdxD3T5GOiOS8PGl2y\nNz4Fm/SV8xg/8mb5CJwaBs+06TBep8uflRNK9zoP/CDua41cJ79bWrN9kO/a\npKYqlsFtFzbIXJAZJOmUKJnYXla8uLeeAiCPMOuE8xpOYj8kO6mMH+N4EeXQ\ngUksctJ4lZyrfHXpIXc2fUitA6kkQV2meXE1t6T2r95kx916vW8zpPIS4B2h\n4VHwQqMRWkuEpv8JhoB0nBukm18nP0jTyit41jWhjzMZ+H6JqY5a2ZDG0TTP\nTTnxdJQOQN5SBxbRlJICnX9YZARbE0eW4OCObqojFLzgZlfqWbzqGzsbl8B9\n6jLPymXHx+vAhY7uyk3RVEXsGLL9mLzC6st5x6HpEFK6pVnQqmMCWjV+aEeB\nZo4I1rszbUx8n1oHgRlTP8zMUV2b5MnSfW0whb3+6kHsc16merwM0Vl1k0KL\nWDYpRnCtBwbKTEnQx4nFcC8vtlcVStjoCkfdEZ3myh0hf6aWKm6+e2KPRV7U\nBP7h\r\n=YxTo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCXoLGvTq5rvxxKgHkyIMMFS8PxmyAWfvpMAkZFX/BCbQIgBc9OnEMUMmhZygHsdStTIzS7LQ33/hAmL/8eu/MumFA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-remove-add-goal-sideeffect.20181008163725_1539016854090_0.5234106546837416"},"_hasShrinkwrap":false},"1.0.0-remove-add-goal-sideeffect.20181008180252":{"name":"@atomist/sdm","version":"1.0.0-remove-add-goal-sideeffect.20181008180252","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"e0963ba355928eec6c9fa72ed15473b90ccfe0b6","_id":"@atomist/sdm@1.0.0-remove-add-goal-sideeffect.20181008180252","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-M91y9Ue11JHsE3iIdNXmsMmmgcO8Gumbrtk9E4hyqnylfX0XcjEjuP10DHh3uEW0T+qAen3RpfMac4h7cA+mxg==","shasum":"237fbab3453727fc48821499f69180512d3d9588","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-remove-add-goal-sideeffect.20181008180252.tgz","fileCount":681,"unpackedSize":1565568,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu5y5CRA9TVsSAnZWagAAEn4QAJRv/0omvtg/oo6+2hxn\nL6N4RuePpUibOnPkV8IMze35BAzn0MmCRPCF2josykLt5cW1dvkxkXNDxw89\n3UQ88CLl6wd0pES5qiThJyC1s7AuQ0d7/0wnyIZ9/y5Ke4ouFPyJcHEdwJdQ\n+p0aHQ6+X62egSWsmK2uh84zRcojlQtqsqidPsCnNlxgsoxQeu2+YSBh4o3R\nL36Z9H10UevvsDugZmwWK9T8wM/HCg0oPBzI9a68kePC0phr6pVOkPHn4qH4\nHoUNaZvfSp7tZpUi/iRUX2PjIbiGTb+OM/HtQyteWllC+unrf2BWmjDk1ATx\nOlJs/x37JbRdRMMqHGkSOca4DgQmzQpSTxr1BcaHtT/EzR70Ph4YOggSkhA4\nv9pW/VTAPL2ezM+KpjPG/uEeZXsQx3kkaYQRcUzDgUmXM1N26N/xQk2leaJ1\nx1f0kZM3hYClEl13FRYsh5p4sByTBnQJDVKEg9injeOFSwzmoLrn5OEYoLU7\n4z4QG7gQaIJf6E6FU2a1op35b1g36BAlLDEWqCkC7L/SbwXPqwPDm2Gp9lLM\nXlLWb4ayCaYhaSr5dSd80xTPn9W9YSmlSHEI5yoQEnGnex1bVI/CFgB7iF9h\nBZiUUdeYfm75EsZodxz+8zPV6KVucqkcAEM7IdhpXn5pt78LOB7TlTlgA3kA\nKTeZ\r\n=89pT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDa8AHPOB2ukdkEjaKQp1rwyTjsTxjJOfdKiwVrqJdHTAiEArn29+B4k8Ded4yT/0g8c6zbrkYb++c6FMWkigolSKfo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-remove-add-goal-sideeffect.20181008180252_1539022008241_0.12011669824035631"},"_hasShrinkwrap":false},"1.0.0-master.20181008203922":{"name":"@atomist/sdm","version":"1.0.0-master.20181008203922","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"d3470b349ea8a0fac1e074c95df962147b4935af","_id":"@atomist/sdm@1.0.0-master.20181008203922","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3ayiI+HUKhhbThzqRgRkGX1WxqFTbV0YyoCXx6CBcgNDXtl/UBjBbLycNTrAe5/hKq1ZvXwTHlXFjQR12ByxAQ==","shasum":"265441baa90e496c5281dfb52142fedd32e4c81a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181008203922.tgz","fileCount":681,"unpackedSize":1565654,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu8E9CRA9TVsSAnZWagAAvVoQAIrd8Er5Xa6JqB116oXF\nurT1PcAkoFCspBt4QJNBAXh0Cv5I5lTW5JzAuGK85v7rkXhna4pb9OoBEVwe\nvRqWqD4+KdyB9rrE+TPsTdZ5XnRkTj4fVijpW1Zhy+ngF6ietxOPswtiJNvk\nlqXA3R/LsodvmcN3L/Wmw9N1hD86EZ6nrB7Cc4Gr7ZQ9s/FX1lH9ImbP70H4\nykljVXmJXFjTdqo0pmg4lJSE3ssh0tbfUIQ+SQ6JEPqIeG5X8ipCyJRGgS+1\n4UkSC9Us8qT4EOWcSu1aNy5Bnd1+xEyeFA+QxoakhMmlOh7Hxhfts5laDQdG\nmmKDJuoE5/t7ucc8NXE9cFxHNNMOZXxSMMOxSeiA5CIpfCyQ8DHxQmEQmv/f\n1V/YHA4AL1T+0KoG1aE1s1WBkr5BEfdhAcwow0jlKfG6HvIewQ2hU2OqmTzc\nhVFkQBnrl3W//nIlV5ijZOyIvf+JX2v2ZE3mhD4eIiIEXMjpOwJmtBbY7LYA\nRzo/WVeQ8Ub1cwz23RFgIQnlxy/pfLZVZLxd9su6oUJmwolKsp706WCyu7th\nq50AP8Qzh0v7bhVR1MumfXb+HYGoS7jGPwHlLpOEIc4kijU7k4X4Zvji7vvM\np4toEhZqdf+hQAQ62eDGfWoYj0nuYZcIqTaEpt5KZADCNBtz6DB4fsE31KcA\nIJ6u\r\n=dzDh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDT0E6tSheNN1kWTvX2PRIC8kubAgoHt+H7pZ04R9yJAiBy7xVCAW4UMdPkP2xeFugeWWVpCwRYV5kvYTLmgNCPww=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181008203922_1539031356868_0.8338216360869031"},"_hasShrinkwrap":false},"1.0.0-master.20181008215342":{"name":"@atomist/sdm","version":"1.0.0-master.20181008215342","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/slack-messages":"1.0.0","@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*"},"devDependencies":{"@atomist/automation-client":"https://r.atomist.com/3239322896","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"eea203b5a1d88ccf17ebf67c364316edf709f372","_id":"@atomist/sdm@1.0.0-master.20181008215342","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vlMPgK3TZLi/5jVoXoNPoY3GAk/NLUaSA7HXAFtNfVfV/kfqLURgNFsxhatoOST+amBpU3JJkSf9mdsA4JtAkA==","shasum":"3313539b8863691f3e757c4ed042ed1265db6aea","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181008215342.tgz","fileCount":681,"unpackedSize":1565655,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbu9KpCRA9TVsSAnZWagAAogIP/0yw84IUPt7BEJrh4TYF\nSIkiFBekNVhVYF30Z6IeWS53v6JGb4XFkw0ENETMpXG8/7nwwMCP2Y8mAwA5\nPdJeMFrCituU6gLSJC80mGtxrhi+Oxd0Gcj6NvipOKlxShVLk45PkQBMyXeq\nAXTvY+DWoumaY4A7MCL0u9O0GuB55R2vVl+0rhwnHSf8xFZfcy7FUH6DXoeW\ngrRBei2paWZUYR6aSgT6WVH581/By/5M5J3raSkQRXNZ+Y4NGKqK1sC2AjmX\nmVwQAZWiOAT6vH0JBm6dEBZ/tFlHrr/t8eBI0+KaXeljeoCrHYzwVebyzSb9\njvgjxdQPcGCG1Q7cODkE4Yb24o41E4JLIZy6razIPo5Kayd3zHCQu/Juwhn9\niRDCrlMVIiOJ6r78BwvozVVSc7chWkndPCZ3R/GXSnXZ13XKrN/0HxgVve1K\nUL8k97/ZdrIXLxUhCtSJ4ch/Y10+XW22ZaloZlbDoBGFNt+N64+J7aDyEIDH\nDAR7yoMoXEt4o0qjBpIcP0BBWQV8h54xUXeNk5NQWDfpF4sfGOqQjDEeco3E\nMvLGo7ImqAv4i9FQD72UScDzOK7NJieX6NrFp4CV8CsxaUEg2WjhdpmdQi00\nL74OngAX2zDMdy3jYUdH7GYAE6HB+bLltixGIX3I4OVtOxfyosztVK//sE/I\nTTyT\r\n=ffVb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFbEfRpy8+vHhZEqgVvPGYbjYKc9WL4yft25d5dvGUY/AiAe4i9DQWukS0M6Ok8Hqdn6G9Fiztx3PxDqjYfFibAtGg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181008215342_1539035816643_0.8242259233187776"},"_hasShrinkwrap":false},"1.0.0-automation-client-export-544.20181009011731":{"name":"@atomist/sdm","version":"1.0.0-automation-client-export-544.20181009011731","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"702f8469cb3f0c973bf33472ae9a9d48f71a4832","_id":"@atomist/sdm@1.0.0-automation-client-export-544.20181009011731","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wl3IKntsAFmqGJBwgyC/bVRrPXqOb9096efFKaL289KGoh2amIrRWpEbS7JIEfEFsXMEDSp65Zn/7pjWH1pfdg==","shasum":"adea82e5f306b7952c707963bb8e92ee534773b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-automation-client-export-544.20181009011731.tgz","fileCount":672,"unpackedSize":1569713,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvAJ5CRA9TVsSAnZWagAApCUP/1hzQ5VPHNB1kh2h4RTw\nOyuMiWzzbp64biujfXQwqOc+GwfgbO9yAbIUUGJWxUDkt+Pr9to+wUOwwNJi\nYRqkK3OSL3K+QZVN3qVOpf/guDASC4ssBBHhOASHzjScKE/GlLgMcWzIhA7Y\n8IWqdjKgGKwf/4gffSdwo5FvZgJEKHW7LV1gVpZ3u+bGSJGqsufzTHcXjadf\nhrVXsflqDUxARpk5IuRz8hYI7kZBTXS6JRyKe6LXFKf1Su7IIEa5PVfT1vtW\nMoCafDlB1YbErA8IvmBrDJFNO39N9GGfX5tMiLCXc1eXmyet7OVtvU4i02Fh\nLBg5+NJdxfQHE04ieJNOBI+GNUMmdgBZ1kNagvptnMqr6YfgzUM+J2EzLpc3\n+b+zahGSI/ZK8JHpGjltzYlN+bk8sxdl5fATi//Ud8YulaOX58QqHO7R6JoH\nuyLPQCHqpJ0a8m7z/yfmNfqbosgh8RiylCDBT9wvSTvIF3OdTvpeGbHPm6G7\nHjsra4/cCsRllOnyBsljFHgj3ByVJaC0Hhk/JpHjtE3JCayFByrDg/X+YPac\nP4KUify2HfIGaEgBUeo9gND0k5vKb5W5XHyb5Jiifb3I4nJ5aaTZlIYTZG0R\nyZI5r55D19ZHkiUqcEjI+FZwg1vTGE/92dj9OvufBzH9GqOIBWT6V4CZu7Pv\n9QRX\r\n=Lz8l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjheuEiMggTdruw0xJhRdUGFnnxuV7x5vSBtFKTa6/QQIhAICTnQVDltAm+qdp0L/l9xT76jSdeDHMxpDIDvbIIGA5"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-automation-client-export-544.20181009011731_1539048056508_0.3416593406144701"},"_hasShrinkwrap":false},"1.0.0-automation-client-export-544.20181009012548":{"name":"@atomist/sdm","version":"1.0.0-automation-client-export-544.20181009012548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"76ceaa70f68896541036ac1ad7a4fc71ba2a7c5c","_id":"@atomist/sdm@1.0.0-automation-client-export-544.20181009012548","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dZ+EL653Aht5momJO1iDuk/3do6kQto6JR0SJwP5ulHPFWs8FDGCaAIRJAm+GE6S6FxrluLgJ6eMwvBSmHGzlA==","shasum":"fd6ed7015252489949c1eb8fd7f3b394bf6363d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-automation-client-export-544.20181009012548.tgz","fileCount":669,"unpackedSize":1556776,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvARcCRA9TVsSAnZWagAAFcQP/0BXciEU81wUVJZD9I/s\nOXJ90hzIdOsp5yn075lHrwc9gLZsiIiPsGFqgf8VtXd7Zi8zxP7ee4BgTEKq\nfXLbSUvb9vYbz65N3OyYqWIsb2prRt4aVxW/43kipFuO6Thg9WaLUjStcZBl\nUHNGiCpJd+uVhKHaZ1tU3PY4jZS02wGOIU+mKCrP8JTuvajV4YrJnpnvmulE\nhbi8eHuGUeOXDbtscbzNlo+cCHjSD8t4rFyeU+GVZ7Cc70KtoUOnb/gV6cc7\nw101NHNB05cQbzENBNUU9Ia0oMYfkDmxc8fUUZrO+GUiEs/2D6Wz+w9TKqo1\ny+VDzNLGyDXNzu+QRtvkvpaNQRkscqremTLSrByg3rg6PBaD3+29YngwkTPQ\nDXUKtuEMLwpYTt1EU+GbgBvaHKiLEfFtfKefs+42UQlN7qg3cAn4lAGpKhca\nnThKOzzonusN+j3HTRyUDRrkRZnRw1M6BROHZ6TdoQIr5zlwtXU+F1tz7MBu\nurRcSX0wjgBLdScb091Xn830Ov759XWkH4uBFJsU9t6U8mAUDEB7Ml00s1lz\nmzRos/5unE42wzaTAt82iZ4ApWWGv49BYQqChnjdfl+ek3E6ipZiYjVWGMAz\nER2QLvwT/WkD6rLe0fY2nqoM+0lOxoyvpe6zcqn/bimQcWwE+iYncAfCBQE8\n9bB4\r\n=qsfr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCd7ats9TwXypgca/wi6jt0bbcXdaEQ1HcncBACwSlu+QIhAMgsfFCmW9YVzE/hemh/haDgF8EhkIpBUl8ABZHdFuz6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-automation-client-export-544.20181009012548_1539048539671_0.6571107599336916"},"_hasShrinkwrap":false},"1.0.0-master.20181009063318":{"name":"@atomist/sdm","version":"1.0.0-master.20181009063318","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"75bb6ad87b1977e3703f455dd92ca96f876256c1","_id":"@atomist/sdm@1.0.0-master.20181009063318","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-u5xyCP28tDSl6nVFRLKBp77OOlphdOcU8VCmp9qXpK5VD4f2bE0q2CRj9t9uHHQ3h/oylPTM/vAw54ObnGUsaw==","shasum":"97db677474a95cf58db4c226b7faf6a411135f43","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181009063318.tgz","fileCount":669,"unpackedSize":1556967,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvExiCRA9TVsSAnZWagAAjjoP/ivT/gQCq6rjKROEkxit\nn8nNnd9/voull95ZnYtKkYyigV7/SYLSowKCtMsrOi5UD2cH/on5/qcvuDSn\ncYNHjYptAZYWY8F7Rpsl642eVGhUQj4PSmYz6F4uazee5XN/Cz6OlX+bLp/V\niAAcKPjqIOheMvyQ2Rsx7pkCjb+Chz12GUufPdjsdhhr/T8boZkEbmSqm+ej\nm5DEBsSlK8EsON206FxaQRtMNhn0Dzc1rKi9rOCUjKUiYXeVSkTyJ++3ixVJ\nQeE0TJUk0S+IjJDSANxXTuub1iTVPGy0+Qchr3wsqeyCYGAo3sk7Spjw/65e\nWPd8VhFRpV/6RQqLrMNeH77/eff3FC6gGXV+h4L0d5vrS+NLsQBkq1nU517n\np2235YfbKclrKwfFvRgqnLEy9Dlayb+zaHlf52HOY6D8rcpJX8CD5UuQms+J\nPep47wWZedM8J2k9gi15/ZMWySjuxE6X5r7hJt13A7oCNxyO8J6Ex0RjTKXd\nAxaBRn6D517o9akZ6Q4hGTrcDP5PsLXsl61+jyyOpiTRo/fTU997PRFSzqHe\nF8w04udeFiH9fsPOhkD6O44+pgedRO410yTb7n0R/4u7MJ8jgGK54627sHbS\n5g0XOSwVB8maLjjyJknT7SHDY7adLuVLBE0N8/h7WVELONZxHs5rVMonDwcQ\nVJXs\r\n=IMNj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHZ2Krp2s9ZrciYHz8R43X6+p04su365My85EZFiTognAiEAiP7yNLJbnYBLIoUUdN9H/MSt/7aWuzkufkPM0yCt1dc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181009063318_1539066977293_0.6839125742454175"},"_hasShrinkwrap":false},"1.0.0-master.20181009131042":{"name":"@atomist/sdm","version":"1.0.0-master.20181009131042","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"7db7b012d7eb5a721e07b212002615b780d4a7c4","_id":"@atomist/sdm@1.0.0-master.20181009131042","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PTLZKOGMFNh4OCKihO2zpU5UpsbyyBiPeWziRyDXTZXFHsYo5BkB2CLTSwcWFiOEahw8LR26O5InHdzLh683ww==","shasum":"1fd332e07e4e8e094d6335b62e044a7ac3fba808","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181009131042.tgz","fileCount":666,"unpackedSize":1555196,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvKnTCRA9TVsSAnZWagAAIVQP/1YFo5jCWljJN9BW5pRf\nrbwzxXXtbT0GFlGpptMIQXqbEzkEbjORFsKjxThN0KkYRImHlDus1UT/XIw+\nkGGHieyTKi0p35Lb30uyQjJXegWadC6LkLLCHSff47iM38XNZzumDKTnlStZ\nyoQ/FyXO5tauzo/+vwr0JuYvXkt4ArDwhaYs9wVWsMPtIwBMJlWoWmf6HiJp\nGpuRbgj8ZADbi8pVIcGOtnZrRgL8pkTgtdxS3MZ6gAVVM/AVWDLmWG6UvRx6\nCAFijEk8C2SyjCyAWSLUOeacIIyhUI2c8od9ERJ/Z/bNO6M4F97i2sW/3dZt\nXonkTySlMdC8Zn4+XK5AEMjdLQ+t8BdubTpKwrQ9xnVGPPplVoo4a4Ta3Whz\nsfstCoNb+UsQoudRbpcvwiGcIci5FAiH1ux6KtoHiRWkWHf+b+SYzXXonfrj\nkkpapmfV+yw0RUewdJoJ6KE3qn5bZi66xWmO8rr8QyfLzOGJnCrErsjBa5/V\nOgam4oOEfpPturdnyp1mY42KIQO9ABrO98NvSSeaUh+kutW2qn7hC6/NTB7e\ntSrqL3U9/sLmFQgBbLqBw8JgVxoM/2yKI3ZXi7kvZP1VnsPok69h3InOYCYr\nB8/ObbBbs85NoiLcCYLaJTBcPmBi36KJm8eK5lAR6yhiL5MCZaC8+2b0wQ7m\nywyR\r\n=DBd0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDg527RLGEE3lN+SDlieRhFcuu7xftvlvuMDdQzQ6pzawIhAKfpyl/GL/ca1Y0S4+A0OXxF7w07J/2MGnZumKUVhYi8"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181009131042_1539090898826_0.48574541425239204"},"_hasShrinkwrap":false},"1.0.0-master.20181009132540":{"name":"@atomist/sdm","version":"1.0.0-master.20181009132540","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"fc82cb32ebd452fa872af48f49d451a85458e417","_id":"@atomist/sdm@1.0.0-master.20181009132540","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2mOb2pXHuzqGtXr5V9xTN9GqMCKyhCBcKR5NOXUwoDsnj1KzE/QdqcfP1ot4kH7BtwqDMae4hhd3Ze1nGAUa5g==","shasum":"a0198b12f4678f0badfb55b67e27fa9c36c2ac43","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181009132540.tgz","fileCount":666,"unpackedSize":1555106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvK1SCRA9TVsSAnZWagAAG/MP/32gixLXg+1+BhhegbY4\nIITD9r8mH8JrrNoRP6tnOZqUaXoQZT9PffrBWnXlZZasiSu7gxm2lYWZZgRs\nB6V530vR+o0fmkx90EUMCp7S8v0o18ufDlvFylH5OqCLDB3uy/S+ACTDg8pC\nJc4UQV378cphz4/eKlPrSkPkcj3AokYHsiJtDwbG1WZQxVZqohRl/la7hnvt\n/eCBi7pI+Ipm+ydauwfQpVKwUZqjjDf5UKyxumAiUds4T6vVE0vp9gcUXMM3\nMzn7ka+mLpj/ERyHz+ei9MY4JdiykXRY4/uj05eNxjFldycA9lYCMI1wEkhJ\nxrITNh/APQdQKvd0Ouz099mLJAXAH0nRra5wRhuf1y5kK82AW3fBB2sdE6DY\njwclwgbz2SUyEOZ6nbXOS/BVz9AePXZXDrPYJpElNWEwKh3o2mI+TRBQarPj\nct+hMrX0/U5+Mz7042aYv2NQaSrAS6kMAOUzwJiy8QXYAEDFVasf4eQR8MHb\n+YlhhkhIofcmxaSNJ6YydzUlqRkAefGa6iGw8tgn0jqVShKdPocJkVyF0ydS\nh84HKnnE7cmPm54WQ0/V2y+58+kw11Zg+n9yxV3zUpY3xtcboBnIcHILePUZ\nhqZpmhkSXYR3ZSVO1csqEggQYVeTISDcBypUk/xdtR1vAEpe/i8KzXdveZaH\nG1mg\r\n=chmQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDULwmP+KRrau6nvoNU7FeZUihF115URdF3haQzxACS/QIhAMy/x6Sa2FOrDdjsY1vLzokEwmT2AUEZZP7PDE39g4dV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181009132540_1539091793493_0.4002177197738743"},"_hasShrinkwrap":false},"1.0.0-master.20181009135628":{"name":"@atomist/sdm","version":"1.0.0-master.20181009135628","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181008113855","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"49c10fa1885e8ef88b3c58e7973b8b5ca409150b","_id":"@atomist/sdm@1.0.0-master.20181009135628","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cxLmaNjdC34AqS9y6GeXGlE/3bJDwV5LsWTacjR5ZbeKC25kd+4aze/SepY8mYJWpESaQrcAPz59n2TQxFtOHg==","shasum":"26aa0004b1585aab5513fe62e5e1851d869c9dbd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181009135628.tgz","fileCount":669,"unpackedSize":1559423,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvLSWCRA9TVsSAnZWagAAcn4P/1U3RSuXWGXHiOIpHjHF\n3ykj9ED/Vbk7ZE4qN30LarxDvpDxrL46OvxzWOKO9Hz/NDixWCqYJAdyWJ5q\niliKhQVhC0ReQGoLGOnpgUYMy3xLRFTXI6YGeo3wxYZZpDfRN7sC/ZAujgwy\nXPC/mTlUMhjKSAT27R50ll2JfN912vKlY19ma+pqqjOY+ktl55zdzuRpfSzg\nLMiqgCj3BBvGW9qc39BfyrQFryvwt/WVNAO/pBuvTJMwvUkqgC1dblRpTmUU\nMwE9tQaGFM+Rkwg5FxlppaWPLWgAXRKoUdZvDLuS6I5wcF6EodbV9S6EfZZ4\nIvuhayn3Boi5SJ8VNFLuUItEaXYC2qqkMKfJikh27BPv8y6gxKlCkLDUVk6a\n+idcbd80Bq1ivG+/reT/WbCNdWzFtx7va79coUjPReN59zIcxKYm1ba1WRPD\nRZQL6RlHxCgb/n+nlmaPxSHXq4KPy4WquWJwJE05ew4t1alR4+XJTLqy2b5r\nDZ+KljXd4WlaoBie4EGUxf3ZvZvCY2YL4V5WJ8AV2i+5zLV4PLAWEK6Rhm5X\nGuvyVcvRkuasPBM+XZbeZAptYXZT9CKucJQ7kTBqx8rcZzjZZU3UFYtNUh+/\nG6eVAWyjAp4OGWyyKEVy7ssCXdYof6S4vSp2ODUcJIWjispv8kWTz0Vgs1d6\nwNON\r\n=q3Ov\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEcZrOdzAYIT8mjnmnyYJAaUS+tahj0IEGgvhELTnrQQIgDak7jGeiER07er5OjOW1NIgnLTNCgbL65QJhRisR29w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181009135628_1539093653493_0.4430213885794827"},"_hasShrinkwrap":false},"1.0.0-master.20181010074610":{"name":"@atomist/sdm","version":"1.0.0-master.20181010074610","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010073224","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"2bf521edeb14387a123b6461985df572ef9a3aeb","_id":"@atomist/sdm@1.0.0-master.20181010074610","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cl/KoG4D3oVg8sTQxe87kwqCTxDUvtT9v5NFNjPPzR5FkrmXS2PFDyR9epcQn4vqWiKpVbuDV3h5ioa4Q5T0bQ==","shasum":"c299793416e3e6b9d1a896ca1c41f947676e7d80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181010074610.tgz","fileCount":669,"unpackedSize":1564555,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbva8sCRA9TVsSAnZWagAAF0gP+wSkyWNySPv4RSEdx7Pl\n8rl3XXkYl99tf3IhzI5LqsGqWE1VmG4UZTtxdxc9HTugTSKp/Kqhk6cnk+vM\n6MDztFnH1qjyXSkquaK8UPhnkqvrm+g+B7kLN+PF7UKNFyLa4eWKmkYnclN1\nnnpLV7v96CBEBc3410Gdr+avIC34B9kCl7vVPieXM8oJ+sOK8249swSIdyUr\nsQku+36dQOQ6YZQK/ym5juHLZOZhzr3CleC5DvZX1uC8rHykZEMF3S0BKMOU\nHrytVBJcWurmeMPe2qo5NjAjQ0H0SC01ZIuVn5kGTdhV3wzqQBTXk689oMUA\n3mT3th21UR5OA2MDp4Iyx8Fyps89g83q2RW0IrLOaPENczbUYyXrKPDQNsvZ\ns82Aemv3WSpChd/nhu40YGK16dKsNjwvq7WChEkQrffhAbi02EqkR31Mt3b/\n/ZJdzHS3X1IeWK0Ta5yDMVUXIY3vrqaYCzp4NTTpUG3uSHRwC++cBYtrOk8n\nTlWz2QRrub3cBlpla16gG+7bx+E0cwcQMy61TritANjpRrqska2GMENuJbNl\nTahm/EVa2BoS5dqLkjk2wVhVnowTulDtrIhDsW1oHn8wab2OrIgJnmsiBd1a\nylvka+aNdNwKKE5fMcjpQF7eC20k26HCNSWN0DzRf6BokwYeKomEoqgkTfiS\n2Ecc\r\n=5c5B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICSI9RxFPEmvEkTa8NOzyiRNTJP16dQyKx613mwsVOPEAiEAoCz47xWTqQZdl0/MIajBI8/I2LPdo8qrYLCp56GYYeg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181010074610_1539157804034_0.28269437737203207"},"_hasShrinkwrap":false},"1.0.0-master.20181010081849":{"name":"@atomist/sdm","version":"1.0.0-master.20181010081849","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010081045","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"cf5c38874ac97b2903f26fd57db036d9bbbda317","_id":"@atomist/sdm@1.0.0-master.20181010081849","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1NJf1kkngH0X5I0Tp/v9LmFO4w9JgPIrEpuA0aipDmy3X0AilKMK2JvMQp8epuKpbXX12jWzDTyyEElQCmCmSg==","shasum":"7daaa8642a5e789a36a068e88978ee6ecb7eb28c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181010081849.tgz","fileCount":669,"unpackedSize":1564555,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvba1CRA9TVsSAnZWagAA1loP/0ySowwXH9qqrEaxSGmI\noQLklrEY4+IiKMJs4sbDiz5v2T/NLUH+tcSJwRIT0wuIuvdSMr5nf5LgSrze\nu9H6+8HJyNIwMUKxEC8CWhLKZTgg4QnTCq5oYmGgAMca3J9kOXoVKfRWclk7\nrLwcOM6h7uuRSZ7NzpS+7WII7+/8FpEUeIM3dpK6Elugv0ZhhSLd9ioxsha6\nMR4OwvBiPqDhLGx1mTeaWtpYDaV3fHXFqggAK/ifhbpG0vjNQob388FtPbbP\nz67d0FfYLOprxd4EHVPo7KKsI2mh6Bx6U7tci+24jy375V2Id2jvpAVtZbsh\np7ZjaVHgg5nYUDo29lXmz9KKozg6Gc6D7dQCULAOEZgWW54Nhlg6l9pOachw\n7g/wdI2g92tDoj3m9XwrcddWQaaOy3nmP/efK0HVVLmcIONHCVRZUZsRcJJH\npAdeJTazjVRxcu+/APYdLBraGoUlHVHP53LUYl5BNbR+EPRjU5zASeN5oFZD\n31ezmuTdTEM0n4lgZacn/nEpCTHPRLUxzEcmSNpZmF5pUPqY8x02aMOrXE4y\nsx9JqI9qkDK0030sGbfnLeutSwtir5sPLchbcj7itFb5OzgwajZxD0pPXfRW\n2UH7+c9xJQ92zxMMQsVjEkDlGordaJ1M1xPb6vQ8ShLRuHDGtoVQdpJjl8CE\nyrGq\r\n=bXtJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzflQTMmqWW/Zv9d72APnTaG2p+Nh3uYjooalaFBj/YgIgJs26UkIFpQCRrH9yQpIa4Mm0XnGWVeSEgNcgXcMbjrU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181010081849_1539159732304_0.1303526745612935"},"_hasShrinkwrap":false},"1.0.0-master.20181010085046":{"name":"@atomist/sdm","version":"1.0.0-master.20181010085046","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010081045","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"55a70afbcaa81911d1442f06867ed993bf595c05","_id":"@atomist/sdm@1.0.0-master.20181010085046","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3o5DgW2gq1l9M9Bk/j/AXk4TOnlg4Aszc0h5YLPSjtMuOw/tS9FDpP7iYBCbOaNic/+W8/WqcXxEISSPGwjGlQ==","shasum":"207f20098547aebd51ac1299f459c6df326ebcd7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181010085046.tgz","fileCount":669,"unpackedSize":1562846,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvb45CRA9TVsSAnZWagAAKm0P/ir1lPmKq+L2S5HKuHuf\naHO7OIVlqLVhW0l/+Oz9nBY0D9zhirA8lR4ElV2WnBpKXrCCN78VTbGjviAW\ncKFZlkqxoOHj2OMYEnGLO0/BjsjgKMo1N79e07rf4/g6BQ6ZjYYlR24qEaTU\n8FuNR5fckbuvxRp25qZrQwggxBqu9IHhJSviAPwDEDBnO1shtyknLQx3bRtn\nN5HAo6Wz9CBx+5vYR1u6IMExcDjvcerR7xKn6etvUqea8DWocAmx8r8qFg6G\ng5/fVbJF2+8iYFO3rjVCpOn8BGFfKB6qqVpMKV8FtOt/+vu4wPfIj694hOOp\nzbQz1iockUP7tJAcY2zRMDfTdNL4akLNVhvXo92IFubMKEPdtZY8rrNTxXX9\nVNQR+BTcUbVWhX2Zpofev5zOEuMZrn1SwVG56cxhsuP/4Kyi2Ixk5UnXPa9+\nltdHygSncF0PpWSTkUlvMYkcyIMUhAwxiOKzHzmV70bvMxriS7QRtrrP2GsE\n09ooJ+BUfSlco4zNEh+SATLb5fk4jmcDjwfcWUfOJuO3opQV9bf7fn9CZPnc\n+a7+AfM37+v6GBpGa2eE4nVLF5D6thFg6kp0sY5q6NcbVXvgtkuBxkcx6PQH\nOYQXsRq+Ey5g1amVhzpGxK8DzhKhQyapXz/YQayNQXWNIc18YLlvjYJzYpSq\nIobo\r\n=zEe3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDoHtjFDimPV4kHjx4qsh+YXw7EoGKCQMV9ZXZjmaQ9VwIgOhgYszuetAX8TcYaLYZq2P/w9cE0lPSL0XlS0ZPaejs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181010085046_1539161654233_0.6567788859604577"},"_hasShrinkwrap":false},"1.0.0-master.20181010145707":{"name":"@atomist/sdm","version":"1.0.0-master.20181010145707","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010081045","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"docs/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See this\n[introduction][atomist-doc] for more information on the concept of a\nSoftware Delivery Machine and how to create and develop on an SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting Started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-sdm] documentation.\n\n* API docs on this project: [@atomist/sdm TypeDoc][typedoc]\n* List of third-party OSS licenses: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist - SDM)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm typedoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm typedoc)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n\n","readmeFilename":"README.md","gitHead":"c1ab514845b92369ba3ae1bd1770eec0625344b8","_id":"@atomist/sdm@1.0.0-master.20181010145707","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-o/p4A0+ZRi1H6LwoRSQ9dS4UZ2/u6SeedOTaTWlY9f/JfMFO6C3kibmt7ZGWxn286867gSNUlh2ihSsdtT00gg==","shasum":"3727c95b0e7de6510aab89f5855db622426b22e3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181010145707.tgz","fileCount":669,"unpackedSize":1565410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbvhQECRA9TVsSAnZWagAAg7IP/0XURALgdn1kMblu5OLH\nzMQG+jycCOuFz5hLYscERQOkK7lyAuJEckOYJHTlpo6BN3tUa+KcZcE0BfNL\nHVohxnTgq3AP5Am+1s2rcXmXMSR+TQuXXvbkDKGNbA5FvZKuDu7a0+7iaA0B\n//hp2wkNvTvupmrsKiVElnybsTpXf7yl9q4RVyenteM0YHHbFW2hJQ8ZaXKO\nRlJUwL4LVBmTsyuAw2U6i2sDg7KYMZm6CtixKMyJ5lcMTsaCRpUEP9RCk6Hv\nGlTr1vv5qfUfgzZEpPa4TCHV9jwhq7u6iKGVQk4zG8k58WQmhnYLqxdW4a3M\niv+8puUAEPKFL3qli1OfQdNg5zUs8toXDd54Gfbe03wiDuI+aVCi0yj4DnDJ\nU7mu8raRqQcTmlbykCxTRlmzJ0/jh5RZ0u5IPzrwgQDOtaFZNxgmJEqAX3vE\nmnjVP4tpl/PT6P4PbDVzCEIBxpadsyLnRbTcxQ+J9+uK2PKP49DtArKS5xdV\nQVzFT/vdjI0d6x3S2rnXgeM5lIMRRTtQ3CqEFaFkJSfGcHdkNu7ONh9+up1T\nHNMgb6We3uZAciRuEAHImsiDgQwCP/+22dniIitUOzy4aLldHukuwnd6wwiR\no+nch3cIEqGKAeprOnZComcMayy6zgYsRCWPIeaWglIrJ2K/qKKYPH01OVA0\n6nQn\r\n=Bf64\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCc1ibcULAaNtEcqGswlo8SpLVVpEUpL4In7vx9Cmxq0wIgLRHDxVEE9PgjZoNIOX6LL+5NEZ+4Uaa9smwkkfbq9tQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181010145707_1539183619369_0.6388937796032019"},"_hasShrinkwrap":false},"1.0.0-master.20181014192008":{"name":"@atomist/sdm","version":"1.0.0-master.20181014192008","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010081045","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bd40d2fcc0b910fee0c6d0a9eb70d2d048452ef4","_id":"@atomist/sdm@1.0.0-master.20181014192008","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OK4VibzVo5qDUGHmbxWr/6GTPSISOlx/tHfGGRz+1EB9vvLcsThtfyduDr5xVTwe1Sr9X3a9qasfQrP3oiHq/A==","shasum":"e80d45d4deb558e35351c2457297224229cc4288","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181014192008.tgz","fileCount":668,"unpackedSize":1559293,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbw5eUCRA9TVsSAnZWagAAidEQAIhFQlnteOTEiNQJdbeA\nthuNYvruLGdSVUW2EIqpl6VJMaZYn5yzWWY+/zZAtidVkO3e0yLDy5HbGcEH\na8PuC+iu8Dgm36lPaifKR1FpgJ7bqxSCI/IlfflXJYanvHtcydo0kFi5nZNc\n8xvXrXous03w5EC+lmJR3h4LO9GRmW+cFf8j3rsNwvHqPU1DP5zKaRVrqssW\nyriY7R3lfO9QgahKfmBa6IRjIdSoVJC0p4FLeqp6BKW4wKrRouEio4dHjda8\n4i5SvN1bAG71JL8BQ1ek7HtshlUlrpmqL5H7r+avUIiBla8M9KFsTKiRy43u\nKj1PHciQMqTlWSgBr8+giZZSd1JnOo1wiQrHutRtnRwtYBSerAq2EmK6MCx0\nsW4VEiCNHtz7aEBzy+rwTkzOvAj0pAzuUQrlVY/2AAvCqysqZuYz1RpLVPsj\nbKNEFtHMyDskkxa7tGQtNxdmrinflPwrZ0V3sepD+ek9pqDysNhyVojcH+xs\n2b+b+g+2tffNfdugst86eWhSH8XybLt170pm6bKcM/Ngq+no3JC3z6YJG340\nfuEoTYutIPnAUu4/44O9GRl5dUN7mmh293S021qRL6giaC0MQqLdL+yO23DL\nWN6wWGJ8zmInkbu41KQBSWDqqdXD4+cq2YZAuXOQ7kLYblwwssciR1mNo+ga\nKzIt\r\n=334o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFjYai8Pdp0VpFT6olY2fqtKkgsLNYsQ/ZZQ0NTkXr9nAiEAtEhrL7yc/xOfM6AWwakGhpDaMFJ7kUT68dQ4hBpdw0s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181014192008_1539544980109_0.7565628326773719"},"_hasShrinkwrap":false},"1.0.0-atomist-update-branch-master-1539546589474.20181014195104":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-branch-master-1539546589474.20181014195104","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010204819","@atomist/slack-messages":"1.0.1-master.20180830190736","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ddc0331ce0193718303ad6f28e5b17f0d79fb2cb","_id":"@atomist/sdm@1.0.0-atomist-update-branch-master-1539546589474.20181014195104","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T43ml+lRpOeT/0fROm6arFNd6uvDt/5yz1FAaYpkLFE99886hhP/nxQK0ur515+Mo/EIpMtGMQIwGp8hz/XD2A==","shasum":"a0e32f4d1c962ee32637ed5a8cf9638575335c9d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-branch-master-1539546589474.20181014195104.tgz","fileCount":668,"unpackedSize":1559351,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbw57ACRA9TVsSAnZWagAAQQwP/3eabkkHYDSQMJl8bzgB\nQXelV6n/Jp8n9zgXF1sWdU5hYohF+iPRWdqTE38Ipzbo6qXxb2gYCTY5Mb3l\nKxi4UFUnAznvdP6K26FkpSJSj21yotjpviy8Ee6xETB8Jq377klOUGi8aUE1\naR2bCwABQtdWmwAADUP4xnLSa69kc65C758fEqU4q7iBRcaB7M8viAeWjkMl\nO0K5YImN6H95k+Fad2E5CoUSEGzGeLDOEC13ryoOUSpIfBG3aAUXkVRcJBkY\n9YiiSnkQj4g2seFvq1PZLzlHtr/x2744qc0vfXuP0gCL3jbQgRS+tEI+ym8t\nbmQWEOEd/Ji3tYIPnwQClojP1uJRzWW+Fzxt3mX9MwoEo5+bwliICUYg505F\nCVaqsbaBPOy3oVCDWH/2IHzx2gTVGitWX1SXaqbiAfoACdgaQ2iHgjizm2EQ\nQEJXV+gTmfdU+ib7bn3R6MxWcwnyo9SFMMoDd4g/Ah2djjtoU83n0zHZVCM8\nKl4bvazCBEtumPIgyln9G5groxtcTIbraOIo6h/qCjQE2gV/SD43pkXcssb0\n0hItSypmPhWWjZmP6SSu3V9FXa9eHDYF5aYzrrT+kW3ZOLfchGUC59M2W4ky\ncU3ZIuvmfejUsG07LwEp7P2c9N3kktbOm6nHfdAy25FIRwtRiLnuJIhQ08FE\nl70H\r\n=TjrM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDz6yXyQG+cb+DhhdQrFXE+RioCqR2EcoQ6CvvFImHHngIhAJoWGgBKBxI+BuqlQICvI/DvDjpUclbJH5SjQyWB4LG5"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-branch-master-1539546589474.20181014195104_1539546815785_0.9425003593568468"},"_hasShrinkwrap":false},"1.0.0-master.20181014195612":{"name":"@atomist/sdm","version":"1.0.0-master.20181014195612","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-master.20181010204819","@atomist/slack-messages":"1.0.1-master.20180830190736","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9284539480221b9998daa0e7c1e0f0857715cf8a","_id":"@atomist/sdm@1.0.0-master.20181014195612","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-z41jiyyL2iZqwj95PA3+wR+0rM9AwzEO9YUvfyXE9DWLtLzaLgC5OKaDExAH49OT7y6MVwSeP5RAozMaEuCjTg==","shasum":"add7bb9e71964c87fe7fbaea9f771c67115d1a5a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181014195612.tgz","fileCount":668,"unpackedSize":1558527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbw5/tCRA9TVsSAnZWagAAhcwP/2XBR7o/rswMagr4REx+\ni1KDsWl8xOgoafUYjuxhad+Pflrp/K762PQry0GCivs1+mWuqhPDzDrLGHJj\nGxgCiPF+l9fWcF3amWsQduVFooUWpnRXUjy+zp4jDUO3aWqYP8FjgGu7dmVV\nGUf9sqyh8d34JeXb7VH0X9K/EryQX1zgmTcIi6YTLQfuMqJOemHaMDvJdH0b\na3VzDj5GZCMYIAqfDYD0taAQBlScfbeS/I5/Orkj89mUh53PbwQcqDGiV+s/\nAQP5IkGLQJTZ2NbSGSXZFKOIGVh7TvaHuQ0gbQelWd8/yLFFl8+gHAzkEAgb\nnsjH16JJpI+qIU2VWVcup6EEfevW1+P0yU5EyM6KYgRF4AlwF72GuPbCow6Z\n/3tdTJbYUTRAGs3k17Z8XpPicZ/YtkBcsk3i4gQkhHvB7iHMrihbbhlcGOkw\npWMcQAfXFT2DHowgf5jRFTLqWE1R9xtN4hY/WHgexLqW+OOTii+T27azy+E1\nbpjWn9Ue38BI1Uf3Jat30J2iAjG8TKH+M6sM2iX+/E4ZA5eMxYTh7oHj3Ouo\nXuVsoWjxHjq/fLP+YTQU3M4KqLdSNZGoWcsRG7xp7rS/1anRApAES3OdxxgS\ngdqK5JIQypTh2LIYTIYkRAL3pPvXWotl/sy9Syv0c9YlHfb8Hk2g8m41XTjE\nSzoo\r\n=iGPA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCrAAGzt0zDZUo21C2x0SRR6/mEey1WEBZGaasugAhiEQIhAIdMBbSOzfiD1G3nypaxRGJuGxnO15PZJm2QgNukxEfd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181014195612_1539547116811_0.945479599383628"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.1-master.20180830190736","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ca692f52ab3051cdcdf1b0c953786f5564daa54e","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/LyNISVHsN9wDaBqbyAYV8RzpD2fa5RLa4HNbytYNd/6rY6neKljbxi32JqzJLxVZ23BoYluza+/LiWR+Blm+g==","shasum":"06e62fba42609db1ffd3231153b43179beddb90c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213.tgz","fileCount":668,"unpackedSize":1558543,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxH2uCRA9TVsSAnZWagAAricP/0Ikfx9pab+rAgceuAOM\nPEAxRYWlRfnJkDShr4mOvtwngYn0D/GjuqVkzDer4hadH2v3Xph2hytn5hfS\nz+d1SoxrsIjAoTvETei+8D7poWtu6f78JBtVKQztAJz8n9pg76IeVDDzzLMT\nVoW1F5p21qQwEiVQUVzu52Wo3zkCcgt8+pshhDtHXTlQIELtFpcajB8+kDxR\n8F6rIrmhwvgyKVvVGLVVMCbzYtt9QAq7s/oj1aj1rjBbs/u916Le15ScHEXc\nQrZ8rd/LH0iqO+Lts3ERrqkrHTvAJyQfvHl0hOp2Ukf5aBwG1chRg6xZ2YfF\n+006mRQSerhiw1upWdvJ6SZSTUv6jM8xLgF7GwkO8GksNn2wy4+qX8hPLP9m\n7kcT1/PbZB6c0+DLbyTvdaNN64Qy3ykihlE+Qt+kSWcLGEnVXpI9trN7o9/H\nIzN4YB+rsOYzRAgfknmogWU0k64mj8DK6VAWeWddbAbQ2hqElfIq9tiWaYu+\nKbwkO/LKbCHm3rfY+VcfuP2oQQoJu0P3uLVsn7+y7zz7heUdj5577XYZV11Y\npXRA8gvnnwA7zYCHaC02tRJ9OzVdyqVaWxlXuWcqx7TCjMGBpucK/bFBlXhF\nJIAE/+4+aXb5jTk6UHuhPSSfhyX08iVOJY+mNWRpm5HKOHdnosuQqEZX44aY\nhfj1\r\n=Ua2P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDgbvQfjxGS7yxZqX5VnxBBmSGVHaMly8DCItEFEPyF3QIgCesfk6ba8VfpBPiewwKDOEiXsvDpi3d5x/tt+2DudTA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213_1539603885337_0.4398537203048918"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-1539603949460.20181015114652":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-1539603949460.20181015114652","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9cdb56b5104e8baf864a12e7fa6440d27b8f56a3","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-1539603949460.20181015114652","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lUg244YkuLtI0Zt/1mhXxr8/ST0HLiblcNyHsagW3+UrJomqiyxBQNYyqTS5k1YxHKeeb+vk+WbQt/YAIlVihA==","shasum":"77198ac4802bcb7fb13fa2c27e75e1e87845e856","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-1539603949460.20181015114652.tgz","fileCount":668,"unpackedSize":1558516,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxH6/CRA9TVsSAnZWagAA/9IP/RLTpg/i+VWny86Dcymx\nQCZ/UBpuk0GhsILx5L0kGxjq43hkdbIWu+/iH7rkPLDBHXxfVLH7NoFZ8hcg\ngO5kDrO+5fsaaRADz1k+ZmP9XP6TOiXqlPnwktxukHledeQQ1k98mPVtLJQw\nEJtIgirWWBW7Z2ZUSRRGGvHDKw/rXd4/a3A6MfJrP6pP+Aj+nxHYgqcyYb/C\n9hCUTp9Z9jAJKtV/eS+yRUg6RbzA6XLnEszUs1K1M6i8X4iT1hPdk4mLFOER\nOljtRPguwEVu2DvuoVr07lI0Zl1Hi9BzoXml1+qscm6gfX6C3xKDcLtEwo4p\nUoIO79o2No7oTv+ZHiK6pA+NLGVdQFJdp3swgsYzg7bTYTjBpHIhI4WG4Nmz\nEm9VhNI+Nk0YZOFcx2tiN+3mKrEuctnwQDlZ/F3ZiVreHDvHCxQWFGPOqfYM\neAp+3HOoq3dnRBwv92Xben3MTVpLQcXuSI+RQqmSwc89qiWdT6+7rnI3yWhT\nJrYtT2C/OgxhTbycZGt9Z3FBm+uzrjXiv0Zw7oL5Vbt4MwjVoHKDAP3LfHuN\niKm2RRQVTIbNXsC/UCy7bf4YsXglhtS1zfGq5DLzSP9A6b4RFR9mxkGlAAHB\n9cpQK0P9Rk/vYgXPI3qpSZpNPp1/gdCjoQ5Uyb1H83AasKBuxXxSmMV2Jhi7\nMh+b\r\n=lXpS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB30lt2vRBE97kZc4peKQ5sgNaj1iDaiVCulhdkrQo9uAiEAjM1OuknBPwPw9gnlYbex2M6K/ohuw7Lr1eemk2RnZjo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-1539603949460.20181015114652_1539604158738_0.12554904984339066"},"_hasShrinkwrap":false},"1.0.0-master.20181015114715":{"name":"@atomist/sdm","version":"1.0.0-master.20181015114715","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.1-master.20180830190736","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"64a6d7aab7354e9755f74ef086afce4de41842ab","_id":"@atomist/sdm@1.0.0-master.20181015114715","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-svBrllAUHpk1F0jKrNwHOuW/ROjr+qOXiR7oKNrL7wAOHs2lrjUJQNfyuOLTrkHWr1s3d8Dc7ALPgEvpCILfRA==","shasum":"0c01daaed51aefa7572ee17d5fcde6ea4bd5a8f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181015114715.tgz","fileCount":668,"unpackedSize":1557912,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxH7vCRA9TVsSAnZWagAAHXwP/itxaDQw1yHV1ZnvvKtD\nS+U72Zt/lG9bGv3Vmb6D6A/VQF9qIOJNZduhHlIdYH2OWbXAFV+JgNjYfXbl\nA3rUfW08OGNfzMQhrM2l1eyX8bCDr/4SW+rCGHXa1DmeNNti9Mh96WsNDYfx\n0dLyXxlV1sfile6DE55FnOvIqgHUXmoBKutIc3bQwds4IfAkeAbmrc5xYKWb\nKTshDb76UGUJ88NgTxGZiD/qtYl3CeJtx/w9qiOgpNZyJGQH2kDhWEEZLJWE\nWjQBAmufkaUp5O5UHhmZeZfkg+TzBh/1TA/ChkGDoeDTE71UVdNplpD50+YT\nHYkBIkUotCRRakrPZ3inaPG4kporykkKmch+WCGzWimx0/WNy8wb6f0BgqwR\nZwLkWgvnQoMu0j7QXGbSnHyABiyrAGCdYaTLnY3tAzMdqwWS0ruxF/1a5LAq\nAk9MovQ7JqdE0Z1RPOZdUmsX9Ys6JqDn2ZHQFiWfwwWk+ZDgFdkmWnpYY41L\nvd9ixvff8m/8gNbetVm3AVCLceDQ8GE06GDjzsCgwdEsi548oYYOhqs2IGwt\nqWt6Z+V1vWZUDF5rtgEAt3dysGAYnRpD08pjiD+iPPCgBC/C8jNaEPDAYkSJ\nhNiEAJnxZglZjAbIFMy4CvcWdhCBwK6LmELd46ETji9Ujc3it773N41q90yX\nmLBt\r\n=dtW8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF6OxzT2+4OXfhZywyNw3vPE4AlibcGfaEE2BIXi0u3OAiAa7jsJ601qEW0FHCq/uTh0FKz/CqtNYrx2bdFbM2gUEQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181015114715_1539604206752_0.00631447794983564"},"_hasShrinkwrap":false},"1.0.0-master.20181015115148":{"name":"@atomist/sdm","version":"1.0.0-master.20181015115148","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8fe28a67e58ea91f067b9c9e2520225b212415f0","_id":"@atomist/sdm@1.0.0-master.20181015115148","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5+AVlLDlk0qhCLAZTzKpR4wEkIAyDGroCEe2ruPGUSxiTD2Qg8HDb7w8UrLMNYawNmMS9dUR14x8WGR+VU4gJg==","shasum":"1fe91a3c883ba09e8fa6ff2f0240896131363108","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181015115148.tgz","fileCount":668,"unpackedSize":1557737,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxH/nCRA9TVsSAnZWagAAy3kQAIkN27IWdw/Ugk4EW/ny\nc/WX8UalFg1hw661QM4LCuGYvf5Om9SdJsg4mh/7JF/9Er3FnZ9p0jJj5WCe\nKC9HtTDBSdRSTrfLGDfNMqqpMfy7xpqYWxLvmv2YawHaN0AMdcUXbWRPi/N5\ny/fI6mm9s89oD/Pn6cLI8+lubVr0C50zfDDgyrH3eIoXpt70ERT6ufZKyw06\nkAgTpZ1+h37i1bVQff5KIOwNAf4nkbNUc3xKPEPHxSjv8ml0fzWXFkg4EQyC\nZ4pNUmVOee7VMCR7SI0m1I/29xyL25EE8Hpq2VtLale6kOgd0gjs/Kg9ih2v\nWGzTcwApu6rFNXssLwveaB7s1Kb7bBD+EzE5TaBLRRvmPOQ9RZn5T8v2qS74\nxYasTEbiqrfCA1oXUS2zOr2fKQUPA8cexCqlEWfaPLKzDeaq1IuvMPZHaD/D\nHwGzIManFJpvBb2xM9mudnBN3WeAOcT7K3PMeS+Mx8j6dHCZd+PoR8Ikn0zh\nqk5AK+NP+zimF1uGHoJG8GGhTpcBfpzEMBOHMelo2Ghdrx+z0Xda6DMIDsl+\nJk8Pj/8NvTaj/yWG0zAQYc8dWYSK/qKheDIuQ03f8zJc+p7jBYgpojzz4014\n3YFHJ2M2n7TKZkZaB5F6He+5BwsNp6C0Eotbqje2qCrt+IwcgcaYYzgFKGcb\nvYPV\r\n=ehqY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCuWypYjZHF7bVXkEHlAgeolbAluPS0mqUTBFwFiesyxgIgNgqugZdOghexvrU+qvidT/BiQTYblPlk8AZI8Ty+qWA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181015115148_1539604455165_0.19091686375640426"},"_hasShrinkwrap":false},"1.0.0-RC.1":{"name":"@atomist/sdm","version":"1.0.0-RC.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"gitHead":"8fe28a67e58ea91f067b9c9e2520225b212415f0","_id":"@atomist/sdm@1.0.0-RC.1","_npmVersion":"6.4.1","_nodeVersion":"9.11.2","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GLqdstxx4Eo9EuIFnJVTU9eLjlqASEO2XLrLUB73L0fjYKUWcqAp3CEcrtCI9egoLUOGSnj2w0TKI26pf6sqDA==","shasum":"7ca76a592012061f84c265ec25d5920ca9613877","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-RC.1.tgz","fileCount":668,"unpackedSize":1557720,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxIAdCRA9TVsSAnZWagAA7h8P/29S2svGxfgbN/Xsf7P8\nSSphnRSC3qwoyfAXGJ7B/ToRz4os6iOvHb5dAyyNyG+iKaPO775nV76CrBAy\nl3oHXFUhVhi/VcYoqPX+boAgmrXmwlju0yojsAjlJg/HHk1Meqhp5aRFZVq/\n+cPGNM846/Mo37VeeEkO/uTF743iJAuAf3JiaZg8IVysQiPQfvIGFzAOA8Hb\nJqJ8PjVLG8Encog69KjpVdk97G1FRzrHUV7Y0tC4ocLB4oit3e8ui9PPKxx0\nyON/RpytHRgB/wbUX6KrLWfaGd3me3ub10ie9W1K0Liq40DvcZTaIifqKa4g\nSqWI2e5RhgsCCVxApZ/nlBBiAeTHDKeaFbxOr1tyB+02qrPCMovrtEkCsF1G\naHSeSdMnuTOrFDRCuEvFhkhVSdzeNGtK6eyC6MfWsMtmNZAvtPXg3VgTQgbo\n/2zeejyDWtmwRrAbV2+Q4zf2UB25fKYy6WLtK+KjVBgFKGzBs8SPKWf3K/wY\n0KXZd9wvRWLVTC7kevD9kTplv7L2ThkuWehEg/f86RqDuTfYm+4App17VU79\nK1qd6wHF7dl1GjKaYpxAgPYyXN8RNdw6H0lIjlNf0nCBjLxdTHWgo8Yvcv4z\nBCAogD8TtnhZkimc0CiuAT9ANaZT92dX0Koo1Il5YUtYOeERB7bRi9+IdiLL\n6O+e\r\n=AsYA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBDpktXuMh46i2j6dtb7aEHSoKAhdjvhzztnAkDy64mAIhALK7xFT8uzh5d+P4hiP7R249jfS9HnUaL4xarXnD9DCg"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-RC.1_1539604508972_0.2000152837138447"},"_hasShrinkwrap":false},"1.0.0-master.20181017081924":{"name":"@atomist/sdm","version":"1.0.0-master.20181017081924","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d16548962df8b8980d82dd031b787572aacec87b","_id":"@atomist/sdm@1.0.0-master.20181017081924","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ozyDsanCB9lqiNS+dDD1uAv0p94XFIAqz0hsz3TWJD7ZcGf9lRlVnBd4X845T4sqCAbDga/NbO+yCYDKKdz5Gw==","shasum":"04fb63138fc9a64ca51f83a4e0e9658db8323173","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181017081924.tgz","fileCount":668,"unpackedSize":1560817,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbxvExCRA9TVsSAnZWagAAhPwP/2GSXn2ANwoMGsnuXJj3\nXQM/uKkGa4wDpSGDkIylYUHBl2VkE9qlLzb9VrJNtMb2yvU9QJDVpDgYqhoR\n25XXLzjYl3vHgL1CZZbHN4CM1kpuIuIDMIlyRmr8ENjD5MFVlEHm+hc2wU8U\n7v/2GFkmB5p2FAFUyecgHQplJFA6OTmjclQnS2oqH7vblWb/4GZYFYMajiwO\nyWN4E3DabiGcYP09GpCOd3Rx7xWrEqKCZrQMwquvpznKdaXW8zdZlURdQwyQ\nKLfzetXtspYckl+3kpfkzf4OY1Gk73iJMs5bbECwsOs1JA6qLqPANyauQ7QI\ne8gNDME09/dkC8QIiw4+K6hys0IMVaAEG0QOjUqd0/U3j38ftFIdRhtoOE7W\nf+Uao2wAhFG07Vv6sozKd5smNeWJxQXIf1L6yYG1zy1mZUmXhvdt4lXygOI4\nngxRzX+Xmpav3/ry0fb5Z4wbWo7/yB2i7z9SXwitFr4am1UhBlSpngrm5CF5\ndTirckkB4YUVBexm0182+x6jK0LBNMNR44TArvL3cpQ8fbMR8AC2pU/4Os21\nUvAr/UzgZBj5pJlX5niWNdNgHxoxrSo2li10cy7uquG1koA2pq0cWkze1DQc\njfiIwyq7GGi53cmLQfcS102r66g1PKlVVzm4PpY71n6+liDQj9UTukSDaQST\nuo93\r\n=m7iW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDfaX82QvyQAmegiCZUZb11pXeOOD11BBipsJS7NbZdcAiA17NlzIQSUbFRmPfxNJX9Y+JyhGgLKxnkf+pSq5/F8Cw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181017081924_1539764528109_0.9967808835587677"},"_hasShrinkwrap":false},"1.0.0-master.20181017121533":{"name":"@atomist/sdm","version":"1.0.0-master.20181017121533","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"edf9dc715ffffc7e077f8c703c3e16153e39412a","_id":"@atomist/sdm@1.0.0-master.20181017121533","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-64+48Hq3ewzh3ug60XBecco3Dybf3DNZ6GjMIiFQ28kBzilIfgva6EHekKo/ujE9Z8DUBpvJ1pjzkg3Cak7PEg==","shasum":"8ec98c4ce96570f78e09edd8b7055ed06ea1e258","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181017121533.tgz","fileCount":668,"unpackedSize":1561355,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbx1WfCRA9TVsSAnZWagAAHxcP/1P1+Bw+gHlKPCRk0Yqe\nHFsNs383qk3HBaCxdPLVxMPaK92/QsYV8zYH8Z3DfyCmgBYSmoN1UwsTovOv\n/NdVCW0n6Ip0OTRToksx7y501UabvJ3aYcRS5SFco2Su3uWK79H59kW5HzNp\nefjLcvljPC8pR20OABzyXoc2NZzk0w2j9Qj5EwL6YtdpBn9Fu3uTi0p0wUtr\nvr07AClGMP7XINAC7aBGFqzCZa3Xp7EusWmzQTmvzw/N7qp4ihAhsm9lgox5\nzC06bRS39UojZfiEppdPB+L0xweAvqITFalNsnqqJmG5u4GkWlU6SDYvrhxt\nCesJgD1ai0oQHWEYcd5YaCEGs4pEuLfPDQzH1G1qVciTwrH2SUtrSUETCz9c\nhqPqdCc4eYKoloX8oVA/t1m4VOgT6PFtJU6bfUpRiRuWHAuEJJA1vgPDD8NV\nvhgqO2txAX3snf/XUsQl2ZdzCS0U1pjFyHQGARV1IEaWS1QsjqVpnoZxEdKc\nFLWr+O/T4PMM1QKhwEyksbwAQmfJqKwN7TO4nL81DFjqOv8tAgcysCAw0/o0\nZlHHAjjHC42TiZabtLm/5tNSUX6VbPCpyst1pV8mQFli3jVALjYQ2NDbzp0v\nrcRgYT/4LA1lUPbW/4qVYb0jUxpoNVULRwUQ+Nb07DT5erM8oXxwxrkIJHHt\n6B0U\r\n=Ol8l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDhBf5kxVWvdIZhuCbZ+/pf9JuGB0NJG9NnWbnuqiI1kAiBRygO68vD69rhNyeLNfn0MmIhz42oEOomyr0xT3LI/gQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181017121533_1539790238248_0.2342292848402916"},"_hasShrinkwrap":false},"1.0.0-master.20181017215705":{"name":"@atomist/sdm","version":"1.0.0-master.20181017215705","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"00679b8ff43ecb794ee793d5a69f28a30a2535e9","_id":"@atomist/sdm@1.0.0-master.20181017215705","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wEFz7KERRCNwJyFY97bJJdyb23mCBCKa4z3kGjg4nfdRw1WpvgA0osRlArAPx6amDsFQPIrDc1FmwVFmm3OJeA==","shasum":"dabda0ea89c1f6ae230bc665b454fb4fd8814f3a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181017215705.tgz","fileCount":668,"unpackedSize":1564325,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbx7DbCRA9TVsSAnZWagAAkcAP/1iXxIMxa7I+hc/7bWd9\n0i5e8Lbyoth9zHGeYqkesOOX8L05FE4CLQy5hIkajIAaVyY4YC7wDDirO0QB\nMI3hOw6f/fw5dfSaEXFjySWQhAsOU2VwM/hcixxBdcx8/sq2jiQ0uidu0OBA\n5Pk956/4mUcEtwpNvBZbkUFQbvfTlXhAD+r3tOB91evsX10cDMavmWRl7nDj\ns4/m+XbjDxFSmeYdOcXUqCWnstQhI4GiQwlmJCra5Hbftges7zrecYnLYHvH\nAE2Cg0aRlboInIR6vsV9VNQUZd7KgocYp+h3h/xnIGUNPwGpm1hHu3gUFsbb\nYYDnhJeArGnbTK0RUwv8Wps4RD3yJrjykfC0leY5dIEjq/LajazIsi601+C8\nzY6YDWQQIm2Mgnza7y8OSEpxqF9Jb3PmSdzTfk29S9d5Upj2PIltnfu+gxso\nn+lYCtfOV6YSpGnvIvnFLFZWyLbW2oRYWaYLdbav1C51ZT86EFrqieEw961D\nW3l3Y6h9DHo1KUGFmnLDSXZ52X1va6lD/FbXwSn+7Gy2Ym68sMtrzaFnnpcS\nHn1gk5BC/SRd4JXYZP+EOTntiowJS3AHDShCZKGMyf05EsJ1HTPgEiTqk7tR\nIqGt0Fi3B/DR/8yqS+AX+rr/72T0tQQClYe6SplHtuP90y57lO4ovCtYsY++\nKA8n\r\n=jk5a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICwZZZU8TpTG/HZRUJwEKwUvwlwMJog2g2odjk/sj60bAiEA4foyImxiYciJLESG62YYWFeN4LSk9juiDXcxM5mumCc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181017215705_1539813594789_0.3958121188300512"},"_hasShrinkwrap":false},"1.0.0-master.20181018153716":{"name":"@atomist/sdm","version":"1.0.0-master.20181018153716","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"078b01a3ab09bae623434d1938a2b6b924ae1494","_id":"@atomist/sdm@1.0.0-master.20181018153716","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-hFxHqiKHXE+1OIFjf18EfyFBDw/IKDNFI+7QI2WrAaIgzhgGqvW/POp3OauxS/hFiFE1bQDlS7lz/aSoQ6m5Rg==","shasum":"cf87ac21e7f60be9e658faf81599f32e6e1384f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181018153716.tgz","fileCount":668,"unpackedSize":1564108,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbyKloCRA9TVsSAnZWagAAnvoP/R2vu5OWCRZGhlreJYcy\nwebe2QymGOSWaIVFBOUdXTGjaHlHayrdA9VSdraWDmLvyD3Qe+QMgsw582yW\nV570zItyZYwkTyKQWssTgMn8JGUSXHd5ndVkxjVmTyXptaUgiywKVJQI9u5+\nwcEJdZND1GScRmMpyHV7ADi4qJCMucAHvWX978Y3LMuSG0uQl2Dfjf1Ic6ve\nUel9bP33pBFVa+O0PJDvKacSHQIwCUTXfiuZjqFh3jLLjki3uZaQLqHAAmYO\nuQuhyC5vPdIICfN0kT1Z8X/0Fc+7yR1u9MBo0slpGPaPw+rAwgPE16+H8/lj\nlBvlz6DVvwcsZxUzzXw5CegUC6i5n0mi1S2x81dLpaflCoUBkd0uYUcbC/Uy\nE5GSy1BRCQbWX9Ih4ukhajdeLzAg7R7TswnNnZrLiIFU3dxBFaw11Wac7dYG\nWPC5bUZaRz8LOFMa/tp8poLEKtcsk64tvFOF84I1ueJYYubjcm2SLSw2TWtb\n5OoEyVHSfxbTSnSQl2+huemWrhzdLWKO7/j6o6ZbAKmfQiTRbkrIoJSbsqrE\nA6KcjAqrMRmvzMOf8KurrNHG2tuft4gBsoIbRM+gz8O6GIoN1CW0ujIoxno8\nMueJ1PYyj9Ac+dEC3Es2m2AJFGBF3Y7KzbKZZEcCjG7l38KQc1heMA+/Icot\nMRzp\r\n=O4rq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGmvp8Zc8zbAWd0i2FTJYWlMXaXoqP5ERGoTmU/pL5VzAiAtiF+5XzMPVp7u8h2gLfQK3UTbIr3Vqv246/GkYthC9A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181018153716_1539877223690_0.4333582966894347"},"_hasShrinkwrap":false},"1.0.0-nortissej.apidoc-comment.20181019153307":{"name":"@atomist/sdm","version":"1.0.0-nortissej.apidoc-comment.20181019153307","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"eb629537d6e177bd74eb89e8d2525b6d18050a2b","_id":"@atomist/sdm@1.0.0-nortissej.apidoc-comment.20181019153307","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-syhOSHZVe7Q3STK1LJ+Sq/iQF64WtH0YOHSgEJ8PDaZ0qej1VW/3Rcjw+M4ryrur3NBYDLzVYPqqnM2iN70n9Q==","shasum":"68a147bd14fa366b2a6c2645271d37f84f7c0812","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.apidoc-comment.20181019153307.tgz","fileCount":668,"unpackedSize":1564122,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbyfnwCRA9TVsSAnZWagAAXV8P/j+7P5zajVJlhu74/c/1\nzF8Yk+wXb1HwY5zfWoX6lH3EZ2XYHGatTTyAmEeYxLcXakeNXZSAL5zVXDwo\n1XIwbYuJO9kxiO/U1DMg4U7KtzW6uPlc1ZK0REcTrS8GUuTYXoSiTVglkC+M\nYG8iDpEW5qUUt4P4ELs5FCbJuxw7MotspC4zfaYRGGOKmBDzE3t11Gcv9yb3\nLXabe1OgyfLpUNR4w3xWTqUJtFDyQXCn5EholH9bcBv1E0gyMP18RDM8gRtJ\nOkhld8VlcU1nMnIadtaxQYdn5Whdm7lLEkv1/b9dZAaf4JqS6EOqBz6t04kX\nO1hdTF4XR6WwE2VIayaJ6sAWiNYuJNbi+Kof3DDoKv6ozDH+vAyDDPewEHfK\n7VGAqcACK4v3V3XcbmrDbK5Ja9Z1K/86QYq8wVYQ4UyZtdjp5RozSvF/4iI6\nYv/vy19K+axZPE0D6Ky3UKAl+yzD7vjvmUOiRUZGd8gTTU5R5OoeGpMqCRlA\nxeWA48UtPlLoN8jdCgv7nbpnuOFbg5LeWH3MRaqKttehkRn8LJT574vq7odz\nkBGp2yX6LdTA2cyWK/VvmDOSLNXndq4h1oxUs3eS2bKcyDxiVPdKtD5zph/x\nZ4aY80xxhpR/SRlxQEp3dzHcV55shnbkGEt9+C3IWfc+qzZZuKpU2zsqNR6k\nLoqH\r\n=Cmoi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFYBwNk3/mVQsgrqMUxXOYNbzDkG3NBiaepDtw7W2FbFAiEAo+gaxEw6iKqaG+kwmBgHR1+LamIfDStBXbEbcpuD0Mw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.apidoc-comment.20181019153307_1539963374970_0.7220489601104239"},"_hasShrinkwrap":false},"1.0.0-nortissej.apidoc-comment.20181019164400":{"name":"@atomist/sdm","version":"1.0.0-nortissej.apidoc-comment.20181019164400","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"49ec070d44a8cbe3f8b9c5d051fcf27cf3a1c398","_id":"@atomist/sdm@1.0.0-nortissej.apidoc-comment.20181019164400","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4FhWJ2fA+Vcr1s6mCtD3JjwdlGFaPgJ7rJZCbIrTJk/cZpFDEqmGhpRwAk/kchWWxY/ue1vHpPtmrlJL1HhOEg==","shasum":"e47dee99934ecc9c2aa52835e92817a9f6b752d9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.apidoc-comment.20181019164400.tgz","fileCount":668,"unpackedSize":1564180,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbygp8CRA9TVsSAnZWagAAZpsP/jQaZ7ueYu6JqrvJGzzQ\nqW8goNHwHgLkvf4/UtePhTMVF5ANfjT+ZupGR4kwQC2yB4FU40hiJsUTS4Rw\n6SIPu+oVND9K2jPs1wDiB1CzVeKFPoYrVx6+8HAwQZi/90l34oOugnbFMtbJ\nvOynQJWELNSAZtVYXYM9tsvyBGsX6MhWOZ6VWsTOmS7DRThVX4NJD98FReA6\nuZ0IegTJuioHQSvo0c/A73FFv+e1O2VLyUE2kVPEY3S47Kj0/brBNYaou4Al\nUQVyXs4KKUpLyyadUHRZxuI/ImUgK+glDLMveTFvVTKedBdbR3sY8CKWglSG\ngFFfF4Rg6dfM3kxWz2+NZTbsmz6ehPLE87KAHFh/KaixlltkLS453oHjes4S\nksTWI0UTEuLXgTZZYZ4HwZFXerCpJLS5KY7MJtpUnpX1EawDkhq9uy/fYwVz\ncrbv6IOJE1KkeSq6C5x5rxM0VUJcBOViOuv4Sx8HARWqqJwutF+msKwvSugl\nhvR1QdP9tM6tTXx3NIxZWKqzhE5U7OTPHOnPsCpKMdEE5pLpMNkBk/StP2gK\nu2lxDwIXgy7SZgBwT599KMp1QF5HFrnhzAZ/lff8NE2HTWI4vvhVG3YzGvln\n7l4LYbhoA4WKRnJtz5tDYT5iz3No67XKfc0n9DApGCeA0mFZGw5sZmpkED1y\n+rwL\r\n=SfX+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC3jzuH6DYSfF3GKT7XgpxM/UhGS4QIfXvNJCa4yRZtnAiBfS4o8V3kGqYU1viF4LLdOrNVHXS0ykycJEGO3JE2UTg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.apidoc-comment.20181019164400_1539967611612_0.5021328281890698"},"_hasShrinkwrap":false},"1.0.0-nogoals.20181021234208":{"name":"@atomist/sdm","version":"1.0.0-nogoals.20181021234208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"468f1954f57c622029f78af2fc060d07878859fb","_id":"@atomist/sdm@1.0.0-nogoals.20181021234208","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ylp2G76qyDxal11s7dFFZ4VmPd/8jI3zkZ19lnBdADelY9oMXqtWLq4hs4zPYb+yTBupdy//n5UVimJhmKlYVQ==","shasum":"3a74745977527b3ef4a130d4d318633283923df2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nogoals.20181021234208.tgz","fileCount":668,"unpackedSize":1565106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzQ+MCRA9TVsSAnZWagAA+n4P/0rceHwVQRO/jG/1gejk\nKV1t+VOlP2zYvVE4Yj0W22/W2q0NcbYG67109SLc4ohhqHmfzXquu7J69372\nbPeDOO6VQJPqQkX4on9KO1YCXZyQfNtv+CKmmrdtze0nB+T5AxVI5Mh4dr9Y\n9oqOwd45hTlI0b99y9DT5S6g+HJQXmcT0y5zljT+5LS/V0uzLCvsUUcvs+Ig\nvmeo2+RrvLOcBTkUlYY+tHhveEbWLkg36YtSyir9g9yozCxB+4tk7uVrwsal\ntdjlSTiuM9hnB+OsOax+ZqHLxSW7Y/aPRGcc+g+k2qhfW0MvmIOwL5miwxII\nG3Gh0xD3WlV5U/HK3gc7UHOrgVGgRwOpKTwh59xIBP+43tiuoOJg+qGMZ8MS\nhiopaVDUPI8K+h+nqvWb9K0J1dD0P5Ce0JxKt5a8jQTXLvjQJLFLyT7q57yo\nx3AK89kZ/DCOMko1x8xS4XnbcU0EkfJR+pSi10JkvhPP+rv57cfw3f3GhzlC\neCiSse/dO52kOUZlk/k6zBkVforEShPLtTJJ8QjOHpaOAIidH5JKCaE7ibSc\n6dP93Q4KzC8fjInh1J2KjQmcCKp+q1dl//t9ATsMiOaK74oU9JdvbS7EGreL\nyPIqsotIvtckb7DmP/7j3nSW/lU1+rVegw1cPLJhi3u6FWZS2T4WCZddSrGN\ni3Jg\r\n=1dRx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvjxfHdFpDXdisSaXP7TbkbGEqmkMeSXHXGL/JYjRDLQIhANniloLuIaGPYix+ShWGLNpTLF2qrIQ9V8V0voXw+SGS"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nogoals.20181021234208_1540165515610_0.838435481121653"},"_hasShrinkwrap":false},"1.0.0-master.20181022013021":{"name":"@atomist/sdm","version":"1.0.0-master.20181022013021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7aa6fdc4fd457bb1e17a7ccbbcfcc5cc9ba03b88","_id":"@atomist/sdm@1.0.0-master.20181022013021","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Bq5ptZUlsW/oWRMN6Ol3gjnqnHg9FRMOVVu+7LHGnK+Y//xJYgTTXDVvWxVpLAH69UNCnj1KHJkx1OwQbLSvzQ==","shasum":"dd92f8deb6be445da28eea8ffd2531c2e04e4ca5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181022013021.tgz","fileCount":668,"unpackedSize":1565105,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzSjoCRA9TVsSAnZWagAAQSgP/imohIkLDC+wkMdsb9j0\nDp0kNeslEa0sXFtmOJnGJiIYH3+H3XDEemm4zofHj8adHrwkJGc2wjJ6pae1\nA8rSrnTCodw6ergXQedwykizPROLIXld4P1T8i/C65VsT/SISuZYbu0nfX6L\ncWzMeF8d1j4BRv2WwtVcbPNeIRFDy1+PJXc+RSuCKHpuuQJ70vSdmT7h+YN0\nvaprqltCZGI/ZRifjY/FjW9hv4S0yXNGpVdpZ6d4ZWv0M4TWZeAl/Gnjqf/F\nJkQThcUxL84M6H3lq9r4n9ijs5lM8KFqjrX3LhvzoR0KzjHb4NZxK4CVGhFX\niI7MiAkbL5AbbRurAXB8nb9xIr3pV1nR4LGV53sUBFyApIJdlWKz54MW/H2Y\nFbYQHt6zYnwQuGL0P3C3G1clJ2oCyhJOErE7+MGEMkUPOVywV80zrMBDZs1/\nKk1OsT3a2RzHUmzLykF9SCfdQaqKuhEBBKLO13qQm51cLpatBXWjrjVfOCLN\nCkKAxTRwqPoUsxQDSBxtv4t6u/tGRTJQj/r25e8NGS++61h4SjlmQoILkzdX\nH/D5sjtswPrHXTxHIUF23nSI7VkmuiFusFGt6AWiMq+GWT8MFulnpYDG6TD7\nBX72IYNW67Je10/pFjpYXQcoLMYKbiG/LjyXfj19FI9pAQP9rncvYJOIir51\nKVHW\r\n=jBsl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4kQBqdBWkT8HF3Cvboc55aZ344sF0QRiaEUS1GvjSvwIgENzQN5bzfBd8eM8e1rVFmo4r5YTkblfhljnaI4Ehtiw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181022013021_1540172007532_0.007749283622418224"},"_hasShrinkwrap":false},"1.0.0-nortissej.docs-only.20181022031406":{"name":"@atomist/sdm","version":"1.0.0-nortissej.docs-only.20181022031406","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a7fcaa834c88d2d59b4a4bcbff6b5fa8d58d33ca","_id":"@atomist/sdm@1.0.0-nortissej.docs-only.20181022031406","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Dd4t+lMi7fXlehXzRhLFX3y82qv/KAaYBuZL8lpG6rOtfDxd9Q5if7rnDYH+1eSbkrmyNHnDz/o893xOW7YHfw==","shasum":"f295409cfcd1d150429c05e1bea7f6f8fcb16cc2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-nortissej.docs-only.20181022031406.tgz","fileCount":668,"unpackedSize":1565324,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzUEkCRA9TVsSAnZWagAAtTIQAIh/FDF7l1M3SQhXcZO4\nuXTRAd0/+IAEBnZx7DWIpEdZlOEmiZV7Xsy9KagxMPflwf9Q2dzRdeqTFAs+\nVJYmUJwztRt0xBu8O0DYywqwrPy1L6spYunXThA+xGfvdXqFBK5UAdGBg4ES\nIllp1B78QIpOrX33X/8wGHGzNCIQZMGN810XWsrWxt7hOsuDZUCSCkY0eNow\nVyjjHyrpVFWulaJv3YaBIJIcFjNIUGOp+iDhwDgiPn+vU5qIYM377farqpyB\nbcDNU5Qqja4LPf5EQcwKkdG7SWxZHJWgxj074cBTaaOEZWhasE/WQgIMCTsd\nK2vduIanFMlK7TmzmqX6IatpIIu+/PmhBLXt7sFDb5ffT559FophIfFiMd7s\ntuY5K1QInD1zUM4yIotceE77aYETUlDhGw2Qhx9g90PMf7yi8hByTSwYGAVz\nm7RhA25Ee80Vlx7f2tdE2XrTlvFzunCfn+NOCl34s+V4k01tCJ3hm0qxBh9x\nvQVvJ9d0z841JwUVLxG4gjXXp/pFNkucphF/n2ovaXmEKz3D3SIpJN/nW1C/\nsCZZzAjMUPPdDBieBIStHdEXBSD1pi5vH4aM5trV4E1YzHpc3Po3T0tzVyHo\nEoT+jcIO/QjjJ0Ljr/Wlze9RiSYjr9OpCyCJM7kehsT4/bmzmUnYyyaemylB\nkkKw\r\n=PeNn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFUDbEYizzEp5ryl0FpwBjTcPIntxri6fh+GUY5YZJiQAiBCtdv5GS+c4YcmLR0DfNqRfAhR7x3sfSMsO57hIx6kig=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-nortissej.docs-only.20181022031406_1540178211439_0.6843263691446051"},"_hasShrinkwrap":false},"1.0.0-issue-551.20181023092857":{"name":"@atomist/sdm","version":"1.0.0-issue-551.20181023092857","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"397efb3294194bcc6538511c3274365ee5425d48","_id":"@atomist/sdm@1.0.0-issue-551.20181023092857","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZVkqWX+rdMhaLXkFHwC1IgbVEj8GhJUk3G7Kfc6N8tG14YBhuvqkrqgT0V6LCebtuqcA6HwHTJBOEWdL1AavxA==","shasum":"abfafc62a77b7749df053144eb3dbbcda39b8f31","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-issue-551.20181023092857.tgz","fileCount":671,"unpackedSize":1571873,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzuqTCRA9TVsSAnZWagAAc7YP/3kwLoOAtJrTsG9+Cqv4\nYPZCLslqdvGS2WKW/Fds+oNO6ihQgzhzNhSgW0Fm5JpYMQlgNGjnWqQ53Qpz\nclBi/10N415jyaxQX+mGOuu3Y6coNHqaGUWigTJoS3raZTCjjlyPPiuFnYVj\nrCqIA1bhSwUwFW7Be3Lus6/JQzAL5wi+DJ6Krl+Aifu5h01W080T2AVRzXEs\nsP/bW6PdC73zJKu1sp1oDCzaj8oNUJaP1TDpNyzVU6d6fPSEMkH6USIBD+pP\n/uLfWPFsmjM9DkbrtFNJIkl+wUvlXv7IXVGIpNS8KRoou6V3abdPU0wPxEff\nwBJMZlW0op5nEGu3n1XTrZvsI1rm0s1eDP3h3fao6mYv8CM9SNYygDx8kcWi\nFTDfYvXVehWfJHg1RYZ6ZqnzQ+6FeIZZn8vAJG/S5a34PCwqby5yudezUW6W\nJOGMQR+8I+7fjyKt2bwfMZMhH7YgOzHgAUl0l+fEAbHnUDsidS2e6SoZs7Rf\n1YV1LLBhr/+mUfNjWRqZ8ael4Qnglh2kMuB6Yg0wmXpX4Wy5bbdgkfQAlLAe\np0pxUQmqMI9b+FvD18hEPyBB1HvquR0OkEQ8u6IHTr3wEGAtQipWBphdTB6v\nrFP+xAf3ozYvZ5VI4XuDdgYERsolQZumGJJ333V3oJtRR6B0fUXSrvxal8Hv\nc5Go\r\n=Twye\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHchk1Wf97Qfx2rN6fC0vdDBpQ58lt8tVHHdQOVoKTUWAiAeb7Ahv9Nosh7E8YEwOyyWcgumEUwGyiXdqCwpVms8lQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-issue-551.20181023092857_1540287122848_0.15831129756772366"},"_hasShrinkwrap":false},"1.0.0-issue-551.20181023114228":{"name":"@atomist/sdm","version":"1.0.0-issue-551.20181023114228","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1517b8235d040fee0712ae05a5dfb116e2b8a57c","_id":"@atomist/sdm@1.0.0-issue-551.20181023114228","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6IpYy+EO0lPpDM3BAFglwjwuvkoRovNYY/oep6OPV4QbS7lA5F/AHQKk1VyMiwSiKrFiLzoWLrP2xMFWYLcKXw==","shasum":"dbe0dfba1ccdfc907de12c9a129b42ed1b2c20b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-issue-551.20181023114228.tgz","fileCount":671,"unpackedSize":1572335,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzwnqCRA9TVsSAnZWagAAP9oP/AmlRCrZeZJnNN880zR2\n6Cu3rU7Mjb2yieSKuhjieh4bSeredaXtAldILseigPjRBvq0snHnKym1w1Up\nfAMcTG6YIVbPQKRlcJTLYLEDGuMf/hBWJ7A1pOQ+qlUtHQgs4Ykc65i6tkxR\nqbMGVNfjaw5ld3tfrZlh/yzuJuJB85/9XLc1IPOIz/XJyZkojHvhTxv7iUBz\n1g4ic+q5hfkMdaWebEEDLmoAzrgJ2zncjv1e6ZkPcz/1FxcgJPqdIPE//wxG\nbB3yCQQHcfOiXEV3//XFhfVLM2SW+dXWRdloTwTKgq2W34I/dIcRdj8ruK51\nAI9gZVDru6/jdgXA5d4m9j4tCwf9T+L8jE9k12bnDBfQkMWLVU2k+m1MVwCN\n8EdkGqpPcUZ7P4UrqToWtp/f2xYdzHe9Uxa4GC8ZeBt7WBySat8yQ/2fwydv\nNWvQ2CgIBjmcROVBr+cJM8W2i1XsOTHTFQ6QVMap0ipO921LzoS+/lIqrZ88\ngbtlKtS4+yLQ7O78A+qNO0cnmNPbr5X4zFInpu7T+72+Vb04zABxQaigiKdX\nMaGeyIdmtP4Gq9P8/mKIrER1n8JrwleZ6wON2cyLxa9G3B3bwkb4GGet7F6S\nndWm+F7CaqLFJpRIbHvfNJKWKnaJ3OSVdO1DaVPXF5zm0MCTSvPvPH9WVdz4\naV1R\r\n=oQeY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJa0brlgGvO29IUOA15TyKvfhM2kZjJq7DHbL2IbTMzAIhAKWdAIKknoB+YGmwFGw1mIa+dOQkQS+RRZZgxRl6Y2gc"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-issue-551.20181023114228_1540295145398_0.6449296769566715"},"_hasShrinkwrap":false},"1.0.0-issue-551.20181023145323":{"name":"@atomist/sdm","version":"1.0.0-issue-551.20181023145323","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ff6a90b402721ef02db9b5a0abba084b55a88d55","_id":"@atomist/sdm@1.0.0-issue-551.20181023145323","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BwyYeaNjMyzTYwyiRU7qi75z61QbZcIhPNFJRCR/KYcTfKMq6gsc9Rirw/BXQd6lxjPDVD97WQlGZAhpSspGwg==","shasum":"1f5c43c3dbde14dc9d37d6acc4bb06d40078c0f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-issue-551.20181023145323.tgz","fileCount":671,"unpackedSize":1572517,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbzzamCRA9TVsSAnZWagAA/aIP/AjCAsBm3IoZwyX4zIq7\nTx5mVwXB0CkB0n+aA3oF6F8emUiu0wr+zY8vAqDJDWHcEcoTPR5VAs+QzPH1\nTIUy4JJUjjMREtn3zjECX+YOJlXkzknInIscz0qC+vjQSmuSp8oIaMVEGFnU\nxljhxOvrFgDrGtuvbUWdfInDKTRGE+vxG1dLMdj5K7apTkctHUXqFfiQyjUd\nmDJvxG4wAy31Je9isJrj9hKBd2trSdILaLcdl1KIM2q4E4pFl465chFyVYdy\nyY+XODZWHowG3P4DXbKmmwCGymXGq9ev3mT0geu7apuid3+2/XD0pC+97vYP\n5heenm9R1qDY79ljQ3rqL5uMRYIZX4VzScp1VyZddiA1ttwP1k/oTq31XIo8\neTJ2XZFZUuY/8z1GLCXc/Kipa66oTk3vtMdJv/iH3kJoOFFCIeEf/1dVDkdi\n27ZAnuYBbupBwqXv2/jxec/OxxjWsrYTAPrk73q60KHS+dyh3Qrf28SLdreF\nLc1HfmMWGogcRYfmnzuW1Mf/7HMPY+45aVDkGwK1Amxt0ok31hqqCfqLXPHi\ns+EDkmqQ9nWuPlnsKr3a04oMcDRP1NZOAaAsdtKqTyz9nF5O4Ss+TDi/gZ6F\nxCm7JSjG84akOYNQl9R1R55KDV1ebwuOP8SFkV/VyLuZ18lBKbBLz8Twhu1R\n7+l5\r\n=QjeP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCThl69HR8qovikxGNE2Omnkt8LbQ6ryNQZ5MPPZOOdeAIgP85BAnGY9eT/K25S1sbZheV/XNKW3XP/1Bw10UYL/oM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-issue-551.20181023145323_1540306596956_0.2249843116467405"},"_hasShrinkwrap":false},"1.0.0-issue-551.20181023175727":{"name":"@atomist/sdm","version":"1.0.0-issue-551.20181023175727","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ce1cfa553c4f6b39ba3cadbf7d98b2c9b91b729","_id":"@atomist/sdm@1.0.0-issue-551.20181023175727","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dFpMreLKR8w+3Yg7mI0mYwvb9/Iybr8B+SgmYnvCgZEIineWZux7IXby5ikFjn+71xfSnnODeJX/ub0DlwSmDg==","shasum":"297d3015b2558a6633fdd50c6494ba78e51455e3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-issue-551.20181023175727.tgz","fileCount":671,"unpackedSize":1572517,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJbz2G5CRA9TVsSAnZWagAAjMQQAJs8Fa4+g2TR4EwVQfHQ\nkM6Uo9OXAl0PxHpF9rLA3BeBCMDE36qXXsfYJ4IeUYiXDR+Sm4a8j6MLvrMA\nzdkILZ4HP1VFvXE2TeykHKtASAzixxQLemLJP9r4VfD5XUlGEl2viLU7I8mX\nkh2S+/CquyPAzleHQ1ZRyrxF6a8h+1i3uRMl/mX2cm5m9yYd95JR0Ytw+3q/\ntD8NDOI2o/qxme+fh3RSuqcA7W5JXmHCgZ98XZLLI0J+UFBusgUZ7nfxJJwV\nTsBsFMe4E22X6Uxm8CQVlQFnX+Hyw7yqE+thXwQx6WqFZYgcQh6TeeSkt8Ly\n74Ej+BYVpL3gQ6ySKZ4KrN3M7/sDKAOmbo4oMD41IGsEMjFLKozl56qO+waZ\n2bb3e0wfzYFF5nlZ+Pf7RFUns2Wp5VB5l70f25HM+KqZGEt7J4fW5xRii6mu\nxwpehnVhyI1NC8LBweRL13tnnS18POpf/QoEcvo+eKQyUL1nOETWLbWWtytm\nJFKTVDI2QAxxyQ+Q1abD4kzkGXiq2fLxY40whNr8Bs58lPUT8rmBxMfwzpb3\nQNotZKzIbDNyCSGjs9oxFkUOhhD8sCNnHreCxSXqsZg0KG8Kblogh3jX3h5b\n5ll+QO+rtcKfzSdTp4oMjFe09PvGGRve2cMQAbqdahwbSW27CNpOKd+9+WRq\n9CAt\r\n=hBnY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDu6huTKEcFMlK98vRjxCaBz7zIiMP0AbcBqO19g3obHAiEA8E0TzZZ9BcQtv2ps57r56UXPc9yp3aHbgdBl9cGbTms="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-issue-551.20181023175727_1540317624027_0.8646729051556161"},"_hasShrinkwrap":false},"1.0.0-master.20181024141507":{"name":"@atomist/sdm","version":"1.0.0-master.20181024141507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e386e1c01c215476d076f2d5883f1fbb97b6b959","_id":"@atomist/sdm@1.0.0-master.20181024141507","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FWTEemkIpctExZDUWsxzQVvioMyiLdhcrCVLq2RDPJrnn9W8KoUkqNdga7EqZZBJ4s1sWh+WqQqa8jdPWA97Ew==","shasum":"e6c019c3b49170946fc6e13bd1b8ee0fc509d760","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181024141507.tgz","fileCount":671,"unpackedSize":1573844,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0H87CRA9TVsSAnZWagAA4jEP/AyaLxmI24tZZ5zu8cS9\nHAuOUXKm5KhYrFx1EKaaNWgRxdv6GL91DI+rFBdnTqecYMwzarxUfzBvNTpz\nDo7JGKuMDdu1aHYc96NXsqJc41GBVpED3cXYNvrCioky7pI1UADnOuIKM57l\n1U2eSdhjz/NEtqFCP1Mitz1t8B/dIAf5W5I2ywoTXvfV4vdUhvdwh2bGyhoW\n4Z2RPs+kOUz7p1XpIOwUbu4FpHIW9Bu02reXdwJrC/HoSEXCksgWF7xHrMTL\noJiDiEvp2fOh3GvKFOptM5lzunDZYNgaHYmE/QvVuKlXrsQr0K4H4bG45Oca\nP0YgdiHmRNK91fgf0HE//ESijhJUYONYM3KCtYg2YXohq04ygFbzsuJKqfa7\n11ziUAqdEp414EvP0rdaqh2yNRObo2mIihVoLBF0Y5G74Ct9kz2H7Vm1JzSa\nUZUVv316l2E+WtIPsJLSc0MbcTVE1nDTv8ONMbK45kzyqVlr/cQIVqRKoCzZ\nzkfCpJHRxGjIgrz9SNoET+AnXavW6SDnr/SUysXGX52KD+ucrhoTO22WUPO5\n7CkTfuFInAx2EkHoWutoI6Mm69s6EPs/iFLYB56BThLZP5Xk8vxj2sBOaEMj\ngJn934vcCRDd5GNXPEXYWsSh1me2uBjVNmLtt2izd75e5XizgOTFZ5wSmMrM\nAkIm\r\n=blYY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEN4/ZqAZarFtGXb13qmcDzqYQZeb0QZX9PCHpAV/bJ/AiB074jBpGenlWP2n4TQheV8mMkvRXrTS+lU66qQaha+Tw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181024141507_1540390714649_0.1034107410898426"},"_hasShrinkwrap":false},"1.0.0-pushtest-structure-more.20181025042300":{"name":"@atomist/sdm","version":"1.0.0-pushtest-structure-more.20181025042300","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7dde410ebe9c0b71db57f675da5962221c903f9e","_id":"@atomist/sdm@1.0.0-pushtest-structure-more.20181025042300","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-UmSL6UH2rekdI6CiicBeTxm9sreAEbsl1o+bFZqQ5mr5qTIWJsxBjNvxnaOXvrkACwCMcI2ZMCntbl3hV9OhxQ==","shasum":"b333f79121d5bfc277957802bd87a4c928770b10","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pushtest-structure-more.20181025042300.tgz","fileCount":662,"unpackedSize":1570083,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0UXFCRA9TVsSAnZWagAATCcP/2CQrkzFYEqrqfJAPE7R\ntwWK3NgXYSdAZY0m1goehsFK0qqtpo8dFUhMD5w5tIaiQs6pXpogrf9FX44A\nIjql6ivRjjoiVuGhw99iSdusxn73mHcyG94woHoXtIy4KzbPle4jwlCNMwYy\nF5h+C0QVrzyIFzjii4QQo9sWIq2OwWlF5Q0sNm7rS0Tv/GYL7KkDNC1RHuHh\nJY9abrjuMTk4Gp1RTB3aS7up6i6dtOCepeErJnB1gb2R6GIYDfyds/WrXhBy\noVcnUlndX7KNMX6iPVRlDkjChTlXZXqbY+YC6+RoArQ+bNtbqsOhiwMLe3L4\nmVn/BCJCCIwI0+R3OQupql6NZTyVgZVDh5ftyQw54BcvNzheJs0QFJZL8E21\nheDwdbzmOYOyfthv36gxPOTkPq+mf9Yrv6z/sV9GnIo9P0UpqwMSjue8HQsx\ngO7hyqh8VntDdDqxNEvneKOOno+l/fQsQmVWgeJfZrRiQfHlZOGYc0DccLMq\nY3/c0tB0ulJDjamzvw9rTBcWUL81TNOtnAGA1YZjIsdUwgPKyukqL5s9DV4X\n2Sc1ovwHKcgtY+vAfYpzQ6y9NRucleVm1USUYh6RHbX7waBv1bEPakJP1axb\nk5nkmK5I+/miChU92DX97FMcU8Ig8eOtOs6AOs3xMtA7LpKSRt4p3pvsXM0E\nVSfa\r\n=idrA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB9FIV6D0BZZ1SX1Eytu0afht1J05PU/fL/UNHbIfOEoAiBHXD4SjRCG5+hCp9Rmjnw031Ipypl/3v+MXICGGyvLrw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pushtest-structure-more.20181025042300_1540441540092_0.4764385632339583"},"_hasShrinkwrap":false},"1.0.0-pushtest-structure-more.20181025051523":{"name":"@atomist/sdm","version":"1.0.0-pushtest-structure-more.20181025051523","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6a5e07c776dac611a7ad4f6e6017c41cead0e0a7","_id":"@atomist/sdm@1.0.0-pushtest-structure-more.20181025051523","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jXfE2B9dcXmX6P76I9PnVwxYdZvDGchayx7Xd96MzPuHqXWT2hbq+OsdX0oucptFljSDT2IFOeK5JJhxaFqYig==","shasum":"579e11ccbb1fb3cc51cc85c15d53a244f1cbf2a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pushtest-structure-more.20181025051523.tgz","fileCount":662,"unpackedSize":1570083,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0VIXCRA9TVsSAnZWagAAT1kQAIjgVyZFG8EFHj2Y0tX7\nnsU8OXBYPw15Hsbt9Hnulw5oFIHFYkdD7kEGBR1QhAwnA8oeRDrVPhKZpqZY\nQicAbt88iHD+rgR6pPq0KzbgSlhSOLrPYST+yGK/k3xYWtpxIuH7qAPo0rZ+\nAZgAIm7n6/dZA/5YEfIBFye9jLRGp4t/1AuKXiRrOQ4MkZV21XTbZKoPbCRU\n9uaP9ArUs1mSY5fMfkvdUqb9aNzO32RuWUJ2Dbrh6jvPCpkqKE/JcHVHnAgK\n78buTlw18n5XBi5C1F8K5o/y59pHT0sj23kMDpEMv8viycqQc743E1zetW0r\nhymaNpLdLXBvIWJ9LKqLIxeQg4wU0Xsd26d/T7qKdLsZ6SDLRMJUbHqXhsLP\n9kBbmk20A2EydSRTGHnhNQedUvKIKKSsFewqlywaIUaZnrHWqYsa55Lo0M0O\nHCPQZDk7TWHo1aVqyq3hMBTmD3uQwgk2Ftu6pNQmUN7pcqdtq5dXfhxuboJs\n7uB0zLZyBYjMx8zVA4MsGj+BMTIykVC/O1dMbnytahBBdlp/u8BYfFx+DXHr\n/N9dRxVOh4pXfpcdXV5/33xzhdy7u7/TYVPLwZbAupxFRZugCLW/aizYcEHO\nse++KgTQufq15VBeWSBoiKNR4b5lryMb5OT4FPnjbHjXHJ+4VTVnnOWuaFgN\ne5r6\r\n=qVgf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF7Hq1k+qze7ZfbcQU4mRiuuaBr664GMCM4CTD+U7XihAiEAo8llJEMUZaSLdbEGcrzVJEJ4Ks+hPQOPB6+UGRxgyy0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pushtest-structure-more.20181025051523_1540444694276_0.8558845268507307"},"_hasShrinkwrap":false},"1.0.0-master.20181025094406":{"name":"@atomist/sdm","version":"1.0.0-master.20181025094406","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d2c737b9e24e7494a4d5654a68093a31d4c76fa8","_id":"@atomist/sdm@1.0.0-master.20181025094406","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kAe4oGFmLJ+ZnA7UoDG671acQKxjq5PvI3bK1BJ6MSZRmkRAFyjG51KaWHCqvDNvi4orqdLu3WcQlnX65bBDEw==","shasum":"f1aec0621a30b2a75496b96a8f51f533ebe81085","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181025094406.tgz","fileCount":671,"unpackedSize":1574931,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0ZEqCRA9TVsSAnZWagAAb3MP/jJRvW8/gcNy0CY1VC45\nsKBfddKsI0tSWjKobTHmV3DVY8D9pgLn02TCVeBcndIXtudmwXekn5WV3Nwu\n0zGkKhqB/Uh+TC73Q/ch1/VJ1OLCs7AwsqKPD9WOIpM/2IA63dVopwXXCVtG\n1eE8CZjswdb0IZF0902IJt3XFm3T1F8d5gs2SJYDQo02bJdDxR8o2XkKx4J0\n2uSGhqSlMR2Fmb/svwNnhAgySYfP+h4fFFzsWhwT8R64ninnB0v0kNVSKvM/\nZx28xavrcpXJdZHJCuaJTdbsT5T4PkcSNJOeBeekGqKRGV+murhkg/syOFTx\nv7HrOLRCN2xT5nx3qf1AIQRFH7O2wwFZQjqPONT3UUeQRa1OGc13F5xgkY61\n/iZO19XzDgL4/43hSnduTDogtfL8vnmvlFIz4v2w6tmKa6ryUYMHmZC+z2nu\nS4mC5RYvqniH+0CjPwgk/ccBICTFI3sWhbLj/YpZMp5QLoEf2RI+PXHI5jAR\nQBTUeBgtmSp/RrkmEzLT3u4wLrHqrLY76KdJjUJrIidMFCrMboPwefPtdoyI\nc0QkW9ZsZnzmtIa+luHHcLEYE7jUzX7JkXl2iRP56EMkoOURUmhBoA+RwDk7\n65SoW4tV7AbWLSHrAfWWflL4SicNPGE2FhebtQhWB4Ru8SPhrMyTdJhkME6a\nZ5yi\r\n=zp5E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDkn7EbcUwghTiKFFoRzuJjRm56ITFiVZ9xMEYO06+bBQIhAIhKgIsiKFuLv8pWOtyeKgjpIfGm9QKuM9e1vTAqNwYp"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181025094406_1540460841318_0.5352752580218711"},"_hasShrinkwrap":false},"1.0.0-master.20181025110208":{"name":"@atomist/sdm","version":"1.0.0-master.20181025110208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0b2867ce007da9c39a982170b8a041624da423bd","_id":"@atomist/sdm@1.0.0-master.20181025110208","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eszJLA6ZnEf02D3r12VFahf9lIJ3sT1S0Qw0d8B9ee+YbZNfyaP6IST72Iz8MiWHeDOZ9h0pQIxv4DVr3MrE8Q==","shasum":"6c28aa0b6a613982c8e1b98660583ade8e3c5c24","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181025110208.tgz","fileCount":671,"unpackedSize":1575041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0aNRCRA9TVsSAnZWagAAaOQP/j9g1S5DegnGX8z8Pmtv\nY0m8Y+A4HPaOhUiAz2y/dzMyEzFz1UCHar/hL4Dymy40BW748w0YiS7Bvpby\njSQ+LfEQ6PZsY+kjnTHHw+GLBoo/uD8+27X0VUn/AdbIUMouiiesJctrdNTJ\nJ9QKZVx3+68q2LD+5wkg6H/f3ZRRHxzk21joVN9nW+2I799Un7YMVefjw6ZK\nGJWywBaS57o0LiyLJUw4+p9Vz1vDSpc3956wwqoR+SBzdZqwctbf0Jpwdt5j\n5OesX/E5a8jJsvtlCNmFXuUej5fKPNQZIDhrbdVu83Y08AyHjK5IP4I3qw+q\n//+i8f3WSWAQ5mbb/XlksN0pTBXZao34RxrAQDD2JTbgLsAADG+le7c40d58\nB1VY1FQFV7+T/xcIt05RMeVnhPrpBETNEUGXCYL5/NlrlphxbSKkSVptORsd\nvf5rGQwgwCq5KvG7oe4jcWTk990l8T6QGgLEO0EYKG45Z0kOLYbZQ4yI/yqc\nvSSamLDfMZCU0bnyatYb08RXxcPNdogaVChTlrjw9eSu4V40pWcv2qDUdSlm\n8tR07mhb0vp2Y5iChYXb1NsYB0j47uHpomBDIsh/RW0TolNaMf8YhaCOiDl+\nLmMVjKopq5ArgKf2rODMsEaq5afP4hveYsRtTKZaAjnicnkjqOlqvJXZcuAC\nP4xq\r\n=6hJt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDyae99ayUuSPb0l0/7bDu4K38WrUVrvNRnPugyD+EIAiAeugMj/FcOxzxF6qNx6144ca8V+99ZPsRfx51HDejxLg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181025110208_1540465488751_0.6403091911615097"},"_hasShrinkwrap":false},"1.0.0-goals-in-subscription.20181025144307":{"name":"@atomist/sdm","version":"1.0.0-goals-in-subscription.20181025144307","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"638e44ff18cbc829f77131474c12a574ff35d7a1","_id":"@atomist/sdm@1.0.0-goals-in-subscription.20181025144307","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PkV43jBu+oIs3uVqY6UpL6k3t11D0C6sCxy3zzPnXfPbjkI0esa+5c/Akx5HMhWFFElw7OOBnXuo9iPqoNf10A==","shasum":"8173ff7ddb0928c65381cc0047fed8abefb9e6d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goals-in-subscription.20181025144307.tgz","fileCount":671,"unpackedSize":1575838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0ddCCRA9TVsSAnZWagAAgecQAJAzvsNvVwOjLkxoMdem\nU5ilxBl4IIzP0LtTDVKJ1A5psFsq5w+kIK/zuJxUz9494j2TOY2vBn9ntqIj\nhwxY98nHc8xRKHVNsWazDgNTOR+N4yLtrbCscsFYSVWziPnhaOZNjOTN5uLy\nqFDEM2L5MJ2RpOFXwAEFbedZaQbI2tNJhASJ0t9wWNAt9NcpQWPuoR3S78Tm\nCOme6aylg7V76Y5WyLNZ48ZCwRQG/KT6D6viDBi19CdPOmP6BWwPijmSclRM\nFXgS2j0XWxuG0VM1VVlk095yqXBH2OvQBxBxCoihqccJ+TisCSYDyNDwTDDM\ndhBxYnMKDZQLhSkem1IjzdF1WibB041X1EIirRKEQEWbya2H2N7Ff6IKAVrP\n1RZxUzAwRhmuX45jrqcrEORaxIle13AaQOzPQhta0W7ItGLKVaCuz9A1PRZq\nbycWIqsvEYBoPwHQz7wT4XDrutqshIReM/WbSiOT3pFduXQI3ardwqD+XleE\nPHOOu4C+p8vtGr6Hj9pLTnTBKjMqVQZU6Hd4jJHmg2vWQVq/RuChuA1reqx4\nUtFBIpLNbaKUzVK2N6Ah379lelXPIzNjm6O/JnteG+9Z++Jv2h75Ifwg6Mx6\nSdtcbKz/AmK0ZGTh7T9Ryb5tAGOJtdPcSSBJNI+o/aeweO6Di5twnfj5Y+LD\n7/dy\r\n=Qakc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEtJdwEbYnSilm4Erc4yZ06BHJtwSkdRjwKMf7LxN9TmAiAcbaNlzkK+TU0FGGME2JUm1/uhHndHxxJpEHV5rOUvxA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goals-in-subscription.20181025144307_1540478785198_0.7338483631748152"},"_hasShrinkwrap":false},"1.0.0-goals-in-subscription.20181025152318":{"name":"@atomist/sdm","version":"1.0.0-goals-in-subscription.20181025152318","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e854554151ad2f829d1c615a495eadef4b908764","_id":"@atomist/sdm@1.0.0-goals-in-subscription.20181025152318","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Pmf1xaCq0tCWbZmd3Cxym1TSUX+TQycFCPqtY8eEj55asQE2jnyVrW5ahSEpMohAgTv8suD3o1xzynYP2D1Y6g==","shasum":"337d491014e2cc33d9f3ba8491dfbe3701e46d79","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goals-in-subscription.20181025152318.tgz","fileCount":671,"unpackedSize":1575838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0eCOCRA9TVsSAnZWagAAHFgP/3hFHuefj/91CUIfrktH\nQr2clyFZxXsojD53tW0LGqdqiqvmw0HERmmphr92ZHnf4UFrBgP0MWKz8zDl\nzZ+OLE40l1+TtHSV6Q680M2GgjVKPMVHg02kPCqH0OfTaDct/PsXkmUq1fwQ\nn2QWiMbeo/2j/kSdq8DO37IgMSuPy/XWi2uJlNTUsSjI3VhRR5P7jaiYh/qo\nEXAZNhwoG8GsTBjVFJNbALbFbUftf9MVhQ7kcp02PVcNuXVQgSFi1B0iXnbe\ncCDKkS65D1o4Grj0Kfa3ndzoWRzxfkq51bhYCc6EMESjUkQnKpjAVvZDCVNC\nj8Hgft5cTOL0BXh91GiJTxGVQJ7NC7y8rv5+OdtM937EC+mBkBSJiQT8H99z\nXX7c0vjxFFf3OjyTU0Q+4votqOHq+fEKy2wHpp7mBHHWczNTImNIhWm9v140\nMkYdDGsUh6Is+1ADCpA5yYyDkA4C4a4LMb5ZDW8tiyYHaSpYFy2i4GmvoBog\n6ywShmEGRsrhMNrthe4NpuOVhEAMcV3FlXJJzkUtlsXVHjcZdOyG9cLqDQjQ\noeeZyUc5lSyVr6DOdtOQ6XSIHQM/jQu81y/TQVXmI0CITQv3k76Wsrphuk9K\n6N1mCNaHWV7k91tJ+HXGghu42QLl9FnxW12/d4Sli4pdXPLxol1QfddvUIiI\njk63\r\n=94Bc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCeAGUq/rn3eaozznRX9CEld63ttO+9ZCZllmx/851c2wIhAI9SN1+KEhns3Hxg4E3fOHae9rOnDbGolrMox6+P735P"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goals-in-subscription.20181025152318_1540481165696_0.391539175826233"},"_hasShrinkwrap":false},"1.0.0-goals-in-subscription.20181025155243":{"name":"@atomist/sdm","version":"1.0.0-goals-in-subscription.20181025155243","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"14e39cadad57641ec66f8fd4db5aea2804588d52","_id":"@atomist/sdm@1.0.0-goals-in-subscription.20181025155243","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ok4g9ftn0u8DxvifSIc5gGc/1wW/hdYtuzcEjzL5iWp2Mm8vooyMWp3E6vcm1Et6lYQEQu+F97hYzucLEIm35Q==","shasum":"f269bb01489f58ed9591dfa1cf772726706ad0f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-goals-in-subscription.20181025155243.tgz","fileCount":671,"unpackedSize":1575838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0ed3CRA9TVsSAnZWagAAX0cP/3leffa1ggqaMNfw8D6w\nFcj1JWM4zYJShCF/zQTE0FylNmliGa7uuDK0WM1IxvlAKodVHb6WiB6wVc1e\nCgGdmlgt9Jr8ZQcuW8mln274Tsk7zOFala+3+RrehJ6ZHMClZ0Sd1wKlmeKm\niS38M8EsyuvH98zEIbequ/t+iwgV+OxKnN1V3DikRC2jnZ8DwtlJ8XfqB03r\n+37q1yBz8W7KyetCP/eqFb6WHK5QFmaBx1Tfwi+5u7/AnAb8lDqPvsSQE165\nFq4qqAQfmZBSdFTuAXDdIR8Lvo/RBKhoI+gL/IbyN31yMFHOd6KiOuiWzYy4\n8A8JOCSQ2sFKWt226Yklp3J0JFfk6LvOOOV5RgZ0E0q5acx2y6M0/juuIUD6\naL5UeR+e2Q2zInLUbpCGVHdBzm7jRJ8LGsZ5xf+DGDJ+JOx2NJzvBPxes5i6\nZtUqJmSEXLh5dgpzlaROvXoRKxtNx3k+Zmzw+iGaFVPSyKAUgx4QLgm/r7sL\ne6RnXJoK4nYrAEKi6PHGtXs0qQR9AnCKuiw6NQUhQHPLRF1GRMWm6b77NhYn\nyy0MX4ZujAzXRGVjVe9910mU5q3CW6hshvY/eAnzDCsn3sOOr1Q9gLuAD7Ir\ngdyU12CvVM3mKcxJs3QBLjtb6poZ7FWtsL6xqS1JIVq0HNlEL69HjBKPdAG1\n0piV\r\n=f74R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGHskPHYriXb00Ye1jgVYBTbM8hpKDJWvyIt+QkTeodAIhAKu0TrbPOikL6UCaRs0zDes4Vp55kx7YGyxfhRQ+xo9b"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-goals-in-subscription.20181025155243_1540482935222_0.6027507764526028"},"_hasShrinkwrap":false},"1.0.0-master.20181025155704":{"name":"@atomist/sdm","version":"1.0.0-master.20181025155704","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6069fece16e0ee1f2756b73c92bc6d73ffa40d02","_id":"@atomist/sdm@1.0.0-master.20181025155704","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TUcpCzqXbMwa+B308G7YpJ84TPLjTG4K8VwWHmMQd0tCs3LcsUurznpbDR1hZicBYcKWHwBVe0bHuYut5rPJXw==","shasum":"c12288a531e2ac26b91622018179cc886c585659","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181025155704.tgz","fileCount":671,"unpackedSize":1575940,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0eiNCRA9TVsSAnZWagAASX8P+wfq0E82dRtevxteDoJ7\nSAI8QjOHNfV8+xwG0NXWGW38AMuRsAoqQ4nkE7PGHyeVxTfQMeWRQ0tEfLRc\nKB1OfJCmfeeyRo/7zk1zoWuTLibZ1Jh3wn+FtNUwWn1b4YFFWyeqWmLP9wiZ\n6qAIsgNwZSqI9jLWG0HPA/+wHhPc+SWk4oSMXYg6aziPMmKxW4HDbhvGn9E1\ngebyELkmLOadQMIIGJULCTL12ICHLF61YfYJZWaYZRc05sgusUtUKO3Ugv8l\nOKvgOU4823BOMQXHadgGOX3bcs//ETPnAaoLCTNh5gnV3Y65SE3mdgt9VsCG\nlkB++nkN0RwLvAQk5eRy/ujz8+wjPzvBH2Zkbqe9Wbfs+wZM144KtDIBZdZN\n15I/TKbk2N2yVlkz2qblx1n7nMdIAMSAHrBaBlSwpxztM80QXcMTj/L/KrHz\nWqC9+rJzkoLwLGUexRLumjzQ7hmrw30oG6BpSxnmM9T0Ln8jnRxQZWOgKhW4\nB9wz64f44DTEfzvUomBTnWpfxqLt56aPVV1SE93cXvOm49FTlAb+1DODPAvs\ndg/3aGqUj1VTxIrzL4yuVqCGh9I/C3nky2UAPgwmxNV9IwfbGRxCbbr7lFCx\nP3TO1tlMSgzq6CT90qndggA3gAVWy2/jOV0N3y2LpIdjRKTX3j75s9ahv5ge\nDn0W\r\n=5jwc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkoRn1J7d0GDR22YaYlyWniRBlgv1zSlHXSCyu+zeecgIhANV6UsIkXZyKsQjLuBKHVaz4gKKZZoy9xC3/LskPMhSf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181025155704_1540483211295_0.5319981555133002"},"_hasShrinkwrap":false},"1.0.0-master.20181026095548":{"name":"@atomist/sdm","version":"1.0.0-master.20181026095548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"502d5e7bcc8e512d63950c067ac7d760177314f4","_id":"@atomist/sdm@1.0.0-master.20181026095548","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DbACq38pqxn56AoramDSwt5FutrZziUxNa1oS3lxQNd1rxQLaQKpskwTI5eY/VHyzUgh195FpjRQ4JSuc37boQ==","shasum":"0f6103b149e0b117c1c35214bdb6ecda87f5368a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181026095548.tgz","fileCount":671,"unpackedSize":1576386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0uVQCRA9TVsSAnZWagAAfPsQAI22HA+ksYK1m88vJFOA\nFy1345H5CJS36SuzMnl2F+WiSEiDrA0X6x9FEhykB7rBNENltkF6b95rjvZl\nO2fWvbcP9lUiEC4Xom6EV1mZQ4RHVGrkpqothwh9nHAA+4UfbjvrMzV6ojxu\nEfMbort8+QSOSIXCg8pYH03KooRtXglJa3OV7AMF/52/FnY/3QekLrxsdww9\nbRe9CLt2r5KUo1XonoHxqtwSiaZQWvd/Qq2ZXlR02rOmNE/aH7cAEcSUy0pk\n8Ojgo7KZi+lhh4hTrwHv6+rN6KL6TIu8Fr6jsSL7pfz57WfaAUkojX37etBy\n6Sc1oxg3m6H0lGzA0mUMABSek1sUTjpzbXfGJMvV8zJCOZMj5e737jia7HN5\nHNPhOGAYbKOzk645fMnByq3b29j7OAsJKvVdj+qO7HpHyNRwJASA0HBS7gbQ\ncCDVETJxaFdx15bwTLJlU/PYsGineEDu6brVFLlwR7BLYVNPiHBWCDKuPik+\n8FCeiAAHonu9uiPdd55vO1kMwiu3OGzRykyye9R839modhLHUnCPa8QOd/8I\nImFvqVw0ZQAYPeTkNtgpelEyh/rsEW/uo+sneqxZpeCrrieSl53lOF1kGAKq\nNnjpSBS18w+ZuR+yDzWSus18Cdj4e1jLPQRzgfKhrbAM/Q06tQNo0FwfMG9h\nF05z\r\n=U74A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD64VcheZu52iGhGYAZI1xvIkKRN4dBNSMMNkQCnLINmwIgWZ7Vks1dqWMkRGbMMRbfcf6AP/L8riMKryigtATDkwg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181026095548_1540547920040_0.5819636158601187"},"_hasShrinkwrap":false},"1.0.0-master.20181026131020":{"name":"@atomist/sdm","version":"1.0.0-master.20181026131020","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"502d5e7bcc8e512d63950c067ac7d760177314f4","_id":"@atomist/sdm@1.0.0-master.20181026131020","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xwRrMBONQtPcZsUKzIFqprtX/cc97xWv0GDxi1BKiwoZMwqQbFX9SZbUY/k8Npw1f5Xxa645ydAw7APO7wyrRQ==","shasum":"f2b04b385a0d2c7ecd6a40838692e7c2a38fc0b3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181026131020.tgz","fileCount":671,"unpackedSize":1576386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0xLhCRA9TVsSAnZWagAAruoQAKFSf+neamySonpQ7sbV\niiI61+MEq6fOpmrWpwoxRFbGIXYfPYb/ESWD2+puDmR/ZnfSe2zYtwLwwqYF\nr496zjh92ZeG4eJRPaL4U0XLVo2qKdhkcHXzpBURaCSnP4nVoVHcSi6LU/W9\nRKZ7JbR6Zu9ngLoaHECxEO+WE8JFdzTyLhiUK2CyC3UJcXTj3NFR9THYub2I\nfjCa0RBKAhsAp475RDyN2JLmTrnZTVm1qBOPLuc7qqzKlJlq5D+41vxie5T6\n5iT53qnW1wuA35m7lUy0GvzwjOAs83rJLeZiUAVchQ2KS54+G37BoIctjCaI\nnb4dFNruRbmhqkw874owyjrIAK/Y72y7U09mLT0J9RwUzcDwbNSFLDajFA13\nNaUly3Xyuktuv4/+NDwzdyFS07vOMznO8QJI0FNCrq+oPiWGs2tUlhtQ570I\nVny3Qn6rsMb6TdKfePP+hlJ85ZPLZjjSeUwqInalkKYg9gTN+/kvD3VWJrfa\nsmxtUsd4yVNj4m3fodkvJVfIwtGZCrc7gnEMpdaVNFyoJUMC2mdsvj9EcbII\nyacEzbZM40JkPjXJIdNv0aHEGmwRyk+lUdhLzo8sEpgajDhy1cYlFshFMfKM\n7ha2lJdsFDd3VST3ayYmQyRiKAjHz46uingRUKQUzqy6nExUF1WVXeoPFzP5\nLRP/\r\n=ktwo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDMDOOAhuWJmxuypCiEoaqupkLk4w3R54JoBNMlaqfhBAIgMUHCTssiVh80jD2szL4L+aJ1jo9MmJa20M3BOlorwGQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181026131020_1540559584669_0.802846709676923"},"_hasShrinkwrap":false},"1.0.0-master.20181026131748":{"name":"@atomist/sdm","version":"1.0.0-master.20181026131748","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ed24632cb92f169badb36704f69badabf6220c62","_id":"@atomist/sdm@1.0.0-master.20181026131748","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MRGvmE8feHzD+/lHf5blTwfSOCBDr26CDlNiR1SXNAn5SqrUqsqC7hdMapGcsnC/YXsET+FcoF0BqH/ORPJW+w==","shasum":"5c8a0bcd262ead280996af0290daf26ac2ce2137","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181026131748.tgz","fileCount":671,"unpackedSize":1576388,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb0xSmCRA9TVsSAnZWagAATGsP/iVkzs33xzlB85mPfFfH\nefE3KKtEPIFYrLZYDioggkUARWzPQiOfGJVfEDpnTXeiemIz3wEorZYh+rxb\nglf+QUOm68GT9JWzMB8jTlQLzGY8rfgZsQTqqu+3cv1U8JWFgkUKKIjYaMIq\nFlPbeprI18LVX1WGmkH+MagxeG3VOcLVwhTkf/fKZt/cJfa3SHD8oY4gJdFi\nKAktvRecr9n6nfNWPagJVFpZiGExdXnIRhHvJWuS8/3DLoRQUSZZeh3VeszM\nVVGpweiQ4Zn9P4lNMxmDzFxBdT9t7DlyVqExmifiMtIx/Qcsa13lXdKcTZYD\nU1TiJVgEbQx3Tw8dAbVF4Ekuc4Ow0bGJbrRY/uLpxJFz4XyHrErmCoOzs6oc\n1LS+nE8BYLmbmLOdRIWYrNh73NDWai/woIn7IcXxGTZGc8k7AlQEi9dwJ4fO\na8ACyjjg78N5Nxp0a1tHAIK+YnlUaASnjKmhV9wwLJ10OPd8GP4uQbqniSzv\nhOkFPri+Ji4uvk6o/1qavhIpodgbr4ZbuA9zTGXPgrF5juRaIwCXCpggx4BL\nuUE8FYhvGJFEynBue9MbzBq51nqVLcjhoUK/nZ3eBdojODKbXGGTpRqBAizf\nTkW5PXKVR44Obds22hxRqp9bDw+XvdVWZrRe9e2yF5+E19DPIlfU9xnv/a8C\nDwzl\r\n=bDm1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID3uW3ixqbn8SGz0vxnRctG1VaOqI0teAUrl/14gXuVlAiEA+Z8JxEK+gXJB2kPrQm0CFBJRs2+QKY728yFEGC9hpZE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181026131748_1540560037258_0.05670188027847023"},"_hasShrinkwrap":false},"1.0.0-predicated-goal.20181028063641":{"name":"@atomist/sdm","version":"1.0.0-predicated-goal.20181028063641","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2e191612637eac2a77829b812bf330e13749f560","_id":"@atomist/sdm@1.0.0-predicated-goal.20181028063641","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1CyZfxs+WXTTUFX81ebmKY3SpDOXFADVILWjrObU42NjD+SWb9hYVzHp9uXBuu6nTpPJIwgxLutLXAzv2QQZVg==","shasum":"45e3acebbea3836129db00f926b88a66b12fe260","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-predicated-goal.20181028063641.tgz","fileCount":671,"unpackedSize":1580270,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb1VmRCRA9TVsSAnZWagAAPg0P/jJsDKsYolw1pScWlHaC\n2qBJvjcupAL8IqAxNkqpM8+Zr1LDm5IBOD1G5RFSJFDlEvvZ/Pu9nG3q1cOb\nauMHvFobQw92VygIm/OjwD7gDamsNJfCr2BwODKjpaj3FL/EsOw/iCm79SXg\nn2mwPsNX+vH1FB+6RkQJQ0X+1dqSrOGcVNxmCyfiWo25B1o+97IzExYLdju3\nIvXNASyeMtTmd34l+i2GpHR0uh8YgKZL8AgFQXdUk+dIzRaFXEf9AobThS0b\n/W3SmYYmjQQkDiU9RV9CANrGw0lq6iXTef4dCpWBHSpQ0ltc76I5Wr7vz/YI\nYtrmcAw/BBc40DrGiiE5NsfRW9PUFJ4C4sT+YoHUFdIwVLi0C6djLgO/EGEE\nauxN2F8eho+S4GqX2tBelCnwxtn16ARIS4sLyOLFxmtPMUI7UTwJe4e6Xxo7\nOfihVaL29oTNR6ip+lXR9vL5meJkzguk1Z2m0xksWB3ECu19KeArlFr0hSug\nJdCSTf2X9PMO8tL9Tk16L4jyk8chSgSfka/FtzW6/q/a+QKcniKLqQ2grQ5e\n7ETDVSymDbgb4goSnE3tkrMaUIlL7giPCrfjSixtb5+4PnSdo05dbqwvF3Yk\n8mykQWLPzZ82pukNbCnAbD3/jb1uVUbQYMMgnL90lF1XbnBWNGoqRi8prLuN\nMoJw\r\n=4VXn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICxVO5PKCCqEtefWDVVdqbgYq25hfmE+XvRR9mz/nRjgAiEAyCXvzkGZpQdyAyua/AYo91OwL7eN3HJjhdUVk9QgJGI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-predicated-goal.20181028063641_1540708752109_0.08552779613355721"},"_hasShrinkwrap":false},"1.0.0-pushtest-structure-more.20181028211828":{"name":"@atomist/sdm","version":"1.0.0-pushtest-structure-more.20181028211828","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"965c38e8bcecfbed651a6dd4808c648fff566bad","_id":"@atomist/sdm@1.0.0-pushtest-structure-more.20181028211828","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jHGzbD8wEbWS9MSsS/bF1/Fmp9ZgkaKqkBS8UYQC3Ju8bJpjATibGq4v/w9QkPxiZfUMlkCD/ZJoLNzgEdr42Q==","shasum":"c960fb2a28be6753dcb91cc3405181ac8ade8986","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pushtest-structure-more.20181028211828.tgz","fileCount":662,"unpackedSize":1570701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb1ig6CRA9TVsSAnZWagAAxRQP/3pM4q3m9AenrYuOwqg1\nfHOTBzv5z1txotymXP3k9fK6gkQQM2zn1KEZOaM7Xh3dAVpYZ5i/7CDh3lA0\n35bRUVurLVSknykfqvhCl1ro7yikVBF0GbidXRFPv2tJ0O52kkfLbV48Vf0i\njRL3nEVJXfZLh74rW9jGfFl0MusBAZksGGhTpEO62Q3h8+cRZMjK99NBaL8+\n3WNod/t4vSkE3l0csyQ6xsDY6Xpamz/RTBWWXDzL1bfMfZP6dIouCaewI2Hs\nCCUn4gySkW2QK/KH/H9ZgglEkFnz5jbVqcOr8eJuToQxRwpqKgES9xy7eFwC\nDRho2T7KDZrjOEUxk3teDULfQ2UAAjrhHzV+T1LL9Gfo+hRX3b56p961zTbm\nGsLnhsJUn/L8pIfbqyaPM78UcloIJTvoFcJdH7HzS4gLwbCK9UQ5bMeIRn4R\n2IuawbKi82yv4sh+JIzjqIgnt2TJqX59NCh2pEE6L1+m68BYuKYaRyWpiLEX\nViXSHEkfeC+7wIsLr8Q30OhD/+sEDCCDi8YGZvISewAf8IAT/i+c5Uac2W/2\nL9M/48SzzzJHN4wM3rQ9gsm1AlvOIy1D4WEDF5gCLaCtLDE5xhMc/iD3UJk4\nvR7zC8eKoKBaZsldAgGWP0Lob1uKLJHlIr1byzGhlEIVgtpWOnJPWZ9tmX+u\ny1+W\r\n=MQ4M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGGZHdhGm3SZjYSiU2Ksq7mrb0zfuthG8QSHIOG1xVjmAiEAzcfGu7d0odi8g8mn2IGVfhDFqbBbIibDPo/VF1MIyoU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pushtest-structure-more.20181028211828_1540761657615_0.737336852119937"},"_hasShrinkwrap":false},"1.0.0-master.20181029095411":{"name":"@atomist/sdm","version":"1.0.0-master.20181029095411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a8e4220e971fb647ff6ea129414a333567a01cf4","_id":"@atomist/sdm@1.0.0-master.20181029095411","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F5nkBit78FiAmQtG5ttwpbOLxecr+CD6p1hj3ROwp9utcAB6YOZ8oNx4sFCySFlYp5jVEENkGhB7f/45LYaH4Q==","shasum":"8c99e8a14db5f281ee051ff69deed19e59059111","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181029095411.tgz","fileCount":671,"unpackedSize":1576691,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb1tlTCRA9TVsSAnZWagAAcH0P/ihR+wbgq1SGNgaiPdkX\no3r8f12j3zPS8uQvN91mhFJmgGWLTi1Cn+EUGKm272so5axIkTJ2STMUKk32\n0yTZUDSReTN4rJSP3l/Mx3BKbJ4BumFRqyoGIGdYuJY4z83/0V4FZLq+Z/UZ\nizRk1iXhFAnvCFiRiyVXdwdUdYTARhYKHj5Y+MYARqLkLig/h4w/D9MVzEFE\nyrmbwpl5Q1qec+PdMybtWTJw3BnC2R4R5mfB+V5c3TaHeYcNh4Aaxs5J90aD\nwM7hq7673ZIDONQeZiW1NA+WnmSLnQmW9FxouGqvyX5lT2kw6WDppRVK5vKR\nqwErwp/x4nx54PX433jLjq0C6tbBMxQWRzdgAgcOt/Zpn4Ey0XYikTl9DK8A\negd0VMVTjtUAh7+9sv4w5iVST2/EQTtpKfAB+ZvGpW3m0q1ehQGPrabFEsDh\n/DoLkeRU7qLMZWTQTbRlu5nAGQNWxYdsFkOOa69vuT75p9Y8ty4kvQsigzTS\nLj+L4CDukURtOwmq0uEWEITH2yzLLnh7Q5JikBjrtB1UTmEkI5S4sh6wl4xo\nVVORmR2hS5zBuvg9N+oFbwdR1p8SJSbNz2dhITyfsWRnEEyOloham02GqO1S\nOlg6VfFkgQQllREsmmjBbKbbURPxVnoET4x95FS/585hm9QSJ6mqHAPqldJe\n1YjE\r\n=jO5b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCO0jKDQNVEQ9mIKmuXAsltrPlITYBL19ZKJMqjp1rq8gIgK0LOHSgG0jwI2foTqrKAH7G2DnyfpkjysYQo8yTpqFs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181029095411_1540806994029_0.5419593609381723"},"_hasShrinkwrap":false},"1.0.0-master.20181029154613":{"name":"@atomist/sdm","version":"1.0.0-master.20181029154613","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"eb387db0c75903d53cbb475b5a61109231d86d1a","_id":"@atomist/sdm@1.0.0-master.20181029154613","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NTSlHLFtyDQcEe6wIkCAHWZCcZ9myTlydqBcD8eEhE5dQDfP6dVBbs4Afqfu61NCicsZdJiK4e7JySIFGtBeqA==","shasum":"539cd59b9927474046a1c4a4c06a9013334f1682","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181029154613.tgz","fileCount":671,"unpackedSize":1582584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb1yvtCRA9TVsSAnZWagAAUNUP/A5sfFESdxh5NGdieGTQ\nxFbDd/Jn6MG6zKcstjncoDZwKl4InD8nrfXUP0ucW7E91qGGaYhqWUQ/AJ+0\n07j8o1llV5e+KcOOihwodxPr0s8c52q2HYcNT1IiwuxQSRiCMGmK/HyW6rLF\npoZiyfdeaBXh4tiXWYzZoaU4r4CqtLP2rNfPnr30WJHlVH0a7tANnPsubSmB\n3HU9wuS/cb3yUUsK5fQt7pTFiq8AR5pJ3oeMJw7a7+OY7UYzC/fD2DE2b7S7\nwTvjXYV+d0UuKnGh5SMXIkve4VaBlmdYY+h2cLUjLTiYwdMard/GHtkVhpH1\nWL+gfjoHdgmiUl4tABoepfsW1a7vxepwk6Ro9sWBKzhFrArrHZjqGpIKjiT2\nYRcVTL1qnCfukKyyy4SQTAohgleu0KRziYm6gF8KDDrJuHDOlHINNJhCyIF3\nq9PH+MPwwgpugwYWBrxLHatAOTd6J2Sa+WbVs0gmXONH7MSbxvmM3lte5QZP\n/B0JABYQHjbWYat8J0fxk31R0sxaK3FyQl1+Vzna0dZ7M3Yl+6QeXOphHJsW\niq2kQI5Ws/8H/Qx+MCi04/gclz6GtcuQ9IUQnS0BPmnqOpzLAKfb6tWHDcZb\nbdUErPgcEoCgv8DmZPLHzaQrcrq90SYFplSYQfqUAY7UGUayR2x8DuoXAHSO\nWjmZ\r\n=qdVR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGnFLrhAHU1JgA12kMEEhfJPesdVSv8tNjA2waf7dDO/AiEA6Rm2ySBKk/h5OAK6Fgs6ofUASMk0MHJ5zH9rVBKb9XI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181029154613_1540828141087_0.29174268863748964"},"_hasShrinkwrap":false},"1.0.0-commit-images-96.20181029175450":{"name":"@atomist/sdm","version":"1.0.0-commit-images-96.20181029175450","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd9d52d10035e996dc233a70c703af77ecc69e15","_id":"@atomist/sdm@1.0.0-commit-images-96.20181029175450","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZSh7qnwzRGFrnUJnP7cF9lvUW18DFHK9MGytWvtYXYeGvLAHAbRAv6x7wS7rBJWhVHtDoZwsJonczzJXFX/aNw==","shasum":"27d1727eb90493c57453148ad80b2e26015b1946","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-commit-images-96.20181029175450.tgz","fileCount":671,"unpackedSize":1583316,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb10n9CRA9TVsSAnZWagAAtPkP/RGcj1N7su8vFmxixHDJ\nZ1lNpyOq+ll6m8Miqh5xE4J1QIChB/qH0J+Qh/BHW+OiRMISSpnFoeHcZkJJ\nGwOn8RJcUCayw1PTV48CeqRIlBxeycAv/2HYMHyqcsCSW8diVKPe7w6K7sEo\ndQB9o83kBFHNYGk4AD8AZDiFwMlJxMugGsdavk7FP5pim85naxpFO5IvCREU\n+kE0al6pcYGiVhHra9P1rwHrsYU7RCA5UJeGT/6tBjo43fKsHZBmlCwTpuIb\ngIQ7UxDk9DeVCASQ+erVhYwlRkqFN4FVcCTOs3ogFOkAmnNTi5yTM1bv0tIA\nGxlV/9ln874uTeJ1CtG88fhWQm1aFq5yXMBmVgOUowYUdNWIEgarZVxm2h1n\n5CiCK8Svq7222merj+dqIJ2KHArfyZl5ijGAo4LseEFj5bSevso8cYdTACnO\nwQSk51xL3d6Y0yLmyU0Wkcsunf7rKpukA+uT9sq3zv/yiTpbUYXedm1ny2nW\nvDVcBxXPL5ZzupD1QQ7QInv/bxnhWOzh5sysVWM2Xg6f9yV80HVM2m1Zwb+j\nQwOVrHdo/6z1mj7pPvrPb8dpa7Jw/237TweIOY5o8pwhavC6RYfD8OTpPW32\nA6H2qs6GfcDiP1suX5VXBRc0914O25wyb7xyLjwYLl9AYZQ7zkZcDxQA4vkh\ndhJP\r\n=xqbR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDBazI2e5DM5x3aI3QhOLrXMbTy8IIdME6j6Nye7jPeKAiBCUacGMbaTixGcoguZFhnB71bHV+hj6lvbuIAgwINHWg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-commit-images-96.20181029175450_1540835836586_0.46651028927161486"},"_hasShrinkwrap":false},"1.0.0-master.20181029215123":{"name":"@atomist/sdm","version":"1.0.0-master.20181029215123","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cda84a8befd8b22ac0342cf893481ff7eea8e30c","_id":"@atomist/sdm@1.0.0-master.20181029215123","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-f1qag1doDxb58FgcQvp6Vt0ayXvwVtwaJecPqcF7nT61cTxlZFDZVcDDInUokco5PQIj8Ox1aLhoyMkThRGjBg==","shasum":"87fff5e76cbe1af85aff2c26b1d065c400cf55bf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181029215123.tgz","fileCount":665,"unpackedSize":1588058,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb14GmCRA9TVsSAnZWagAAzasP/2RkpXE0823w2xYcTBXh\n2GLPKy8av/nn2AMPMH3WHNfo/xRAdQy5gCPuOQAmRozi9Krhr1h8HLTae3WT\nVVgHt8e8zujTtqgxoxVaMggyFWz3pE34ensBxLHFt81GUXKjIwxcAVRntpUF\nW42AQaAPZ/h3s8sXtCEq6AxuywIr274ragm3km04hhypFpObW3U8EfkFKurH\n9FTHMK/PW+ldlTxjF0Z+VK5dlebODvU79aQoAEIYYZdD6NYmhl3H0s2q4a2k\npjNONLO+XIVQboHJ2zM5n4m9WSJpDsxr9b3PVITvO/G2f/Fg5M6GOZp7qZRq\nNUmlE2sZidDvmsjkwUrg+e+RsVVNLAyWTyFk9pH0ZDoZDwlmBUzKY4k3Yual\njrVsUkPVtQ5I2xNe/0D8CLU41UUXzk+sEswiHvCXD2IdcJdI6mse3IjrQBPr\n85m8/ScZqRPh0d61rAgToOQXcKY4r8UPtRE117DIeYI+EHqu/EHSWXrIuYJw\nOBYKMkaH6awasi/iewIKSKBbj2HRef+tWA4iCsQPoFcU3q0/zOOjLUbBb7nG\nWqaCzWAriknVRDGkXynvAb9Be+btRtnaLwFI0+/KiNxjy/tOa9YU4xKspmKD\ndlLupGGZ1Xrnp/eWQtr9qQkZUWhMYw1se6AwgLsByzRVwQNh9Rz4FhOXOZZV\nqHT0\r\n=dwo5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoGSlj58Qml1s76QRIVD6VUOLUrZ7mfTiuakddpLfMMQIgEJe+jyvgbdbaicTRdJ34LpulESsh/18uhLVQ1pnAJeI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181029215123_1540850085786_0.3507173704064308"},"_hasShrinkwrap":false},"1.0.0-master.20181029215632":{"name":"@atomist/sdm","version":"1.0.0-master.20181029215632","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"619727b1025be8fb72febfc452ea4437a4b2995a","_id":"@atomist/sdm@1.0.0-master.20181029215632","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2IxfcJo4pEEv+wRJy8C84V1xRCiLvDmU+Aqafkd8KCl9eEDtttxDFT45g8sVA+6EeQ8//C3VSzdpdzsC3tgtbw==","shasum":"d5d3f4041ed0fdb7f4d691e32c9002f7365a54f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181029215632.tgz","fileCount":665,"unpackedSize":1588109,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb14K1CRA9TVsSAnZWagAA9jgP/3dVNVMZ9cEl+ZNDPD0q\neY4+ehMGDQxLqpLP06ED7e0r4By0HQWalLPn536V0tLoqdEwzuejqzEuE3g1\nAu1OWpOV7pGxWtyykHJZ3Cbthv+0idpAhnDqkmuJKjjuANytVYuvVsWLxBYb\nT0MirvkJSdqc4ANw36riZpMXWQkxg3e/F6w2pv6P7j53LPao8zN4N8pqKd+9\nuj/YuFzo0qyxJePbKj9iwQWiWlUjePBN2X5TYrFRk8EAbSXU1J31r9cApeqI\nqNRN21phtq7EXlxVeZFMT7K5OLCzRFwSU4j9//slu3BJvqIN/5FtysZRPVG4\nzp/xIDcH/x/5phR01aOg29A8fVcJ5fTpZIWVdvl8lXTcb04fmLf0s7wViq7n\nBikkgDohPnW5RvirkE0m/VeLvRC+h+9dAohHgSwSMdko317jQ3dOTJJXLC6i\nZgiNklMBTeusVhjOcFxFCHx/oVZs9BppKuRvMPRQj91z99U7lFr2CL8O1Wvl\nt4LK98nqVK4iytoy4P3/QxUHARW/at/GD3AXcul5/d7fxUmvcwoCbQg+BTky\nFVQq4Qf1NaMAAqGRdXiCotYHWWUTQZMLdq9HAOGTfbc1PhId3EVk621ODfql\nwq9KZrLAftUpU02mI9DIPcIei2xbyZnHsLmDIk+vVCrDn1lKRAVtYeUIRW8B\nGv8g\r\n=butO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHxkYE1irae+UaVrMlEKiIfEPUVeyGrEM3SYb41owXS0AiAFn574nq9AO8th6zliQogvAdad/7eeCrYQR4KrhnaHxg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181029215632_1540850356556_0.44248352863448837"},"_hasShrinkwrap":false},"1.0.0-master.20181029230642":{"name":"@atomist/sdm","version":"1.0.0-master.20181029230642","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dabc65470957621c61075d7e17780e73b087c15d","_id":"@atomist/sdm@1.0.0-master.20181029230642","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2b20GFDbwXmhUYTyWpIhiz35m1pSZd7urI11EOYIuR2efthm3EASFGRS0WA2qemEFkW1Dzyo7ySSImjX1VcI6A==","shasum":"5ed70ff1f183e211cc55fe949ab1e78dd5070420","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181029230642.tgz","fileCount":665,"unpackedSize":1589059,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb15MnCRA9TVsSAnZWagAAz1wP/iXJ/ejokfO7mdyExPs/\naN86hM/AqGz8Z/tiVgsUAp0prNzgX9VLZl/vgETmuzwc3DtitN03Vh+KoUG7\n0BTMKZ2pYfGveIHRmRgRh3QXNw/MhWDsgyHI+QWCIr0aP1+QVGRld9j4Q6wF\nx9yl4fvPh4mC9ZSiggFak7VSrkQSJvkkr1lZijRaWwAtHNzfFZG+PQpkquOU\nHbfud7kyFEO6YtCiRgJmBS60DwbYsdQDHgomvwlzX1NjHdlTAM1EtYGGuu4U\n/mklHYgkZb6A1w57dXydsiJmOOVfuqm3Pejozb2EqpUFCNZj9HxDleGAzbFA\nspTTILGfDH2957MTz/U/AY5VugYJMEIxA1ZJKnJVJQbvTwf46JWUD/ju6Eit\n5UA1/+zMwborI2pcmD+CLE5JBygvIE4GO5rBf/PjjIVqG4fQrbgxxhxZ5Mj9\nKuf6Phl4WES54yU3g7CkTH6BBiudDYisfNHqxQe0iU++1zWQU72q4v1DlvFX\nJlLBHSU6KXGZwaRxNiwpXY4l+xjQEjr1j00lHkjO3ubIrjdulBKty9R5LygQ\nIkIVi4cKkYcFs/N9Lc6HgJQnx09v9xCRg04q4qM7wggEsGNDeyrcgoLTBHoo\nMQHm2Mlnmzt8DXiGGJ8HkTjpnZcmJYSsw4FGgzQIW1dTkGh2fq31BytJXCHt\nBTqS\r\n=vd9W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3+WB2d3+aofXuZsKttdmV9aOgbzImCH4cbgIERw11JgIgUgrDUmKG/7+Z/U7D8xaxzphP2kL5Zi27fnKUc8ZLwds="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181029230642_1540854566475_0.21989435771248078"},"_hasShrinkwrap":false},"1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950":{"name":"@atomist/sdm","version":"1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8e02b56ce8f39ae5caf610264117efca5bbdc4c6","_id":"@atomist/sdm@1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-liOG69UndBhGi8M3ylT9PuH58JNl6yoZ/kp0KdUEeRvL8wV4Nf6tsRpPJ/8MVSoy8Yg3u6PZXn4+VIzt+o/Ftw==","shasum":"6cbe80cef1e63d52e6fb22713f830641a25689ae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950.tgz","fileCount":665,"unpackedSize":1589092,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2G3dCRA9TVsSAnZWagAA5m8P/1s0D/zJPn63JbjeImgr\neflC9iUSPog/7CDxR4V7xCrPNa+izZ2gD8gXG1STeFUTi2i5562RgT/kDFPW\n1EaDz2ijKyhatqw6P/P5a6Fd/mlryCUBl7Tw2vVbmzRaRUOvx+tELu5DZFEr\nyXIQRc3uf1Q6vk4pOlnvIGVdwLB+QjXaKoVzqqp3NAU4FKxmuvsQqv8+cvMa\n2VrPiO0306INlWjQcbc9RrG9+jJStNbGDueFU3I9cv1Ab3xzyf+++qIPgDMv\n3vIHXFW7M2U23w7ltrTCctP53plTizEJsXoIZS8lq5s8QgGDlYFdH5nsoas7\ni+ycM2D5Wd/7UREFj6Ig62ylSgApRqhyuxvdY8JUoSEwx1TFN1Ng5S1UiteR\nQzIY+q4GPptE2M/7BAUeP11H4htwZaIkSrWnPFB6nFGJJWBs91M8wDoKswRr\nAuKwnE8tfCJnGxHF/szz/J03MeOUP+AayOOiE8g1sArjC3+8uUE6oJsNpopD\n+ukohZoew2vQpKKSU77Xq7APK7gdwrgJAPBIh+gU9QT4aGqbabtDaM0BIecu\nt/QYnFHrs/px+mTg/K32jW/wEkRVHCkBsdnh/uHAnMQ/TBIPiUpdQaG7Kqh1\n6NnemWfQnqRSz1r8WmT6VToCvHNYnC/U1IaldkAP9iWDRtMRSHQMiSi7wXQb\nmtrZ\r\n=vO06\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGBctJal33CcpPCsahZ4u1PDT1JpsK1WNVm0mbLa4/gwIhAJKce3rvn9S7rde9v2K5FzSihPS6nIqSlxpTGDWSFGex"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950_1540910556255_0.9080369413727094"},"_hasShrinkwrap":false},"1.0.0-master.20181030144507":{"name":"@atomist/sdm","version":"1.0.0-master.20181030144507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"95db7b4895321c5f1973810faa216892c8eb4cbe","_id":"@atomist/sdm@1.0.0-master.20181030144507","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2oMU2L3td4eg9MGLv9iFr7lolwxjIB8HISYmvNo6JktZovpLVaBauVU1LvR7JkzS6ZZE7/DSvZscZhTINEk2LQ==","shasum":"8e3d1ff77c604ca45bbb496659fd6fd226845227","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181030144507.tgz","fileCount":665,"unpackedSize":1589064,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2G80CRA9TVsSAnZWagAAAdYP/iweiKWargSGrn6uyA7p\nmnUKAIcvGZOzJvNTQLV0tcEzL3e03FVF3z+f5BftOdpmjgoMlSmRxChfPwEN\nuVYz+UUQJrpUSzv1eK+ick2c7+J/r3LjWJlTbRJMYI2yv5u8tsTkvZv9ZaPI\n8Fup5sOESQleEDMaqSHGpv0/TobuH8lVeQJVqVlAd4udlCpfX35/X3LrckWv\npFh8CVVea9k9nxZKbTHNijEJcje6ZFU1+UW+JJhC5QLsvW/rMp7xuqt/i9SE\nNATP4rQO/0DUClp1G1vODVOcuSBmi2AmtbDZgG+qW8hpLwffQsCPETddsz1O\nMgU2EXPrEI8oJO1Kj1Z/a7Ij2kPqELyQE2rmnNkdrubyPlxONZ48V2Z5u87W\nEYh4trEHegy/6DD8BBslZnZw+4xDupxP4Dwny2i8WQmwsic6YAatmRebtuOm\nzuZLAw8Z/u4QlxHq8+POPLI/wZJQ327G+5/2k+sHv55SUwMLMcR4GQy3PnBu\ngG+QIynkxFWsjHmyCpKp+VGDc4dcBVG4Rqh5y53wULlWnQ+wbXA5JPJfTkDH\n17uFnu60YdStOM0DIuvujkZieXNeefucoVaKPFyeiaUTCNe+/Phn08me0Tv8\nKILzBlvvqvUsLqJxUgMYQtK7yji60gxyoPRrdZh37sO0k0kkx+PnGZfHmpzU\n+jUV\r\n=bwgY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF32V5YYSWqjDkf4yeB/ZtMBYFQr7zOuBZ1G2crIPi4IAiEAuRj0TRTsOvg3H5pt6o6GAQVowvUGeUsGOA6KOey8d4w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181030144507_1540910899560_0.027551171307257594"},"_hasShrinkwrap":false},"1.0.0-RC.2":{"name":"@atomist/sdm","version":"1.0.0-RC.2","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"gitHead":"95db7b4895321c5f1973810faa216892c8eb4cbe","_id":"@atomist/sdm@1.0.0-RC.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZKYouB/j8tNqngbjBWo+DweJPdqaJXtGfXYF0H5WwBQNbW3KbEop6YLDPBTCRlxapF4IsfOd/zRG+/yPbPYcDQ==","shasum":"40e101b569f93fe12eff87a2767aeab51f6d2dea","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-RC.2.tgz","fileCount":665,"unpackedSize":1589047,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2G98CRA9TVsSAnZWagAAAqgP/ioyv74xf/6kEpyYemeL\nh0LK2VB+l9gArrM2R4qXMjAFliyj+TJQwrPYFQSr3M4X+LSg6KLAMFqGEoJZ\nuT7Y8OaCQLXoAj+DmfLTQf2LI68KmszTYja9oXZU2XdDYpRLnigHytTwH7j9\nE3r28mzFakYzZYQdGpgYjB+5IlRlTzRjIwIdTSSdsGOsipyWzilXIdG7xkot\nrrJzs9Dl3LBI1ZQMUWdmYdt6n6o9puthvt+Mr3IAlcPEZXDwf8Bs5W3dQKc2\nrnwWJz3fcTqGUm6l8vN8O0/6nfYHEMPBqVpTnf3QrGrmNoCOJElRlFi5v7nV\nLxPDXLGpS7552Q4+pmprybmsjOQirKRf4jh4Ei7SLW5Q8EfspOd+jX52omzW\nnPDV790PKFEzaLaNaEk7MZD8YSFfkvkpMHi1QhJGSMvalhmRZY8y3HqQ/WEA\njXdiTyC3F/MQ8ye9Jl3tfz3T1UYYj1T2f9+OxQNYmM58BUTq7kma+CUIfwzJ\nLIx3a6/Qkd5wWK/CABxysF0ifqF56Rb7of5g6gfJIKMu22t3T2rFYPKXIuSO\nsbgyuCM3nc1gGe/XKjk2DEE5byCfus5X93hq+T8cAb1Y4RhyH9IAM2fWK5Q6\nfHJVOLimApY0t/TE5ydUlrgJmZ5QZ1gpVpRp+WJxH25g+pzFvnooqwCck7gj\nqNcZ\r\n=FB8s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB+jQ7QgZq75x4L7gbGwgxB9JNYR+ZP19eLgFYT6MzOtAiBLh4ibQKKPPS4VJ0TpAzuYmNtyVp68j1SvFlaGmCozNA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-RC.2_1540910971298_0.9601378843874111"},"_hasShrinkwrap":false},"1.0.0-master.20181101044435":{"name":"@atomist/sdm","version":"1.0.0-master.20181101044435","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"63ff1b9f90894963f83b514b45daba6b62909327","_id":"@atomist/sdm@1.0.0-master.20181101044435","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-CtE3ozLTEBbh5tb/Q3JNXNZX4YZyUGsbOdwQF7soraodkJXPWegBQomd01EoSmciNYTG42gk6QCdmHVDBbk6vg==","shasum":"101bf6906fc1e405e9f2c932356d3bc01ca50574","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181101044435.tgz","fileCount":665,"unpackedSize":1589872,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2oVjCRA9TVsSAnZWagAAsdQQAJCt/9NCvhTCRkndeLiA\nARLZpEa7BKhdO4ICIA/7pXS7lEbvxuccMq/f6M3fwYFWipZnMMhxmKXrEMbS\nKL6K6htWi+O3PIrIS6UgINIx+FGVdyIlBj9IXXgy333Lysau7hgdlQQtufEa\nHq0/3pNA+SExbttm/ojKKmaJk3mgY5tpVYvVwQ5h564JiSjSvxWbj62tZAuG\n5AziV+83TD/371gYEDtGpI29lO5zDsgW8dUod9N1kt4AbGg6BTlAdDkFaLUB\n9DCU2xKsGfGqjkjAIW7HpwlhscY6F4nwZHDtoEGIme+8QVVAcJ8B4d9YBsu8\nw2/xWbjBgR/maJ0+oAwfVg2trB31ZX9mm2LP+9EDehyErxOxzq+LRcsThFdV\nEa3D+WftN1sPTk20q45EXzHUuvN/cZv1eo/9a1A4yJCMs8YFAizpsaEvzBMa\n9rVDApZ2tP3NG7wuTkz88RhLH5forvbe4X3nR9YeS2FUFunLnIXAmISnamrm\nExZTEfsXBvjaCJ87n6BI3FqpXU+5gleOUC6QYUcPwHwwbHTdymEOThQxxHeB\nzLMhcqvOm3pI+OdTpy5cQScLTEkegZqr4AXARb5e+oEOky70AgTSEtqJ8rOy\nRST7i60ffGmStJiDxqCHpPt9n5C+x1AwEMGZ3kt7ab9jo9mUlTI+2uOP17vH\nwaRo\r\n=S0+O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUDcuC/j5eIW0Fehs9a78yBq34tOSqyf8ewXuLdSgF3AIhAK7CXHdRXzZKiC78ExpKavT3YeZ4yzanqJnCmYxjBO4W"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181101044435_1541047650402_0.559522621334305"},"_hasShrinkwrap":false},"1.0.0-well-known-goals.20181101070159":{"name":"@atomist/sdm","version":"1.0.0-well-known-goals.20181101070159","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"62f73287767435fc55c28b78c0e949785ed99e1c","_id":"@atomist/sdm@1.0.0-well-known-goals.20181101070159","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eeM8dPwK87MIpNvSQftjayXI38Qx7VzB02s7hamIXq26On3MWsARbDX10d/NS/pSXEJ8HI1iVFwsVskmelyi2w==","shasum":"1a5809c328271adba12b4e40d4b99d486c01dc3d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-well-known-goals.20181101070159.tgz","fileCount":668,"unpackedSize":1591686,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2qWFCRA9TVsSAnZWagAARB8P/0bQnr4Sl5eWB5wTcE1V\nWk/1TbNPrAwJtDfLN2bxh/BuFa1qtk/dMpAntXT4OtK4Sx/uArqzJDzBlAPo\ncH32YKUTskPwtmVR4p0hh7F5eTgXTDFrDKFeDY3+JxQ3Wvr+ZmBCQ5+2evg0\nxLehT9p7G+f9ACXS7Q06Z18fLF3n1qLlbPfB8hJWDbDtpA7x4AgBmtLZxl+7\nXNFEPhpWF4oEDbcwoTsAmt3Bhis641Q+ZXxF/vDOlnYW0W4LNYvikz3M/mQB\nCgF1dwp7IceRoLTcjcu1pp5q9dT8a7/o72XT//2DRTBIAfIY7ta0U9a/h242\nEN55jWmQNAUeinzPrLDr5aUObrejn1QXNYlHJ/cXu7Sn+C2zCVuNG50lWcf5\nr6zSa4DJzPK2uOVejephO6awqvErc2Bwp8gSFNDsdNUfuL9JmupDMu9Sggs6\nWsN+LBIL8UvDrAbHxZX/qlWHu22qQ0PvAxIMQltYPKOnS087Z7rg+UyAJKBm\nX7yzszSECh4Ge0KYkphLWOOwEyCtBXSlakQ/DqbusE1xQjUcgJ9FS8NeWSHm\nkWgdYe8xhBjT6k4fkzwxli/R3nhnnTeMWKmdZzWru0RIjZLaJbsYTmlN0MFC\n2YiHyKt6XGsSdTTJU078GBG+Wcf2ffb0IrjiJmxyVNxeLrr5bpfyY9+9wYI4\ncWCN\r\n=RE6w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEGoCTidcyWiBDiP9Xhbwgudlz67OHwzRzGA5zT2QwUoAiA8GaG/rcM+DfhtFZdcrb4YlGq7QNLA6M+Ybaxhdi97Ug=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-well-known-goals.20181101070159_1541055876830_0.3408905051840865"},"_hasShrinkwrap":false},"1.0.0-master.20181101103750":{"name":"@atomist/sdm","version":"1.0.0-master.20181101103750","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"73ac0f0f35b5f76abe5c2e40dda6c8d5e1a01b86","_id":"@atomist/sdm@1.0.0-master.20181101103750","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VV+9XMOHAE+Gyso+Twq3BzWOIoDdNxQ11ip//ei7QU+pLMLQlcMWsUMDBtt6ZaNRD5JWmbnbcQnrcV0Qmws0IA==","shasum":"cec87f3dc3f5df8764c417a1fbf016c78cde5431","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181101103750.tgz","fileCount":668,"unpackedSize":1591762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb2tgiCRA9TVsSAnZWagAAaFsQAJ/Qd1NZ3IxPbLhQWwnI\nSs4PDQZJwF0Erxu0zFBobe6gGmO4OFnkySaNwvop7GL+W0BdskIcy0a6ARbp\nMRpRbnu/7+UIasYRBBBvXKBlYLhLzVoijbbiZ10BFAkEvj6lEZ2tLjdI3lrb\nnrCjbWJ6+NfS6KeBqmRd/lsMkt2si9Bb9oZYDecI+T4TNObc+hR2kpMAH8Ec\nDewqtcfTCZxjcSnCAhbBh7xToT59rYBulTzuv6SadGJR4HSryd0dVmTquyMD\nPzKLA3BYdhqJqoYOME8lFgUduPukcFq7A0w2jeMTp0W05XUQLcaRZEWPBNEV\ne8E8PNEI4J7xMnubKNOTs2DhOLaeo2Eo0eGRmmCz6jeSvWiY7ZKVOGbb1Y+F\nQmssqbT7tzwj+jZloa1xQV2mr2wq8KWsXAWPacs673cEZmFq+ecamPTTzVvI\nrPfwA7BzOUEDli5yW8f2arHyL3qBDwOcHmzMoQNJ1es6KEZ1pFkTtZ5IV4Dd\nr94gkmLVj/z5+HN6P8KIUro0nmsVfX629EgwNIJLbUKqKuibl5D5RXIoTYSI\nCPWDuuA1pjjb0/0oyKW73UJ6Tibs1AZcEsbnp/ut9VtcaN/sqmybZiSyUwCv\n8ExsziUhxi/bcEN8AJqSY4veqTpyVGa8LI+Zv4oc2IVmzD55WNi/+fI0aZdb\n4X4b\r\n=2p1p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGgu+mUBY1HLsgUjKuuR3/Ntr65qaunBM0SidpSuC/j1AiB6ocwUQdHpdrnLrRXeJxDNg8n+2QuVbs4iL0MJ9KK3tQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181101103750_1541068834019_0.8197497535532754"},"_hasShrinkwrap":false},"1.0.0-selective.20181103034717":{"name":"@atomist/sdm","version":"1.0.0-selective.20181103034717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"196b39dd4b7ee82a7d93faefde58700fa684e16b","_id":"@atomist/sdm@1.0.0-selective.20181103034717","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OS5uEP9NJ0r2a3dUW7oUEEUkA49hmc9gRedzKlPSe7xdkta1nJcdBsJnLRt98UJMCuK5Tb7jIoMwnH5YRCHU8Q==","shasum":"fb3e7a8b00b43cd9477aaa048af6ea9ec13a7be5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-selective.20181103034717.tgz","fileCount":671,"unpackedSize":1593604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb3RrjCRA9TVsSAnZWagAAMj8P/RIPK+e5O7P6k1HGTSJM\nqe5PVrEKF4OnUDSdPu79uARknyzNbCUOPFvGJ6Oj6KjSdEFtkHnB2/zMHQm/\ntYDCzZU8Itt4HdJyTg7a4RF8pZtBt4gPCrXe35oue9Q1e9798n4PIg0ZPUYC\n7wpWAVsA7XixKb3Q7cCvGFXhLn1+qVEUH4JXyz/0lCYLZ7un+GfzRLl+gbmA\nHBKVxTT3MPh8668jKQ13fTVeCojM7XCL6R5x0TWlbOnmiS5dv5b3MZ8gTJzu\nwXcRx2U+ZyDGNIcQiWOnAW+W7z//wFWKZFnLRcu5kRW9PnHMAby0NDgvp5p0\nNMWDRbkuV+ZNpr0dVhc2Ay3UkOhxbJe9ofaMCtfAOaFTe/jC3TU4R4UlhJPp\nKkiZysES+Ma4wuTBH3Pei2TQRnEg8XM29GvOwlInrDJhCh2xMBhMDU3dqjCq\nrpv4YjIE6hp3+bNwTas1EjnIm2rYAo/dDk98GD6s9QyCnLK3gdQiQIsUWof2\nhYXMqMaoF50AiuQi9hJggHTDTP51fFmqOX1+dBGn9qsARU1KKHfZH2qqbbJ8\nka+Qfbu9T4SXbW5uPOMOI9q01TBbVm85lCSkUajCXvjQUL0uTfDVaOq9wCgD\nuLKCutj/4OZ7JomxrcTETx35dRmB+NSA+ry2lkYEVpkzgwHw2Rif61NvUBEO\npbNq\r\n=6KgG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEuvczYnWo8TxpiOM+2oRoWhlhYFPdC7MVaeOKvvJiWQAiEAt5tFveFc1fb+cKkLIHIgwGKy/ZT9+T0qSd+x8mM/XAo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-selective.20181103034717_1541216994737_0.5732874028542505"},"_hasShrinkwrap":false},"1.0.0-selective.20181103035613":{"name":"@atomist/sdm","version":"1.0.0-selective.20181103035613","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"76dadd7696b46c290ceb9b99a6321a47676af5e5","_id":"@atomist/sdm@1.0.0-selective.20181103035613","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DVTIEe3+mWL2WmJjlab4TEDdYdI04Sb8J1Wh5+RQpz9Fglws7eQkZBl03gleQyI1+jxVUHrIp30C1fXaaLvWzQ==","shasum":"38cb60ebd0dc05394432db9131edd6a5c6f257fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-selective.20181103035613.tgz","fileCount":671,"unpackedSize":1593269,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb3R0BCRA9TVsSAnZWagAADwQP+wVh31Xglt/JyCDDXpCD\nW61rzscqczrf1fvNtTSjOZNYqwrQrBIdCAVDR4lNrmUu91oDWnybPmuck7t5\n1QlUD49fP5EUdX6cB8frhqvYVTL9frKd2/dnSm5GtEeWR5oqBQHvz3B/RQmy\nYy04S/A3bvcUMrz4l1pyhEb6QzcXFgVH3QXSY7oi6VzpZ91M1ytr0H+QbD89\nszz+3N/EZN8G/vom3cfEQ0BgmIYKiQW+VcKZW/8TINDjIueILkI986lwGljZ\nxm1+birtlczBUD7aXPSmb3CWU4oXtevRpsr9v9qN4l2d1bTM4LHiw3fa3+dd\nwDrJgK6OTBGdAAZ4Y25HbyLBZvFHDk3suFg16iFSyjDKQL+RbAf17YNjWZIX\nHEQrPFE4cHuL45iBWnnwboTQOoXOuiy5nkHjFXrBm25XjvGXnXIuTayXILYI\nDL7PuR4c4Lwr5DmyJdnJgVUGGjXrrpg2XqLbr3fyDOviuPPcDQhf4jesg3mZ\nQWCtPXva4bOkT4V/a1SiaJkJGM8gdupA7Er+A37tuDaxnAa0MOoPuiF391LA\nFgzgeIq2gKPNumedSO9zpVuRHcGnO34tJfZLehj8/b1HiuBxKXMKLfu1cJoZ\nMWwHP5jRe2+fimTZdqx7BSGmgvgG8fWdnPz2Klc21cy+cgB06wlqSBsUb4iw\nDpug\r\n=p0i4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID26IRHtgfxJinE9YcBF2AJWKKEUWavEn9Bb4yygqbl9AiEAirJhpnFwsVD7yECYeKnYFQjHP9CNbSYyclNDLmehzs0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-selective.20181103035613_1541217536833_0.5196742595928361"},"_hasShrinkwrap":false},"1.0.0-master.20181105091644":{"name":"@atomist/sdm","version":"1.0.0-master.20181105091644","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0460a8dbb9610be1cc8332a2e47a1ba99e4d458e","_id":"@atomist/sdm@1.0.0-master.20181105091644","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8RJqIsrRww0EdB4YlKuYqR02CWWE6vwmyXvYRW37HLOBZBXL0EhJ6YN5wnrOTh1IoVRiWtCIB2yeWRjoIMvBmg==","shasum":"2d5568deddb9e17ad7e450bc4bf9d8585076ecc7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181105091644.tgz","fileCount":668,"unpackedSize":1591730,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4AsmCRA9TVsSAnZWagAAp1MQAI/zWmN05CGM8AD9b7RY\nXsxZBbtZkt0Qsn4hXaDsWRimLqUbvWDlTnPj3h6ccTVjYn7W0nq3ImM14b1K\nip4LLe0nPoqu0P2J/lqakDwtLwKIf8g7LXYheuUH7KiwSe23v5kZqm8wmmHy\n/g8nEcTMjvTLX7PfCICRdoctGqhIHI1hhjV8QpWEHsiqjYltSXUVNMn1YbvA\nFYe8inGYfmcAhHHcJIPwyFDoyJRlP5K6q9vgbZ06zzbLEPdYRj3xh7ePRVXJ\n4S/VLs/hoY2BLfV6OD1jXh5rXTtIHyDR2lPJudxUvn/z2+gKdwJsYCqSfd5Q\nPz9tnp4zhrl8n3/ESOQgms3AJMI2nZDr9vKPG5maHsgWHTmVPzg7Cg192n0m\nM4OGzIiLpMNqM85lGp7KtnY0L7HVeBrmCDbORxr0NFBcsRc+F/hLbCqbgOrh\npzqPWJBz7RZ0Uw0I0LPykjAtY/HKo2c2wnG6MTyg9RGLjRZCe0zV7BnHph76\nMtRphq75ylG9LDZ6IMhV8kTvUrYzqWeY5HIysfnUz8GBj4dKDtWiYexn7frF\nxVuAIASZlRTYRZoa8JPUqs/0mNEjhi2Zbm+vBDIRk+x2I54spJw+AS5QAKs9\nn0L/dmOohZ/XGzlLT24cPi4OQWjoeGgPTXhGYN8VGdfUoLcNGCvKl7dQ045L\nYNYP\r\n=pcka\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCAAW2AtrdUwZEaMajoL1iVCWjCsCzy6QRp9Dsnn0zQAQIgZkXoGbXidguih/PujON1LWkwCFLvG0gz1y++4je15W8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181105091644_1541409573659_0.7209303376133303"},"_hasShrinkwrap":false},"1.0.0-master.20181105092518":{"name":"@atomist/sdm","version":"1.0.0-master.20181105092518","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"94961dc3ef2350301beb121c731f9d8de9916a5d","_id":"@atomist/sdm@1.0.0-master.20181105092518","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-o2M7aqEJqfNkbVfSXq4eStrYtkMppt2nAFbB27IMvxXBdH+/ganBBO+Cydl00JqLoXVIUXQ22ZO/rkoRYuBLZw==","shasum":"92168842c0f905c9bcd0fe6ccf8c7c6aea149b8f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181105092518.tgz","fileCount":671,"unpackedSize":1593440,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4A0yCRA9TVsSAnZWagAAdL0P/2QNPiCT+RLhFGQcN2s+\ndbn9hASdyayDEP7AwxNFPy8L9ER36lspHz3XExK/C4XQQo/I01lIBIdonNnH\nIj9j/rC18yEipt0wZ4htyGldvGJ3RRXycqC0PRVn70zJ/7KeypBjEJ7M+Y/H\nv6aPPfIqZGRtw0SSIRtoz06mmdCffKVb7u2CMKB73bQqhtjFpvaKh9z/851s\n6XiGULJxZgnOWQJx9rFFQikMkLgUUUcORbzYr8tA8fswQlhCFFJM79AT0/mt\nAAZE+Pn7yXSqSo0kRf5H66jSiDJTtRowcr80BZ7k/PyXH6sHANdW9bCKLJ5x\n42pnMDOuHOeY+YjSOlm/6LGOq53SK9UWHW2sEjkIJPS83KID3VtuoQof66Wc\nAv6za0R8PhiMrokXIWKzMksxIdr6ZYDApqZww3ZsEGHuw8iQ2WgM3b6EfPLd\nh+udsmWoy3YfS8nSg2Qr49ZqyC78+zgp+yRj35oNlkbzk1hd+CULJjqbrVYf\nl3QFeyW88KDmpkPPJj7X8UNI86H80V+Unas1FqTBuTQgnpHoUv2wWkYs1dp3\nYtqzWZwgpuF03+UVmSjDNvDO/0FxDj2K2mI1uKWs820I3jK4mT2BG/73FcSB\nZAFBbuaW+5DCACzRygpLBTLn3xbe0sYRD/HeFUv+terRefMMI0Rhn3DXULuS\nRNgD\r\n=ifiJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD57yJ7q/zFXe1P33rooO7M0e5iAV7YgVBirvZbxr7P1AIhAP0nEecsT44fOV1EIwBsasu+jR92SgvZXi9+DdaogEdR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181105092518_1541410098241_0.5145759003483361"},"_hasShrinkwrap":false},"1.0.1-master.20181105103846":{"name":"@atomist/sdm","version":"1.0.1-master.20181105103846","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"77cb668732ea6c4f8a03a50775eda586b9a87887","_id":"@atomist/sdm@1.0.1-master.20181105103846","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-P9nfzcYYrvhddN6ionYlorCAcGxAuowpUj+CQGVXDRc3JwAXYCJHD4gUyUwW+fBX+yTrzzVIQ/qWZDNW6wt0YA==","shasum":"2a1df74e62d2ead4089421b10b5f360b579bc965","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.1-master.20181105103846.tgz","fileCount":671,"unpackedSize":1593519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4B6bCRA9TVsSAnZWagAAhbYP/2mcc9gLHxIWjKZFylN1\nofYTE0e+hLa1yiVmC8QXw07CI1tNWLRdEyr9Un/ezbICzMcvQnBPlu8wK7K3\nQvaS2D2CqeBBrKss9S+CEWBQ4MlHvDDTKOGFYm5q+Mqa03ySj1TejMr6eDCv\nPNhPVMIrT/W/OedpHvmq6YlYiOeWQNHMQHartFsryU9Rv6YJwDCw4CZdk6li\nTub4QSRG9Twut6CujTIytaO1bzWE8NUD10o7O3MBQwHyqFv5DfPbTiBS5hSt\n9ErVFfb8VOWWAS6a8ovVEXV6y/w4TP/U95cBFgPJmjmf9eDrFCDmMOtMu5Mz\niE5PyC5HKVhlaPoIx60o08dA1yN2Cvss3TjNMgWRFWMuCbskauokwfj2oqet\nP/PQFlHW2eyGJZpLAoo7FMvmzS4hTEdMex1gxAQboLILtF4iu9v6zEWla4s1\ndGVorV8r+wOy8LNCXjgW5xdGeRQ29LwzNobCCmdT1xMEedl3yXV0sXef73K9\nlvpLGVpNHUVfX4LkQ/eFQtXtJCQ7ZkHqeO8IzSg9ZIz4YTab984yW021LCxi\nJwS5pYiIRlNWa86FPFJkkWsFdU/s+X2GJLnCtCwyzApXkPbGHrVKP7VqThgD\nqRgWk3RNcscRL55m44JjALoewzakFWIFdK/Wg4FTVy/7DWJN2mHWaWl3wK5M\n1llp\r\n=OFSG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB8aUJabXd8YbSzBGFnwFQgFjfIkEPU9zinylSoKpjLDAiEAkfcZJtx7wQQp4qXcygVx5rJdfW2G1wFK4r6ThmfkCaI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.1-master.20181105103846_1541414554534_0.615186351124624"},"_hasShrinkwrap":false},"1.0.0-master.20181105105216":{"name":"@atomist/sdm","version":"1.0.0-master.20181105105216","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"704b5613a12d5f78909c94555e853829658754ba","_id":"@atomist/sdm@1.0.0-master.20181105105216","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-M0bf77i346KILBIqW3RZTcqc2Yl8nEuSni5k9yqrIcNQiHLLrMoYwsNw6MFwMSX6LvZ7BjOC7SwrbXowIPpbZg==","shasum":"64e5c71bacfbb5ae585c7837f6f377e7570c2b77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181105105216.tgz","fileCount":671,"unpackedSize":1593440,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4CGdCRA9TVsSAnZWagAAc3EQAIrNPN/BvL2uhoaK8Dm/\n5dk0tKyrk1GiqtobJ5KrnPT8166PEoLFwJxchM76KB0IYZG7Zo88/TsHUVW1\njePtQEkRuc4uOBHXjsHDSN6OTa0x3wr14bz2r8oJekuXE6BtuqgjTWjdZRkN\nTf6akmogDbX2Sy2o6Qg4n4zwDFkCt1YZ5JwWTjYv6e2xdJo43LjVsRggXKVn\nJ/h0z+WiF+mFmHsC+7alHrIWD09T0vdZoauANRh2/oF+E95YqVVpU+7D1UEK\n7Ybq7Lfm9peU4/0DQTliayg4ONDkk8WhSGJis49fBu1LGjrNFzRCjiwDbIlt\nTGApNadgrgffOU38elFpB77NQFVAXJ2bEd0pIrxY79164rZum77/Mw5gL2A0\ngAvgB5SqiA2lHLdGon2YxfuzumhvgHcQH+qxZI5UmEZj84g8yveNkxTKIzqR\n6gjtxBcHE2cMHusj4E3f7tlwQAbMYtEoqMNcjJjVEEYzZNH5smhTnr93tlZ3\ngvnyNmvTk/1m/K/owDclRT4eBWAKtgaa1VI5jllNVDd4I09aHYcWDduc+iED\neQQVqPUvWO4XERkLTZ+xuWahfDtncej9mJwfQ2X0sM9aKMWQ9uhYEwHaiyME\nA7BKV5Lu9kC/MEPc9O41OeeCngeFtATJRtDIsfb7KrLtWYFjSiiQPZhXxJze\ntT5x\r\n=5AXr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYf/DWJ1Jv0noGOJ3c5rVovpKCTR7Rxoda4rZ8buy4CAIgG+YgX2cd/v/tYTpWJ9EzrdYuJe8zA1QCmAOYryF852M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181105105216_1541415324542_0.09046751964555422"},"_hasShrinkwrap":false},"1.0.0-master.20181105144545":{"name":"@atomist/sdm","version":"1.0.0-master.20181105144545","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d069b782af5aa07fd23cf5a2daafee4d1c924912","_id":"@atomist/sdm@1.0.0-master.20181105144545","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2e+JJXD8R4x6LSkRiZqcxEJjgIxjat1GR0JWe0FHZzWR41MyxkvWty82NeqLrnKT7vOdnPzbhlLm0xVye6Shng==","shasum":"965d32f793d9e390203b381eb524c4e04dcd9075","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181105144545.tgz","fileCount":671,"unpackedSize":1593460,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4FhlCRA9TVsSAnZWagAA1TIP/RqI4ywyOINtOS5iRUw1\nmFc6t0gfv+VMrbJT9WdCjsJoth7XNG8yt6CCUM5o+sJ+4U++epxMKf55aA7U\n6X0PSDJRs4LZSqmgRBuN6TVe85wzcMMWG2xDUxiKsHBaXss80isVXRUbRBa2\no4VUvt4qNErrv7xOy7f2HdkgBy20c7D6Q3FEi0fwqQgbzX/PwmM4YBslUlZI\n5P2wKMSCDVUPuPG8Ddv2j25LkoNirXGU3sh/y1re3cnv9kTL/JBREuH3/cmt\nzOrp7F78dwBcArEuF/G6osM48XGxQ3ixKEzoJG0D3ABQdytFnSI33AUJPcUE\nABZVSq1glxCsWSRaG9h2ttpdqyJnn5ZTRvFK3A1BEdZ+kvwSEr4IdNi/7Kdh\nyZhbIgvYxmnCoKc1KKjXTwZ8OuKarTLxRLhz+OpTJHfQas8p++/DLg3LZznY\nUlHCIEO9lQ2Xk9mnDyWUTnFxvQLo99DFdxuf1lskfArW4nasKXuyC5zA35N3\nZNJmcN9LMBmAEITORx3fMsqRo70RQShllfYQ5b+Mx1FIwvg+CS/BXJx2QwKh\nrs0WdmDSOHV4mpVmgzdBDY37mQH7szTRjdrOTw0JgsezX8I+lEOkdcJRHoG5\nS3IPc65PxkMnuWieVZfmKxDJuMh7GMVC7nFZFmj10E4dAYskBlvuDZtGjOkm\np337\r\n=VG0Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCyplRvx3q1rrwqMZDTlHs6Wu9qnogR6fDhb/ofHFDvXwIgIYxP9Qton3zQF11cR3weEVvdx9LO1GzNHpDDcBU32JI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181105144545_1541429348864_0.06376912785288735"},"_hasShrinkwrap":false},"1.0.0-master.20181105150406":{"name":"@atomist/sdm","version":"1.0.0-master.20181105150406","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"21d8dd9a040012efdc18cf02621501e41ccdb2e0","_id":"@atomist/sdm@1.0.0-master.20181105150406","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m+XF5Tw9OFA2jn0wriJD1ktCMCsl3A6sCKgepAGPyOJg6meFYflD9pFKYWAVWnllw/Cs0EQOalgWdP8PEOvmTQ==","shasum":"241b5696e9747db6d8d6a8e3b2229ac36f64b376","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181105150406.tgz","fileCount":671,"unpackedSize":1593347,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4FyPCRA9TVsSAnZWagAAkPUP/3AqEecvpf/T0NE5lUq/\nJRXYDiNcxgYrxpNfrDo7eoITQNPN5ZY3Aoa31KtisFGQ9fQXbSjB493kETgh\nk8pBEr7dLSGCQAGw85bCKLK5MgEI+OFKGdY6qSQs0WMuYXaCQ+9/Ff287Cae\nfUkgCNS2NHwzmJujNCjEjo/NOAK81KaY4E0I6EItWTarFitjHfXRdZdK0lpc\nUcZ7GNIq9+e1Nj3DPu6nnGTO3omHCaPDPBMFsMIilncJ+i5pqMHZHcLuDuEH\n2GAR3jh8piIB2dBZUk20wdwXwP9d1JMy8nwnwcy2ouxNZ/hMe9tdRXlrCatz\n9sG2jrhy/AXfWxFU08OzypUR4hKTK229b4R81QTKiO7qgEYYxHH2LB6n5EJy\nkelc9sOJP3zlpHUCUH0jsPDQQF3hElINSiddlPK1p2tIIwTU1E3UufFKokyA\nCqWs4hYBnfZdL6exDWtfR/Gs1Orh0EpwE/s8WQYrKs1NY9sEIui4F6RkOTFO\ng0AlOAsy9Yt9wwcJCzmPuYfMIabAha29OqyMKj/IYaNa1u5dwfZxFyo71Sck\ncWiokWNLApBdzKVKs2XlfJS+TmoNsA8zAYpW/4esADe4AeK/9EQd7YZ27S/n\nt/p6mrgk+2zAmqqEGH7aFyDcLmwfrL5dSrAXsoS8gXko3tS0lbMHZmd9RT0A\n9CSv\r\n=hRDq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFV+a8EZ/Zz8gtUTvDd6OUQQ7eG5MX5MbI61q5Qql5iHAiBw5na0nV8gYvoZVo94MJbsoDH1SzZQBHPuXB94MD1ecw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181105150406_1541430415153_0.526031157912175"},"_hasShrinkwrap":false},"1.0.0-master.20181106103407":{"name":"@atomist/sdm","version":"1.0.0-master.20181106103407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"67fe7b1098a6abaeb6594a093c88703e525dbf86","_id":"@atomist/sdm@1.0.0-master.20181106103407","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OjNPO0YfiA827pVoZucw63/uuPYMJcoY0dFHrRfF+UySsmZpVQ0hBXeG1R3gHSs0ZsKG9zhK4OBDbFL/XIPFSw==","shasum":"3be8b6a3e2c2c7320546b62ae996f15a0551a2c0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181106103407.tgz","fileCount":671,"unpackedSize":1596771,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4W7XCRA9TVsSAnZWagAAokcQAJhYe3Xm4QJCYt8s6TRR\nM0RWAY/LAUKZemY0+USHhI0h/VfdUpdWeNtuYvZ8p5Gua64fOD3MvBgndj0t\nQ50EIm4+oeLBUplS2W+fBMRyFm3QEPmLu0vB/ULtsD69JvXsCaRH3FQaa/b6\ntaS8Fa3Q+Whe6K97FiBj+W5Ht7gCeFu52KXzP+7X/VJBkEHQlQj8ypNQ4LUp\njC7CPR6BZgP8KLSpSEf9UeLGabNV8uoPyQ6toWNtGpQjVUe0Lixg7/DmmWYY\nV5p5Svfp3WOMZkGhAlpjAeuiTLeyHJACgy5TMIYG8oXdwCEID5yj9XDpI/0M\nAei244WN7hp7J655CZX0ESaj735UNtXDmlKXZJWi2mPCaKfXwiLmKpfJ89+C\nZRpNgTPbPu4ZAr8+ysw9sZle7OZTYd8U42YyDt6mRnwKp3im6m42dimRwY0U\npvX8v8ZUpN/wyuws1pdAp6pILq7ZnqFgfjjwyTT3plVz2R+U4UDbJwMT7fay\nNK2CUghLn9Db/RniZ5nbhN5ojV2GFDMfmT1irMHRcQ/v/esn2qUqtEbiccyB\npWBLGdEz25srpK8e18z1t6BZAbY5tOreRRIiSVIoHwzn4YwAtwS9Obh5Vy8q\nutxUNnKkY9Nikc63z1HmttY+RmrwmfmZ+S0za+ytkNnHXlrknzu7yqmlcEhW\nEMr9\r\n=oukX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDjHHdGFnghY9ApSw5P7mSmX+RVX26lM9k08eyVNrwieAiBsqjuvLDz3o3Y2nMMsaEuB0VTOwhfGJp8MV487HfcRUA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181106103407_1541500630588_0.6670084365812239"},"_hasShrinkwrap":false},"1.0.0-master.20181106123058":{"name":"@atomist/sdm","version":"1.0.0-master.20181106123058","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"af079c73115d524d4c6634423b056284d6ee7461","_id":"@atomist/sdm@1.0.0-master.20181106123058","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dn02+W3tZzIWHpK4DMIJ99/bUqS0cCAN7YeyGUC7oLi7ZAakhXf3YQvjhEoto0MdBBkjDKWnEkAghYCTxvc7Pw==","shasum":"c7afca43ab33ce8689e59d021a6320095f0ec3cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181106123058.tgz","fileCount":671,"unpackedSize":1596767,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4YpICRA9TVsSAnZWagAAVMoP/RD0M6mQ1iy8BDtBkGLs\n1jPteGDiy/j4eJ37C5uK7m9JpHcaGkCe6AwlGv30HeI+l4NRHm9yRywDe0E6\naNvqJo9NoYsIuRzmGPHA4xUgD/X1Lp4PGNoHJB6d+bYWRNoTfcMH12LoAM9Q\nyKcsZ9z1IBWPKcf4ec7ncrRFGB9EeAKiLLXRsxOlAG/7kzcfWEmYPu/nIrl0\nKOa5k72iOd2aiRt1LtUuVQygbw0pHbbEZZCYuC8kvcsVfVDil0H493KCqrnh\n4cfVdlcBsOrc4I0AAmy0FF+dfrtSsNdCQNdfZCYnrkEwipIVZDMYxQdJrhst\nYET7F2luQoiiHrYSwwgbD52Y4X0xP5BnTRasGEtg5Uz4WJWMS8YMcaFBCMy1\nWVum0vrMYE3PydfRSD8IQR6QoQ5dOccRA/tCcVYzp2GWGdsUyioJ+10gxN8q\nKGLJGF8ealPsTxRFMTeUyfYRGqJezb5iGxOJzCZzju+hhazu1JxYYVTBmAi4\nJtGpPSMjxdZG0PM4MUjJ4Y42prfmW1AC0RAM+JhlCWvn0Tt4xfHxBgOoGKci\n6mNkGbB1vxMm74/480DRQBOwJQhR2vpoRiUXEKbPxfhqi2r2K4x2CUO7uPqM\n0U7ff6j/C8irA1Isl4nG7lrsivLINt82+BaUAfh9Z0CfdCIhYsxKJ/8rTGSt\nM7IX\r\n=Su0K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4HwMlmTd37J5ruaoAu5Lfm3o2NpPgGTzzYrAcSCR0BwIgCeYGYNFSKWqSCQDItXiPCRgQDn5Xz7qEhFLPHIqEfiw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181106123058_1541507655279_0.27017134171427615"},"_hasShrinkwrap":false},"1.0.0-master.20181106214407":{"name":"@atomist/sdm","version":"1.0.0-master.20181106214407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"63740d011932bf2482931b623408d9aa9dfa5054","_id":"@atomist/sdm@1.0.0-master.20181106214407","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pDSNwsImrSKUtcZmR/rEjLobWg/ZUwqLtVbBSmB3IdHges/XL2Kip3Hq+Ao5qn3iupiqlugebPxH6FuCqUxbWw==","shasum":"2879271dc471023e20f4eedaa0596a4ac88c84d0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181106214407.tgz","fileCount":671,"unpackedSize":1596809,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4gvlCRA9TVsSAnZWagAAp2YP/3DM4A0Qs+6MYHq3hiql\nkZU7w3JSbb45gh8OckEF/x/O4zjIzUc5+7mbJo4h2kV6XTBCAyfJSlhByTlN\nN6Gg9SjmVvA32b/1eEz6iPp4ElQppDgn+PmEIJHtgSipcmjCXRwfWrIvL5BQ\nFhLb+8quQX2Kba0veygCR8XRxK9nfvSFDBSwNGy1hC4dHyzyv7o5jaBCRAUY\nSIkPB3uFm/uyl+DO11rvlE0uFY4Zd3a/LREoCnC3ecujYQjj/HwRmiM3f8+U\nLK3ctWfXZOaG4AiRp30fEE8RlwRO8d3MRPAHjpHjyrm6H9L47kNoKxQsLzZi\nMIti6ui5BPraiIQK43iu9byaCRjkaHeev3+XKFPnTSF+dfZv2+9TrEEjGCII\n50eoJ5x2Jz9WR0eqbk0dH+FNcjYq/dQeb+GFt4/CuiG6bKxb5eJfQnJscaLH\nabLMWYXxfvxOCCWXriy0BShPmDwqWUgzjMki+4IwaN2bDzuT3UT3jy0QiKeM\nwlUjBbH6KU4ytsotkhfay9ghIuShIHKYuBYfelWbrGpt0SGOfCF1k4GddKwH\njY0JMxJXFFnIitv6sBhYffowFPAn51HOrpS+XrJfJEPA37SA6FyewDT5OCft\nhd06qJ18XJBrmeiIDFcemJg2XxAOEy1xNU9NPJjmJAQEYRsOosseffXxTK7H\njDmN\r\n=JGq2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZAqVKETdzD7zvH+bG33D99aCBTTmO4OR8xj7V+vo/1wIhAMPnoAaVJej1jqL+KX5p+yIL8oLXG/NhZQnPsNcslyJC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181106214407_1541540836986_0.05395390629193675"},"_hasShrinkwrap":false},"1.0.0-master.20181107002813":{"name":"@atomist/sdm","version":"1.0.0-master.20181107002813","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"877172dd9b6a1ec80ccfa61eb65288d05f421cd2","_id":"@atomist/sdm@1.0.0-master.20181107002813","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7dZjl3B6nZyRfAPl2e2S04snkQFfNwrom0MRm/vNQghgDfV9O01jNXMZZ0560Ni3KqEazGpddNib3TXl6NBZ9g==","shasum":"b79f09a56026db2f94bbc7222ce894ee8bdf1741","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107002813.tgz","fileCount":671,"unpackedSize":1597395,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4jJdCRA9TVsSAnZWagAAAswQAJG2wTLEdfQGGutTvQXx\nNzl4vnqjFB++6QKpguwn9PUQ1nVq4/kiOkvgtAb3mp6HmupWwxkwJ3B2Pcr+\nUEwrmR14BmRVlozU4Zl+ul7PtBgh6MHfctk9aEH+6IfL5nVngUp+1okVPvPv\nkxLb7cIr0G938Wy1742rfG2qOF6qaG3T4bI50drIC+m7R6maiS19kixVh7lG\nNHSn4lurYeOH838A4r4kYb5EHReNo5vUvgT9tmKeTToJf3FfXLjK9O4PxN8f\nFheNcXGNqNsKR1bvNSFad1zNDWbXMrdrbYISboOkmC7A86idRjc5pX8AQCo5\netX8yZN7r/DFpktjEU7w/xTj5Tuu0xiooPbFZxm0LexlCZ1MfSe7Vi/Vos4q\nGL7WXVQ4cLpTB1Fa3Edo4YaGVGRXzvmWIzz24cfTiwtBx8rKIiIJb568lRgD\njP4WAEFcy6joSOJl0bACzkQTWwaW7PrZ1OTaKiID1X/uRfhts6t9ZUSbw4mQ\nzDSWDr5A6Co/6044ImzCz8kUbAHU9fjLYhAL7IENZQdGNKKT0pZl2/PuePdp\nVJRwdMvCtrDKmdYA1pbbMW0fdrY/41WSxgFjw86L7ABg6qCLDcMy52cyWX5I\nYczaFVADY37RZ43Ur7t4YDo8DxXzXIQ+ldn0k5HD51kB6qI47KDcPK6kJ7QW\nXoSn\r\n=vpl5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCCKUMBu86tZiUwzwlQL702EJo77mlver5HMuaV7fsEMQIhAPd7rWEBzfAQv0qeqI0qjoyNBkKVMqdfOk+CWiVAK/Sh"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107002813_1541550684569_0.7780503571730641"},"_hasShrinkwrap":false},"1.0.0-master.20181107115558":{"name":"@atomist/sdm","version":"1.0.0-master.20181107115558","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"68342c402ccefd51e630b81999ce38c75715ee84","_id":"@atomist/sdm@1.0.0-master.20181107115558","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dIHG3t8HyLnDxsNd/5tWAig8asaGsjQBeR3clglaReKqCmXTjh9adeNAR0nOQZo5OQI1Q8YOd+PcLng4b7bPhg==","shasum":"2ab57c8324693b4c9bc7d0cd8bd9ef0a7893edca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107115558.tgz","fileCount":671,"unpackedSize":1597756,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4tN2CRA9TVsSAnZWagAAQj0P/j6XhbKGS9qL8Vcz4B8I\nLx+vynDyiag7KuFeaUeadnD7Qgtl0GISoWDwat3Dj8FtRYI5fcXR16TkfFEh\nx5Mmpy73SFfhh/58KjJD7dlf88Lbc/L4UlqCQBdIIVT7HQon5IOyxXpy1KiT\nl04qUGALIqMLvxi5/dMNslx/4bgQdjczPAjg9Iwr+iyITdetxDUHtYqi1b02\nYYr7fofHRYBulkTEcPXqb8kWFRTuhTwBKrSc6BhUaY4fTUZ2ZAb0Z3tFfC80\nrwGpHq4epYMGutkdOlycUQEoS5aTctfXw+xrc7JlZcoLKsKKBgZ4+dcm3aqT\nBM3ODDydbBRw+XMcTHyHRYM3v56pQ5LLanYYCwVSXWNglvWv78nO6p6CqfAf\nqapSgAaLZRwNrRsSiAoOX7UYeatFcqv8brc5GTTK784LoAdb4UcEZCx2VCJV\n1K1al4bnfqljIamWKQSZtff7FZ98Sx5dRYub80V3VZpNFOO2E46kWI+OSAcF\nO0iStMLpFFJVYDJIIzOc5RGuZluqYxE3rKqyOyDo5uchIAFaAE4cnv3TzkAn\noYa79LwrcwYmOdDebGTWa9EMeg5wRl/3eVLTmuOnoYNAlIIy86nQI7W3DWGb\nC/eixuJhzwMxNZv8bk+g+2AF8VpHlC/Dp2X5xZFVUPyv+BQsmFP9NIfGyFSP\nO/Cm\r\n=7Pzs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICi6Ie+FnAdFEpN12sGN70ARTn23SNUgDImpExduoxpnAiBciExR9zUi17AiJUPXFjiGWp8kj7n0SPX+rAV7pChb5g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107115558_1541591925848_0.21914464800296574"},"_hasShrinkwrap":false},"1.0.0-master.20181107122754":{"name":"@atomist/sdm","version":"1.0.0-master.20181107122754","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"12416cb86231a7d6c39897c06514bdb16b87eeac","_id":"@atomist/sdm@1.0.0-master.20181107122754","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uQa1i32Sm0zYyMtjXV+WQyZULXQ0g8/JR+mt5h6tw3UK/5w/KTrMaIv8rx/8wuI6I+rBYKYgATzu/cP2g+4qAg==","shasum":"57137e9e809300807f0df212f51ae8ba1e4d020f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107122754.tgz","fileCount":671,"unpackedSize":1598617,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4ts7CRA9TVsSAnZWagAANWcP/RdaXVEQGeFEktnAuc/9\nZfyJ69HGRwoI4oVSiflkiF8jiCK1UcES9yR+1ltqvVg5q4Zu/jSDHYZP4bN0\nwULL/W0zLaYwUXMHm/qHHFIs+wP3cqIMTB6h9zH7e2D3zfCKvZov4vMIpybg\n3NptBvyJdRljljXIxUMcooBKnLpy6E9vETl4o/XTEk4le6o7c58J/ANecWgF\nkhXyUIQxQetuc4vVks1aHzQiCZry7rblvFJtZ1tzZ7SNUHLdRxyIlpw+Xg9w\n3mDUA3hZHsKjca2VspYAKRc4Kx2aReExz3pSPYr98oCa/liiXvvkY1p/+eTe\nQnz48noGgc6lPl1Z4769AXCkBdOJLrHr6X5rZZhVlbRXn/KNrbsqEdNu7C0F\nH1gbLb18Rfadu4YXqhOX496VsJ/weelBrfCswsTi+HKDPTy8mA3ngOgQPnSl\nqq+IsSi5SBLO4dZealvf0Kv7TPVev0vfpMhEKwulDostv/+4AvsIyzyq6CHu\n6AEfx1U0VK+013J301aRhAtzowApEu1yOyy8e7PyIYnCPxuqNsX6zQ55VtZU\njJdgM7jOnDI7kRINGfuDeEVTwOAwdHfPycejDMcdQFF4a2bCUl+XR9a7Sb9N\nu9m2wAkGW0wgULTym0VgBx1hajBwVaKAoxGLlWuCB3tlQZBcTj63eO3QGecL\n/AJO\r\n=iyrA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZxZLc+5v904U18NzYrq673EejfJkJW1aj5QZ4yQCOIgIhAIShdsbr19VzI3+jNtqO9qrCAPzYxWtMl+lt90vrin6m"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107122754_1541593914195_0.2854325973249783"},"_hasShrinkwrap":false},"1.0.0-master.20181107124111":{"name":"@atomist/sdm","version":"1.0.0-master.20181107124111","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2710eb44ee50ee089721e0ed4b8705528571a2ac","_id":"@atomist/sdm@1.0.0-master.20181107124111","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Us7q5BKuOwS8TJgWavyoIqGK6jjYcFchhvtaQpQQIpS+dfvqQ3Pm4tEBRPo+08FrYhuopIV2s3OFgQy7sqpNyA==","shasum":"0551e546d9693ba89a716256614f94be70136ae4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107124111.tgz","fileCount":671,"unpackedSize":1598889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4t4NCRA9TVsSAnZWagAAAKEP/1Kh96pr+n4fzINbzHsp\nmKfzRFiRNsW2MJMODdBwOo9YOMlIWKLYld3E3e83tuUBPBwyrK9T3eR1N0Ka\nfOgktXNVkXhsxte4v5M13hbaiM2as3UCNwfQknF2QWgMreZnXAAO+RylWTF6\nfPMsmNv+QugGh/OPPZKeKi92awgjyicJx4f6U3SsMK9/I4nFwClf6AG+E7af\niSvIUnAQwvo28ABFKp5nyYFhb9kBEJd1Cxn06zUu7mxLUAfnvtlOTNa3xt0T\nUZkF5GDAvZhbZUHVJY2CTwEk75o2e6iraQPb2EsqHD/41fGWqfiIihTCraaA\nZ9RBnxUwoUzsgGb/X2Uzeu7Z++XWyXRQnAXNyHa5Req5DsLZIvvLW5KwtUEf\nMb8HYxZy466caHRJpf0DfpfhkZ+2WX2D4Ssu2BhMpcSnTM/X2iivjswfwZTe\nKvO+fGcu3n4N7Hh+CW4HMNPOp9WeQC1HU3VwoWUlNYzMaZqWFJ3TCzyoEdnR\nb7KAosJNQyS8JMtROOoP5EkGXT8V51cmsBW27i9Z/zqf4wUXMVjHRvr87koK\nUVWop4dSdcl+l2gTw9w3uxmqk2rbFnZWnQcUMbs7fkUes7gUynuuxiUml6/E\ntCGRPjGSYlsxRmrPs0eYBlTWT1H0gIhSWKlVPvhECyg/JSF+1n1yAPIQRaqK\nCds/\r\n=cmNt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGN45XMfhLRdZIr2HT3CWkGzA6qMO6rN/NvonXt6Y4mnAiA0c7tCU8Mmo196oo6kRMuNukwKwtzlgI2xnSjfbwwNUQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107124111_1541594636490_0.7467095605291911"},"_hasShrinkwrap":false},"1.0.0-master.20181107134130":{"name":"@atomist/sdm","version":"1.0.0-master.20181107134130","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dae48c33a814b10781c05faf9160c45727b1e7df","_id":"@atomist/sdm@1.0.0-master.20181107134130","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aQYNwlBdaBbvx3lft07n64NlXmAqI0fvlrXtsjh/wkaa4WGg7u8I5RMC0pz8Elgt/qNmvpqAPL357WRpmC4D0A==","shasum":"452001b7878ace52c1f8e9973de9ea945c873d86","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107134130.tgz","fileCount":671,"unpackedSize":1598889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4uxMCRA9TVsSAnZWagAANhIP/0svztGBUGdFBZw88XP8\nPibHS6sAchDj0WREDGWNahX3ztW7s3y96EomTTbk3Qc1pkmww6BnZvdZypC4\nY7c5SeA8YucNrXgl0PMRCbZuKiV9VJJvcFNndTG/c5tIbeSwCidndBxegRcE\nafMliQqPHYtcAYKUGUPqQKE7MwBS+hn0//VxGVDHpto4cLd0KnO65wUKTPnN\nA/8gjQhvF9Od91JgpYo0yljEyFJcgRimBOk/n8W9TMKo0qmj1MobRMIlPvxv\nnKx8LsFt0CvJX/o2I3POnr20j4GkHhhzBPNk6bpf20SFC9ZiurwfXJrwCSbh\nZANUff1NALSy2f4WlsOl5VJKDZJC6VRnIQDdfhlYqBv8Hbagoe0GhgzRGEss\nlFOguVX7BjnlrqypfA9qqv7XIDQhIEfgag8vlS5qMZJTmiXAKYuCRCLrJUYu\nekjMVm16K+BGKtRZzVkKinpXwdQHzaW7yhZSjos5Sw/1V6/KHwHYq1LlnTX2\niUE9Nuq/YMKmZW7IFUpT9/BW+nCBSgf81bcOJErew4kdv/vKPUlrtNNubzKu\noz0+8EiN5Ou0mOgpZ3OkixFu2tx32qz8gRHewkvihATPkeg62Me+XdYY7o9V\n+U2MXfTB8AXQhku+QM3TnP4xrNvZxK45zl3e6fo3pYOkdExyRx6IQsmWdDTw\nQMJe\r\n=Hq7M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDGRC+wVb6IvbFm0LWkBVmSumsRI9i1mjO+dmnyr+PyoAiEApaHl9Q17HY1QfAIVRhaT2qIT5KsNT5NFsAh4I1HTbjg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107134130_1541598283218_0.3631834758096497"},"_hasShrinkwrap":false},"1.0.0-master.20181107135131":{"name":"@atomist/sdm","version":"1.0.0-master.20181107135131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"941778e76bdd0329013c3b80ba42a034757888e5","_id":"@atomist/sdm@1.0.0-master.20181107135131","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gwqqyVv4DRgcHJx+q5V9kkOnozpwPO841f9sOeYJqfzM+07qZrwacUzoa38x+IBTpcHzMaEmB1jj3+JYukUtzA==","shasum":"e6e29fa7bbf38fe4b246aba0fecf519cf35b7ba1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107135131.tgz","fileCount":671,"unpackedSize":1599445,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4u6pCRA9TVsSAnZWagAAJ8MP/A5v4ncCLhvcUqFdm/YS\nrmRxuohz36dBgC5jF4mayitC5D1cn7cs0M7tDXHVxmtIWv/So73xgCkJSwvB\ns3vZh8kt+cgd3pfmYc160Y8awLkaF7tzNnCi2HNaDXmhFJykv2q4W7CcUXdm\nD3ZRspCZ6bo+51x4YArvGeCbGeFmAs/fUaBAppZ774yK8ChNAL+9wRc8z8Gd\nTSzoBr5e9LCvYLqpzYpvy9mysFotpNOZH8g3quPKB9bBWnwWHzqRM4CG6S7w\n+yeYwKjALG2B97qtm1DCYK8ZiQN2rBMidehOG/MYs90N3it1woIOISlYVLFp\nlXK0OkeRu2epcn5dgbiSVDFDxQr/Z0wc4iIazaB5/OqE0UMpgwAmIdSb840Y\n3HE/le8kFYDSpimGf01eaU+gEJZLpsz0BuUtXYyGA4x9ZTT4MwhdcdiDnnpu\nUrBwc0YeXec4WCN6rs0oYyrIf/J10Oy7k5E2iuWY1lNya61xHM3mcCF8iCxb\neAkMYwBTRPdKYcmfu5N1nQloWOnqwPGUsWZcpuoGJy0dBdw9ClNtL7kC/6zE\nqEAqEAX2LeE2Akhl65lDzi++3MRve5/U+YSjqrHQBKKB40NOF1c/n6N4PB0+\nlN8z2RKt5UVB28xDoP6X0cXsJknURY5YS40mXrM9mp5tZz5y0hB3/J7ZBjuQ\nQiSP\r\n=hMZv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDcDCuISEf5HDBSq8/sSEhkjVjg9goMd0vfGpVHLhonSwIgRoJJ4+8pOBwqcN0UxbI11LPubxwYDbCv1vh8Q/KY13w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107135131_1541598888404_0.3370458908649818"},"_hasShrinkwrap":false},"1.0.0-master.20181107202213":{"name":"@atomist/sdm","version":"1.0.0-master.20181107202213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fb1a00f3a4158daead645ef5fadffe6d6d84f816","_id":"@atomist/sdm@1.0.0-master.20181107202213","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sC4tO4gOEVavxq6dyC7yKBUqG0yA1Am/ykE/SHjZTfNKsBa4DrvZMlflQnIFBN1tBafIoL8bBVlN45r6YmxaVQ==","shasum":"e65dc3af02c2622acf8d101002e19b7f1273e606","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181107202213.tgz","fileCount":671,"unpackedSize":1599446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb40pNCRA9TVsSAnZWagAAT+gP/jHFmtiOLmpIr4u3MHBM\neJeGf4X+1kh9bENAvireKKLcTO7DLr+mDPO34MsvaqgmvBN9/y3UGVCMz9Ol\nq099tr0PeDx9LUOYoXHPRPIKeyojJRW10I3UCMW1TVPMwQzvviN6bVclOz0A\nR12szS8VMzYNPZD/IqYz1/c8UQBdwQTGI2YAnSx49i5bjxoBl8bKmTVsS89B\nGQIFtekJaYbADrpB2OH6fYJUyczvgY59bsxrldqbFsMsrUFHfRF+eb2NCIUq\nYgUsi8CMAACRmmTdce6jBmWoTL1vhdoXIoWU8HRDlWwNxYuulTqRZk6YNMFw\nnwtAZStNlldqe7GccFmfIYCWAhmV6HfYYosgs14m0JxYLD2rabhDN8STVkao\nifx39pnADi3dJ8Ky2vrtnPrSliHM9mqlDT3LjBfF7NO5RB7Lqx2Mlqa6WD3U\nCM9iSTO2ewcDB5sRsZMIuWE6XhfsXkZz7SDtvXmATRHGxtoHQS+q4L4um7fJ\ngrvRP2CkiWyi5ehKgRGx22PkwrpiPP/HedUIe6n9NoRIHX7w/q3YITlYJ73/\ncCH97SXR5/rJL6p2/qE8I/IPxFBXOy3WK1pJ1MypJ4Tq9U2C+ivuLHif38T2\n1/o1DA5cCxytBwqUBOsUzPjYDJkR9oCnR/kRQ7zYISVZGcqID5p0mEBq1OJt\nfYh1\r\n=7Wj3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAUejDNaG+yfTzERZ5if/vYL9jMKntkzmSY+sgsIDrT3AiBlr193PwsgEaBaFfFm4PHGc9bfLOGD5mqG8gerlyJlLw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181107202213_1541622348506_0.790129204351711"},"_hasShrinkwrap":false},"1.0.0-promise-any.20181107234952":{"name":"@atomist/sdm","version":"1.0.0-promise-any.20181107234952","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"227e1f22c6cd8940c0c56ae17e519d402d4a7f85","_id":"@atomist/sdm@1.0.0-promise-any.20181107234952","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bSLQBfARlPnFZ6Q9FxIoczpXNKID5T6P/8Up85BiONc7D/6cKSn3ZCph/H0mk/A441om1KO2s9IJ/RQR/lTJpw==","shasum":"a2d467ec56e11bc89516347ef4d4e7fe9f4e1881","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-promise-any.20181107234952.tgz","fileCount":671,"unpackedSize":1599826,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb43rJCRA9TVsSAnZWagAAjOEP/1wka66hf7Cgj0mW19mD\nxfaJJXd1h0+eJwKpnO4fZzCK1euRP1OyhtxPqDZtCsoU0EYVT41dwj8DKTZj\n6JikFPMpwP1296S8fzOhB+cAkXtx4uhbJRjgiNzSCnsxqF6oujkq7152FByZ\n9Il28GQrwXjswmWlCyZfNRV2ol/J4h+drVmzHymraODf/BtOzqFd+BINp4Jx\nBwB83XVx18YX0SbOOgQgnw4WuYYWUy2iqj73Xs4AQV5AV8/xCJlDwxl4lMqv\n5E75/H9y0d6lJ9LSv2GvJUGlHbYYXF9uto/LyN/A1Nn/6SvTNqXoVmYEftZ1\nhhkkzeLH8y9NTQCG6aGqxndT6Ql4gh7ych4JZpR6tD+7wjUjZ5PCAW8Hac4D\nbiJ5NVL22yOYxABoBNwlT3OdH7nx02NP+MVHo4ZnLrecwiZC55EoOWZEkI/i\ncG/Bss1/UMQbS6hSg5BBgMIU7bfPyhrX7IEbFRmtX9MgieUpxzbb8JQAL9yA\naL2ow6m88rql30I7iaQqOIauUGR0ZuSbkB3XG9CSbylo9iAG9RTASS6h2eZQ\nAOyxrQPfm9QChwgZfANexk0S9elbkWWwDnejLiN2uXce1oGQVe6fRoaWV+Ee\nUHu0DfHA4Qpa6JT31LZrRYFUlb1Pas/MLY/z3ayN9QNqlzXvjzonQ/M8oNS/\ny0pY\r\n=Hm9w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCaXJf6nyTsJJNwm/Adnfz5Wq25drMIUvLcP+U0PBcFaQIhAORFkmSUl53a4wAl8xLPSgt+hgdR0Ju8cclNubu+oiiO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-promise-any.20181107234952_1541634760950_0.41531752791274545"},"_hasShrinkwrap":false},"1.0.0-promise-any.20181108080819":{"name":"@atomist/sdm","version":"1.0.0-promise-any.20181108080819","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"80cb4571439c1e660c7c999eb91f97995f49616a","_id":"@atomist/sdm@1.0.0-promise-any.20181108080819","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dMpSqMxzv/qpakZQgQOMTp788IbuBImBsLd6sVGzzTX1mqkahu6ASaknpxGx7XY3O5Ija1qDdkXvznfxcQIgyA==","shasum":"d00c9cd399a03ca4aca4bb0aaa3246c87f731cf9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-promise-any.20181108080819.tgz","fileCount":671,"unpackedSize":1599755,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4++aCRA9TVsSAnZWagAA3G4QAIIFwX4+iSSeDNaggReX\nhPnCOFuRz4aSIcb0+j7kDGC1nf+jSQqN6wc4ZzVmXNdGk4z/4PZwKnLuegQ7\nEx4+PtagnWMXkwJ2g9CXpUD00qV9oBT/EmdovVtXFRBFdM468lVo8gcfhFW2\nRwlg0Ebd3I9JrXKw82EFAXGoH5h+OQxXRLsYAPJPT2qUGwXF0AqaH3kGUyFf\nCrvst2rKXaadUcQOFrubveJZWC77dsrUGBegka2ZMcvZcoBZWhl1E3eae9bW\nGxR/PuNmRdRfJXjGa/RYctZmnf/la8xoqrfVQrZ7D3sjfIWU0ClzrjnE8q4M\n66tZX1JWg5AwRMXnM1egkIYPUYmkEwpgSWQF3J+KZ6OECSIPhUA5LG/ElM+l\nOY/meM5+F00HLTQjTzi2Oypw9EbepBbvDkx6DFk4AoJ51x1yrvvGh0TM2MWM\n6YpD3EW5Npo6tTOV6+3W23uXK1gaOlt3qxFow0zaRwX/l9oS8yLYlu1uZDoO\nlsDiK2jrnaK8PFV7wG17p7UGm9/RYvo4Aa5U0foimcdDW+xw3DO0x5t35f36\nm59dLRTs4BL9KinvmkCEikVTQU9FICFms4arjRDMjUJU7aLX+1L9OaUmYJhq\nQ/dXYy7yBDGA1xyXmvSz6RR5hiQw/6AQJbxU3P54gU7RkhFpbwKe6qu1n+VY\npaRn\r\n=ZG9b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCyknQQm27OfJhKCDoBgd75iG3CEytGMd0IX3dHTeme8QIhAKKdoYIxSCxYJcUZB9SZHFZ2CXkX4M0VD+8+7WHk+aTq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-promise-any.20181108080819_1541664665859_0.006826165799694639"},"_hasShrinkwrap":false},"1.0.0-promise-any.20181108081251":{"name":"@atomist/sdm","version":"1.0.0-promise-any.20181108081251","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9311241e03525c408975f82ee7bd09dddc0949dc","_id":"@atomist/sdm@1.0.0-promise-any.20181108081251","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-doOMh48g7H2HGNZR8qtcBjOrwyetteiR8M2OdMjJGqlfSIArM2tLhBbflyl0w5Idt6W8+yTPnYDzBcAa7GlFuA==","shasum":"d0d3e9f60396d8c25c91126ffcded34b2fd1fd18","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-promise-any.20181108081251.tgz","fileCount":671,"unpackedSize":1599878,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4/CoCRA9TVsSAnZWagAAUY8P/0DV9GR3X3/nkzeKwXHd\n/l+Llkj/guBnyG3Qv3kK3q4RYJx8Bp5LnhHpuTqaGJrnvTZ76BPZ/xuwTisW\nhkc2+CbGU9gbJ2FEtPTnEr5RqoUpjs6+GKfjabf8H4m9q53dgQ2Ti7yHJIVf\ntd5+ghS+pDrffqV9cj5VEHy8Ogz+wMXvPDAcFwCX3gFj1p+e7hAAyvj0iM57\nm48pXiXNklVovsDsI4n524KPn6LJ/ETBoRxKtkdcRUTvj79dPGzCUWd44CFq\n5nGBjEzKO7w8RdRziie7IAQ8H/3ZiVku/JuRkCEUxyIlmheKjX+Nb3Q9gsC8\najoWKwEHrTV+zOp11vbiTbUnlL1Uzo6E1ZDGRXqfz1SfUrN7kuyau+/plzLx\nxGGzsgamgQSiwsLtbZko0ets9On9MKQfYnRLJCi5dj+SGeEzwOXmx6UeVQ91\natQ1SSLYQREg5JUJ5GJI7jNZllQfI6Fpat0cORDbgj0iDb4lMYx5Eq4p1anI\nZrrl6MgSd7cjT/dViYBH5ANTAcBGwW5wR8HtdApDGU2HsSMffKCrx0STigGx\nUexCI3oa4I6uhhqdcs9vhxa8hDrrIcQ3ImxpCNhkhD9zak/ctNiCnYYW5+OU\nPMorel15v6LFkenTrmqxp3mLgHWE6LBcValogSjGcrab9ep0Uzh9hlMamhLS\nM/qQ\r\n=BvmT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCSCszxO7P3G/tsOmSMT8i1pwc6aj011gkeVSlIrygGoAIgOJl3O6bgp7XoW7bGS3HI+/smin0YzI8EYdPpVS3KyTQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-promise-any.20181108081251_1541664935622_0.7389652517797423"},"_hasShrinkwrap":false},"1.0.0-master.20181108084636":{"name":"@atomist/sdm","version":"1.0.0-master.20181108084636","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"96765e7e70afe834621a5ae68f83ce192f1c3610","_id":"@atomist/sdm@1.0.0-master.20181108084636","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aAydnNcSLwjdt3iw6eq/tPg+P0Egfhai8WwN9V+w8bdRx448gPEUqk0mcGEwCQ1e8MH5kcRuXTzwdBvXpo+1IQ==","shasum":"95fdc57c7661780377f06c9a91c3de92d6e4cf78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181108084636.tgz","fileCount":671,"unpackedSize":1600374,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb4/iaCRA9TVsSAnZWagAA2AgP+QH4tBPWyAPS8FHtn1n6\nWHyxgqnIeKURu9KBltRFv1IpZRV4aR6iM7rg0whGk0Sz742O9cTBsHvZZtsF\nznC9sEhc4Lz0gFUqIBo6yi619XfNlxaISOOQjLDqEUfJj982t3iTgd/xygbo\nXpU7gW9jTEo5tLLAdUlXNVT43aFabaNlWwKRf2JbllT9J/LPi2/Q9yPnSAhu\nfbuVCqc2cDyIgF/81QtTVjMdR3ZSMm0CpsD57Sy0EQ7TDOyxBppbGzJIYkVU\nTNX9U2rA3rUEoShKu164QQvsBuYcct9uiorC2AzWidVknRbWL5p5jYTZz4SE\npyaISBvgFnwk0YjrZshpZrN3ypNJxCw17VW9lumYKBpMCTjEEjP7rar/Zery\nH8zqBu5iGFamwyzk5COiydUXRImCCGV7WJWASdL8Nz2/pcJzQ3vCgA5dn5rD\nPjYiIUAahWezOqgf4TmLLy7nDaWCaZuKBr4A3fwHmH3k1iEtAg7mMkr1U7hC\nedqooAp33LbgcixYjwFa6oZGmCf2/GMSzLovtPXUFtlL0CFaevjYDNezFVRt\njDMCmEbm+C6CP5Ccla6JSeYG6rWbjYsFwTxuDQ2MPXpf66uU/Vd/xJYPy7WC\nAmZccj7g9qpsSbL9KH5ybXsVFSG4bIHzgQXkn4VVUdejh9bIZrD9EAYgwgas\nZTgX\r\n=H1Mw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC7eAA0DcphLt+FxSKl78d+bSsf1x+/xYNDdXmcwsrHNwIgSHGJPH8kMPYqVdSeRqlgfX/BKQ+NkXd0yCkVmJCT+vI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181108084636_1541666969592_0.9399536473526133"},"_hasShrinkwrap":false},"1.0.0-master.20181108102031":{"name":"@atomist/sdm","version":"1.0.0-master.20181108102031","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"69c10277fa34f25e347c0f4c4100638ab4aa23bb","_id":"@atomist/sdm@1.0.0-master.20181108102031","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IVgwnUPW12WqDBp3o+VMt5YpITmAomMUTrkJ393TuOeA0kxqUFzgX+1SmWlxnsaNJJbGjc8MVHdmEpYBQ9U7TA==","shasum":"b906aaadca6640fe6a81e991fdb6c955e032ff67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181108102031.tgz","fileCount":671,"unpackedSize":1601428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5A7ZCRA9TVsSAnZWagAA2kkP/1pm3skBvcCBQGas7P2M\nnFeyUtr1jXGLYO0i+2ic3Toa4gKqWNda/oULOSSEjnCbgD4LOrewY8UvX//D\nbEnuq23419xAoZUwYHNM+dfW+ky111DT1l4uIeQythKFgwVFyikxxjF3MKvX\nJqeEMSthRWZMLJtWycjImEuWRfQTpmw6HGleC1kfB0iUzKXYUGGSzLtEZ9fu\nRhQyrG1hffH557TpC4OmBH0p3bdZkg7TTBaxmwSJxcJqY7+5Tx3b5nfJ3iL7\ne7SK6G192pqCYHo9UA8fcqF/UMntLdRjes2tuMnXvT/KRHFAqIxp3c+J0XEh\nObPYKPKAt4gINlrRsLEKvlDYciZMvzqnvGzUT9JN7Q3WMCCIO2892lCwtysd\nDEToCg7WYEBYvbCTXpBDQNC7GO/qcW7PMb0K/OAgpwU9sMh70MlUuUXOLOpE\nejFejfg208dNiIiCN9nWonkrIxd071hiT6HKGjlLwwzeeGLCPXlB9cVPgMBn\neiQEOYME63/fQjvZpftXIp4wsulFa2vyAAsViz2ECYGuUGzLOwfJsojdIpPv\njExCB9axXufcQk2PCrjJi3b6rTNnczSblM1I/MbeU4DiqzvlNeFnEhqGwbmN\nTV1sMrSPfdyAeSVtBHYvSQ59HuQPJ7QL5xYcS5qapE0YAUxkDOwEjndyYkaV\n4dzU\r\n=iNxl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDB68+SYGgesvlV/kTxIQKv0gh84j5YeqW7bW7AEZPzvwIhAJKaB/iVN859b0vBZtUJE9HF+W2Hewv2PGrCnRbeADDi"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181108102031_1541672664356_0.5963309364035243"},"_hasShrinkwrap":false},"1.0.0-master.20181108102426":{"name":"@atomist/sdm","version":"1.0.0-master.20181108102426","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"69c10277fa34f25e347c0f4c4100638ab4aa23bb","_id":"@atomist/sdm@1.0.0-master.20181108102426","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YhzWJJmI4pt+LWigigazYOsp7ghRZLMCV/ALKpogrh3ZqpqucSREUOuAOo0g/jHsOpZs/ZOwRXNk9Gb1uHIamw==","shasum":"bab09dd091e327a6480c5e1c55b0a21d8dd8d47d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181108102426.tgz","fileCount":671,"unpackedSize":1601428,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5A/UCRA9TVsSAnZWagAAMh4P/i5tqN7YBpA9RFdJ4jhW\nNBqPhCJwQDKWHVIT+7jJxgMkZ1f7f2fpyKtSVx1KqErclG0PGsuGO5ZUx+Sh\n+5hukMVSoIBeDjDH+DW/K5chYPtyHtcqe09Kogu16y2VDBLxHe13b6KKVN9L\nlVA+koWhsD6wm1IebOS2uQGRXs9i5O/Ut25H3SIuUZsQUQm3svwqAoTeQ058\nunfa8vEsBCNYMI1jHmy0jMrreAf1j0+T53lD6fszzyOmoYEmWiWiILmUO0NH\nhQyN3AlnZYkR9sIhxv2a8Gj39QwSPWbVzAxFxYJ14p73diCZt3omQ5BtGesx\nlkSHkmInGtjPfS0Dg2+nX2YDprE7t/QpDq5yZbFzL77huJfKjnIPHJt9Fbce\ncMu5scfSdD7/2SWuBgISxa8/HJKjYJWHKQWUgM9vDY9xUrb0FyjZlixeK8n2\nPQul9n11Nzz7/C+f0ahfRUYaCEy8pbsUk5AqWiNC8CPNC2QKw1OGYwEt+LXz\nsmTN1ZYzD+z+PpWScEVdp8qxTrmM3zfJ1F0h9+WHLO5c1STwFUDObQoacqAW\nLAl0pxfNapnbprO1UEFj0rNv0i+plZI/3dVuEj1h52B70el3mWWJ3e/+Rnxb\nnL0yu5XT9G80wgSEyuADrF9RfHEelWgY6wg0VHHTVBraIBvqK/XoNe6r7Trb\nui0D\r\n=KZC+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEvRA+z5JJyM1MFNBGfN0llYXydlSadc4QQKl+8gCeclAiEAj6bcomNOgIuSN9+pUQzeU+D/aZZEx2OU2/hLTRohJo4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181108102426_1541672915690_0.6890729345185778"},"_hasShrinkwrap":false},"1.0.0-promise-any.20181108102545":{"name":"@atomist/sdm","version":"1.0.0-promise-any.20181108102545","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b3f37810a0a66c2837d1eec0297ef1f4849f84ad","_id":"@atomist/sdm@1.0.0-promise-any.20181108102545","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Ac7e9O5u8HhGWr0vXm1cPR0PNERsWHTNT9nzzLNW2hc6PWs+fyvtNLlYWPp4Anxfn+Tiwf8Dxeh1tENCiiO1Tg==","shasum":"3cdb3d7d7cbaced0d07324f8ed60029bcd8728d1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-promise-any.20181108102545.tgz","fileCount":671,"unpackedSize":1600660,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5BAfCRA9TVsSAnZWagAAMpgP/22VIerFc1NSZzoJltYq\nNGVCcPUgoHDA1JsSJwP7KOhVtXIkIV6l+XjdBCXxcx36VmLu35rtJMq7GxUN\nrqqB5zcoZK0BATAIFfTdWMwiO8gnEZWUb+hI5iJu6PGQygDNef5sD+uxehnf\nhK90jE7yUYQQ9vs4YbJuATrlizMVZi42IgfFNuux9i29OkbmBwxRf8wvMo7T\nOcsCDWEMLXtgf7KLmTFoGsg5aAX2PU4j9WC8ZYZsomVfj48sGhFPgyEj6cCE\nCsBMbc9XUv4qw6/Zs7nVTWQJ4kWgrmVAVOt2eIRnztV5DfQKy74SM1hcjd+i\nQ6qnbLtJ83jBmhWlVJ3isjUGUlNdqQeKwNr1RslTV+p8ANlvvWQkeJVEjmqZ\n8Gi3YK927jdEk2bVq842kZDG+A+0Qjah4MDTXsnYwuj5TcDqOz26vmIpEq9N\nh6Gt3r2E4SE053xJQ0+9pjz0J8pQ7K+9LK/20SpaIpCgNNIMMUFUpshJ64q8\nLKtaml3GnZq2W0Wni+7FimJ5H8M/Mm9dT4IQ5WMiPCM8xqzWSJG53ZygmLq4\n8CIg0S5ett/tboUrOTX9aDHrwFn11q7ue7KcR46sJCZVuSey22qtNAWfWoDU\nAPleWWAjTO12rSLVYKJIiWPM5aeOYwgZ9DKaPMiB40HnjbV78ffa0OVbtWJR\ncfdp\r\n=lwLc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDYGasdwjUaVBHrpQCM7chW3PEKEUViBk7AhaejlM53xAIgHlt+e6GHr4x2p2C/zibTW8valg+5tfvXQvqk2u7fuB8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-promise-any.20181108102545_1541672990881_0.6724226725112403"},"_hasShrinkwrap":false},"1.0.0-master.20181108123227":{"name":"@atomist/sdm","version":"1.0.0-master.20181108123227","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"97ca6cd9e5b76dcf0b87bfa7f6deae58a6f79660","_id":"@atomist/sdm@1.0.0-master.20181108123227","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qoh4HkQy5/Nknzrcf1DO6Mj7pVSHHqwBRvZH3NHAEjvCkU8BFjCDjPU1tumCIBwWUMvV/zIa4jv+6worSis7sg==","shasum":"a4cd79e3ff7607a55d367f2d0b49c7c759a12201","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181108123227.tgz","fileCount":671,"unpackedSize":1601935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5C2JCRA9TVsSAnZWagAAA4YP/3zjOvGyHKPYnvM7lsYD\nLhf0DtMrQTwNmGQqSHwUJA759f6KM5klI9LZvYiHjb2sNopvWSuYeOetsalK\n2Eha/BRq2rCDpnsSh052OQQUzU7U+dAnkos+29QMTfjoAtMmTKtPxvyJtJhE\nSQYyaquTNvcUM5q5KA/vdTCjSwzdklmHhVqLQ2lJ/CcRsv4/zAQpYwxuV2fa\nS+Yr82OVFyxctyLLWeGNvpTfEH3ZAjRLxIUT80GwrrzuLkTGzfEdpU5eOXyb\nnsBoPR2ChPOuCryWvSRI792lVUb/PigOGjE6QV9eHUZ1LwYrkorZLjQd5CN2\ntxwo8l5mQ8Tlpo+VsnpdAbQDuVuoQ9D+OXAotUyB5IWxA0krDyM4k2TTpk1o\nE4Ah7yJ7xXSaQc7hz0jliBjmEuRVgQuTKLHehca11I2G1ePXI9WkwQpgnSaJ\nugMKSL8cFt9EGb+u0baeyGa8pXB6PRKuYancHMR27DGkrSOPNQv3w8zU9lL6\n414/PdaLBB7VvjQJKmvxTK1S/TTG5d20xRV4dgDdoBQVkF4Uem/uGq9Kiavf\nf2g41YQ/Cr70/pYuYvhnPtCrNkqWxBPgl1YQbnU6CnfMhzAc35zcKm5X1xkz\nUOB/spV/qvoY7vyFtsRAJjzHOnL1Lub6ClbwDhZ7/WqtknbYIyiaAVB53Rgp\npmax\r\n=eW9y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICgeXA5/MDKIp9VC2JHF8D5HufBWebgcmyjGGf1rciOhAiApFO9LMtNjyPjgJay9CGDJFRe9GDmxfe62X9LdClfEoA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181108123227_1541680520508_0.479714250095465"},"_hasShrinkwrap":false},"1.0.0-master.20181109073515":{"name":"@atomist/sdm","version":"1.0.0-master.20181109073515","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.2","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"73ee4b037b8fd6ff95d548a1aa53ef4183c7265b","_id":"@atomist/sdm@1.0.0-master.20181109073515","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TOS/NLKIeUWEBSitmRBVpiMf/amF/IkOnGCc/+0ztznbYaOTNJ/OdhpF44eZiKYx2tMt7ekBHXBhOZrAXei2xg==","shasum":"821e530bd1de57bf8e700ff587d52d973441677f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-master.20181109073515.tgz","fileCount":671,"unpackedSize":1601921,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5TlYCRA9TVsSAnZWagAA9LMQAI2hP9Y3/TsOfO+/MnaD\nAwgXvfyi8ocYhykPsBeyOx03GSBgpi/CPXSlObqNEV00FJQQT6HMFbLGMxhN\n7c4A2FlTQTm3Kl2FNxbFHjSx3RLI0WvPVXCvtfQF/IspVQNxXNrLTUt7mGK+\nTy53bWcwZeojiUqN5fQ3AXKWq5qEHWMAn7fTL2rj38xS9nz2tAKRHTNmtqll\nvisNu5rUMjRfXEhYse1gwqtPoYT6KSjlwhMV462ejUmP5wyU6qZnviInFbvJ\nVEK4yW3BTygv7kiXUCkjkkjgg4paouS+GMvzKfxAqBPT5MKkF01KC2U9xrbp\nD0M3H0nR8pXdnfwpJeGOG3rwGtMbN264yoJ6OSX7sZZOhI3uv1axMsi+JYeA\nLSOILgdap27soq9sycw2BVGRgIrG2RkbheakCHx1+R31z+TQE6vfRWWDJGYf\nChzK0om+v0dfi3HBmbIvwA2iGejZZzPVeitIf9O6OmQlUcSDIGS0idsKdPSI\nSrYuj5Jd/mf1wQTKgkJOyzZpuhXOumaxRLl1D8T+ebHtDKCNpA+IKlfdYd7s\njPlxGVm1Lqp0BTyCxUoZ5b7ZOjYssEr9Q7gZovpdEC9nCc5/lSTnvL5WKot0\nriRMYty7dPWaHFWhjOM1I8qok05fYvub83wErKujK2z3Qg8pQekoBhPL1wQ9\nDqF/\r\n=BszO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDWAnccKYIGDPjbcaXY6gGbszvQxc+TFA9vnpJ+NL3BvAiA+gW8sIpy3fW6EyW/AxWFKDUiDuZrC6+rGbGXytgkdSg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-master.20181109073515_1541749079044_0.09484448210397667"},"_hasShrinkwrap":false},"1.0.1-master.20181109101137":{"name":"@atomist/sdm","version":"1.0.1-master.20181109101137","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b14c57c9cdaf4ecad9137348daf721cdd6c81dfb","_id":"@atomist/sdm@1.0.1-master.20181109101137","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SRbLXHxX5E8ONOCUBUGGdygRKXSjfFyYgj5ZBm3f5CpYJUiw5H++SaHJSDtePtTVmD4RD2R9zb1/YmIvwxAz9Q==","shasum":"59ccfd0f4f8dd9112e0bfb89d5d403ab351121d1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.1-master.20181109101137.tgz","fileCount":671,"unpackedSize":1605201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5V38CRA9TVsSAnZWagAAOZ4P/i2Q9iqRbYdtHN/h6mnm\n3QnGcPfYbyblw4YQ1vT/R84FK3zkQJGm39x2SS/iJNh+NBCGQfJOQhmoAWa7\n/Obn8mbXYNj5wasAIT/kwH8OoPN0sS6ZQzwoHcIt1Bey1NC/p//YcVByRH8X\n3CNIiXgyFbr2vbKcAZ5Tv0q1T8E5sMe7P0rWTeIQIKZWJB4fzvjAPN9c+V1X\njrTqACim7LmFnGqzDKBjusEXr8tMRMnqXFuQ4xNDB5UlMrQHUm1hNyWLX4x7\n0tfFQtTJmZJAqcXeDdC2AnH9TR0AEto8bolQjb3yxfPVJoaIWL/7h+pu7Ojp\nfomqKLSL7fgCjbrmifr9piGAmBFmjlhKHJe6ioKd0jYofowiO5J2eK9eLsPg\nk5XWDRghKmUcoQcMb/+zgan0aZ6aXI3/Xd6zN2Jx3kr4QUYmyx9VvIyOlCQD\nsylezK+RSOporG/NmI6/yCowXKsQ4uVMch1tS9uRyzY+duy65who4WfPc2SM\nHBffgRHycjDDznUzZEy60K2LdmnDVKLUaU/s//A6+FVZFrfylujO5wvoS11o\nDcaKwbTldaNHb0akUQ3Kcp1LOoos5MEVZ6t8xsMGoVyc1KzOGy/Gk/nhqmUt\ngIp7tfGbjyT0TvA1qRTR0g31cZqBLw/o5eM+/Qn0Cx0OvCb3JLsnpJWszuzf\nUSPk\r\n=15rx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDEjg8jZaf9V+wQhkFM2+aFT9f6jfu2JByfJVfrdZ2H8QIgEk74fiBZIvzzBBEWAkkdQEkbWoggtsZKRYiZgqNfUUc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.1-master.20181109101137_1541758459901_0.4737422536281344"},"_hasShrinkwrap":false},"1.0.1":{"name":"@atomist/sdm","version":"1.0.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"01575c0fa9d15b5a86f54627a3bba9e101c0cf49","_id":"@atomist/sdm@1.0.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-CUtflJunXSsaC2RWLPg4pLx4HrAJuOek709+VY10adjvJnKRjAcfP3GU8z0g3Ddh+UZucWzz6kThqanGNSWiMA==","shasum":"2b9f87c59a423eb302c02ad0491747bba383c977","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.1.tgz","fileCount":671,"unpackedSize":1605179,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5V/3CRA9TVsSAnZWagAA70AP/jkFqw2Q7hbS9LqaSmZk\ndK0qeyBuu1kxlr0r6URWPzR/56hRmdXvhptc7+/z0yPoKwbkb2bJi1qP4fEB\nEBHRp3SFIzjBMf/IB9oVlN9kI8GrVrNHaWxruGXtM5oILXtAOwGClLKibaiY\nwiZP4Klls2adpl4kV91zcKC4Og23sPsNz9qB/nvJuDkwohpupme07ZlK48tZ\nyIiRFFXXpvgLQQBhqflNDKqnu6CQ5HZOAQyqJAfy2bnWP2v9bHhMuM9fIjsr\ntI9KfnV2s7C1Ha7X+ZR7hcRbQaAaipOfisAy9WNxWE0tnc7CmS1kFvCEqPNJ\nlroc6pdPgyk1ua/kO1+3znjnny7qG7Y5Apj7cp0J8JpH1Fqk2Mk26uG21uWG\ngGmulIRz/PGEEB50eNj+UzbaM570ZEAHmZAw/1cptXBjhT+/RZzoMyUTH3W8\n3mbMIWpr4WvzxkJ91MQomrxZx0vgZFvfTtiQAZfLFVxdB+j0bRpPydOJyMVL\ngdVC+YwZPElJmItB+Wr+0CYhYpVWF195BbRj5jveOUAbTmOwuVp+Ws3KtnnF\nfpDLkviz8HHTCyUA0WM7Ete+xTsYOuEMKAklprnn6PSXlhv2Nl/KV4HjsveO\nRKl0yIqQNcBTQ/Fx7z+0Avr/SDs9gfb0h6xDG/9Yfw7I0AuuNWdQ/juTl/6P\nsLVc\r\n=z2O4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCa2YP0i9QPHmNwGm8T6rQtglsjPBJkgDKwYOYV3sopFwIgDBlLgUimsDuuwyg8c1z0CTNVAY2VhrVNGe79kUAT6Kg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.1_1541758966255_0.7535691353165894"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109210447":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109210447","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a5ff9cf38206b46777e7c53a4cf1b584199976fe","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109210447","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Y3FDXA7u+12Oj5TnfXfA+4qxrcC2YoZXUTJtkHtOPi2JTKDUCjxwQ3cb4hcGBb+PksWGijY+DD2onNn431e9vA==","shasum":"f8d1dfa0ffe1db7dbd73ea5867f463144e0037ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109210447.tgz","fileCount":675,"unpackedSize":1618532,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5fcICRA9TVsSAnZWagAA80kP/2T3k4z1zgVGFpWoN1C+\nAwtB/fgfhSCwJ+/IQX1J6wdsJzgCIrX1r/NShyJ4FosoGzHjlMHaiyuARVNN\nowD1B907Owstt/olU3U7bpmq0n4VHy++nnmyRXuKUd8rbQsRXWBJnV30Lyol\nxmCBsxhtxCfgUVErSqQgG5EDvRjspXPiX7grXdbS4TS0Xzi7UHKJBGvEto6O\nERCf1BLkIpFJEyITTmR476iACNAgTiqra46dmjH8gNbQ+he/MC8fIEIc/7L6\nsuk7wkggMemXJQFyfssvw1y3ddHMuRr/hxdH1RczTQrU2WHIm8jZBFB07/mM\neDhyjGeFW3soFPhmCcle5IlZlfAGaCBF588PtHNW+rjD9SWKYJJ4RGfF89eN\n7gEYCuEzyWviPb7gZoshZp6cyq4FATMJVYqWUiQ/9Xni2X7FKxmb//9M4xtD\nlpzDoyIFGniUKy1FxWb3yWov+vWVA/kNJ7fUXD3ANaMDJNmsYJAnD3ZHVyE0\nEVg5bxGsqlfussLycjTVn/1a5fkuX9JbTkqmKfVbWfEJMlF6daIgTqWAHMFy\n/X7HaI3f8s6Fs6au1gdbC8clIeGFs3WQows8tTDJ6re7mRBp+dvbedVbC2Hm\nM7LEzsm9HuOGiBFO7VgcMkssIYj6Yf5Ga9hbSpMGyF09IQCSz/PkPJaPNw2v\n1GgI\r\n=yBbt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCA4iu2Ud8ALD2wzQONx+nuk/xuqsh4VYuL6FPBO/WeVQIgQ2dy9xAr3GnZaGxzs2Lan4XEn8waRveMqul7Q2tupMw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109210447_1541797639694_0.3746323696223166"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109215556":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109215556","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e4ba6e40c4017bbaf157c32cd08c22e751be479f","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109215556","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vUYZOO8pfCQAU8sVkKpAVQ1MkJwnGYjgqydrIbQKNl5026zPM3OzHoD7zrOl7/Hjay95p5qmCEIAVUKQzYYAvQ==","shasum":"dbcb9d2479bcd209ca1c93be4bce351a4f080ac5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109215556.tgz","fileCount":675,"unpackedSize":1619894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5gMXCRA9TVsSAnZWagAAingP/1NjRbqgqnRHadkeNkXt\nS0A9J/fM8y7GiKM968OcUMi5soRx9YyFMjUMy+tQ85GIRz38YxZNoMmEYYzn\nSeUoTXNpeLlaaXF7jd25aOr6ysLkXYrmgnIQBQNaN6wK33+YVP2jBo3SGEGe\naA+fRqSEKGsLbyK0GIbjl2dqtu6sCgmwnJBkuCixJ471xXKlcAMrRu2FhAVJ\njgp1sB9m9/KoD9rIDedk0urtJaaCECGRyXzAG//JblJxpdmnrW8o+pby3A5q\nfzY8Um9oSYbLovqkXU+tiBlAFKCwImZVh1GE/GbCnEfS788Vzs5otpMYmQze\nXWNqWZjOijQ5gVW21Twaes506b2fgYcC/PtdJUiIDv85vW9XwJcVTvp9jwuH\nf/cNh2uf8/7Nu2acuBMAuVCqwwyn8N3FjeonPyr+NqOGtK3M0ugvombHi+2A\nKxTagfa+uEo6S2W2dZrCHZviXxN1s/diTUwleeZvvimNl2WBr4XV4TcZNZ5i\nhJfDOWnOm7e4q2piuL34p/P8L/VV40IDYQ09qFRUHkIMEvBG9tJeFrYSPgQ6\nvHswIZTtOdc7dCkfJhbsvkaBhkeOwqLNJT36t2XjGeaWzq4U2F02lSvMsLml\nl1gzgeGqrHgx/+CAti900aqXJ8rLEEhwERelhdNti5iIrPZkLKFFw+yw82Dk\nCqYh\r\n=eiik\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKS5pDuo/PAMbugmaFqCWKV+O+mqy8HtWULJsyJtrK0gIgW+j1WweP+rf2NRFJp3KEwdU9jgRSbT67OrCZK5TDNTw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109215556_1541800726412_0.3158108501397998"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109221949":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109221949","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0da3ffe78ff81118a9416732e960bfb26670333f","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109221949","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2auRn7akjfXGy3k03r2g/WFvnSzo9CNXs0eIqY5a1X9Osku+9daDw7aIQUisPJjg041On01m4ZbM3/sLXeu52g==","shasum":"5b7b23b62a47bbb149ab58fdbcd4eb37974f225e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109221949.tgz","fileCount":675,"unpackedSize":1620664,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5gieCRA9TVsSAnZWagAAXB4P/1Cm8rNQryFneOtj7rBa\nas4HBiTL0tBbe7McZWYPPVYstsOh70XYnJM8ilcV7kQ57r6sBjjTWA8fNt5q\n2L9r6PRC5GrR99wWHlWIPoc3gsmrhkNRZiV1/J3X111Af8VvnrmsEXzKiAAR\nIlGsDORCJTTb2dKq0J+3PKUHhYXQQKWnlQ5K0650W7hd1D2Ci9N+tvWwJ97m\n5O8ABL0p4qqutehv4cbbAmfr9wwwW2I3PW1ltiqI+IXuycHAlJ0Hzfan2EMZ\nyvyLFdjOcxbQlT2YAfToIy59XrYcfXEzW0mq/eKpux2cYpjwd4mO//Pb8QG7\nvv9G+jQ0F9xV1W+kl25OpSHgrc+XWYQKldSJyYJU6FfzmAQcdrvE3czGBnDN\nbeUcyK/N46hDR0YELieH+KENGPLJBRGz1WpoAuwOWBnakINAMFg5zvNw3vMp\n59tRioOuN6cEtbpvwuC6q6EkqpQ5m6FiBL/QPWezvcfbAGYe4jV0OiaU94pE\nnerHpWuHljT2yMM9U/tdvQwSdOiukNrn4P7d2ImZurgdw9ZEWhTnpT3Xhsw7\nW4mA4d8QL4/7dQNAOBLFb8LcB0cZUJQMq0DZEV+gn1UXYy5Q6an9OHZXx6q7\nXHyNxSNLS3y913XG8DLvHxlOlANzy6cKYZM539XddH0TBCJ66Q1t83miVWZN\nMon4\r\n=4yws\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDBE8sjYItL7xjdjpMgQo2RJNOzMVkWxJvqNEjqWPwAvAiB6nQOzD7wH8lZzTsXA8jseN0/HnBMD+RoOcZTVNxC9Uw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109221949_1541802141419_0.05261280858942774"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109232037":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109232037","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aacbf494e900a4c1a5e16b11c96d029c32a250a5","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109232037","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SlbsfmOFCp5J7/U6qLAijfypab8UcGVDyXNGipDWTzEvY+rWtzqcI9YGZYxhGb/ByI6GBk0Dn+ag4jNyHDpnmg==","shasum":"a0cac9ece8056ed707b74358e492c182e8d06df5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109232037.tgz","fileCount":675,"unpackedSize":1620971,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5hbfCRA9TVsSAnZWagAABaQP+wZZ2awtmB/v/hJTTHt/\nrnIOcGPqxNg8a2EIIpCdLGUqQehUNJ+uJTkUrv0qjYlJ60kkOel2FAYQFDMn\nr+ssVHjs3R1vCtk+FQrkYwadxaJ5qswYmwPgy/E7E5tDgMayRdbcyMuXDdJo\nO4+3HufClTh5prxg5Bgv/Q6lsVXrH6gH/Ib80p5cxsh8CwS42LrtyaQ2Mzks\nREvMWK0m3d0flRUpccBntyfZsltX4MJtIMewZgzLt849E2RRf10UupBcqcGY\nzacleW5t/XXRp/fImYeXKMuUNzTn+CEeNW7JZwp976jPuoOHQy+MwBWW/cVW\nPw+C0uGWveII7RhrVQmyUmJdgFadbWqS8ZmPeXQJWI4FyEBlTz8RxjpQUKq5\nkKeTrn+gGko7lUkcVvUaXdIwg9v66ZPzp4ZAKO4DQzvo5uAGg/hthlm07IdX\nGM4H5bE2SB7/FjY5IWu1EVT12gGFeefNeHirMTBve+44b2HXhDXPxYz4hMfQ\n3uv0ilqiJzPn1u9lzAnKWlnKKXMigcNFB8u8bBZeGE7BOjruoN7nx3lsnHkf\no+QvBw7keuSVkaw2+jdfqeuHtfow9S2xoN8hBxtE0zhPRRZSvhZjUS7fJaqu\nk1Ee1JfGrkjquEGHJbsZ9TW+qeS5n94Gbmcs6kFXqyuuhQiwioiwSsLYYOaA\n/ZTp\r\n=Ktub\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGNVTvfu+eIFHm7wNh3WMshWjf0bwai4G54PS7jGNigAIgQfbmmZ70fYJjiySTLV+CwHsi4vmKehUndYb4HOm/SC8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109232037_1541805790836_0.6264538577118877"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109233205":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109233205","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"13f1e2dbd5aa4d5c73072d18a498e4c3f9e1c566","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109233205","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-TuYVwIqX+ZjdNu6qSepnJYksLeXkJTaGuGi6oSnnRH/OI2X7nvHoxXVdK1MTmQhuokn36n531DL3ZJ9mJj0/Mg==","shasum":"62ae303e7643a1df9e32c579702d847195066175","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109233205.tgz","fileCount":675,"unpackedSize":1620969,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5hmMCRA9TVsSAnZWagAAquYQAIo+zo9Df9l4oMZ6x3LX\nFJdXoK+DGjSHnf1RyZlQ/sifRPxD8b5UxvlsV3RuQ3TPksgsMMkkn4AbyBM3\nxGt87jei68LXxUgeptssx/TRzMTMgoV0YDpMqGLpCk2UET63ScdrR3PSyigK\npssbLQSP/SQev0cGsLhK73ItpeLt99PjmksZdHifuVd8Nrt45HVfUqQa68Lh\nMs6udYjlvBnj8gNCjYSS81TsFyer0TKDdTqUdEbTm6Pnnvw2T2nlxpLPfjBq\nOqs2QW61iaeJjifgs/ybM5/QrhcSkYZ6DNrqpasyOFbY4hkx66MwFPu/lsSQ\n1apVpMiDbZ5uYaVgXuNVnld6k08rMfOJTbVclJSsI1dpD2aUWQQ4p1f3otSB\njxNPGoifhjllko6csCOtbr0bUBYZ2ctepJKxIggqYjvI7LcqcV1V3Ozf6nab\n/fxNcQmrQ2JblNnkdHhOZqFjureqOxkncxYw0il8eQDO9W6EX0YcMWqN62a/\n/+L7jIns+xJW/C1hWrmMKaS1mkj2n+uZYwND6t4QrkA5kpAStozda4znZuDh\n/qS4YexQjHLO3FIYiGrAgYAes95R01RocORjv6fZDud+eusnG4K1hps/tOzE\nZz8PJMp9wAo98c1z93fnNsyV6Zr7bvptDLNDtZMmJ7ew5LFC0jhFbeBc0UrW\nBRec\r\n=ikXm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGmLE6o2DIN4NhfGRaaUTSe1kL9f4YydyCTPPHvwhcw6AiEAmvdAoMWicv0aJnrZNnkySRMTV9CY49FroZkJFTs4jMQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109233205_1541806475394_0.3019053684198789"},"_hasShrinkwrap":false},"1.0.2-cancel-goal.20181109233711":{"name":"@atomist/sdm","version":"1.0.2-cancel-goal.20181109233711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a5ec688469775ef3fa4a05a97ca542e90fed6eca","_id":"@atomist/sdm@1.0.2-cancel-goal.20181109233711","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qWepXn1GrVvAVMUOIxzZS/UamGBM9gcNCjcLoFOPympK9HtS5KEjQaQ3+76v3mgDq0ACu0i3uDzHVNOzSBnw8g==","shasum":"c6df6400fee8ee07da08e96d7d78556efddd5e5f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.2-cancel-goal.20181109233711.tgz","fileCount":675,"unpackedSize":1620985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5hq+CRA9TVsSAnZWagAAd60P/1yB7wPKemwyGZTKFYMH\nDuVNQTsiHHa6NoDQ7jvCR2U2AtAAdPnVqziJUQbxxSqOFudUpDV7bO+FOzzB\ntxXvKMObmQVOe6if9zOK4GW7i/j0Duc/hrRbBr4HHjdV6QRoadXAY0/FR3yv\nm4uz6IwxtRRyTMtjveXCs8H+7it2wSmQFEFrj7tUEBErjCABicI776wwjhcT\n2tjJc2Da0M8FrZfZL1r/qW7TqoGlLOiuGvVP1KMXuE9ZLpq5Lgbwp+pESwCv\n76gxiofNF/zycy1OmuyXmbyBs0bHpPuG5XIMAxET2zYNsiL1UAMuLwIIxJ9G\nRQCI4379radTaGrI2a8FSSU8h6qgRPiRG2C1+t9skksmODevb7w1DmuDblgR\nK7139pG6lFVKoxF/EB5I42yRktWOaV3o7So3xLsbF4DhrtGpjSWb2SC7dCTr\n//1JD0RVS3OGc4VS5xurfIeQaUu3F5WOA4wg+iuOcc2PZjjUNe96+PTZ2GFC\nr8qvMBnIUmVfISKHj/AdcOJ47rq1c0pRydf3iBeMhJZIrT9sYbDpDWn1Vbcl\n9JPoF17fCn5iraaLNrfE51wMYPt2CHk/7gGfM30e1ATmF6OX4TxC4GFFalBW\nkGm1Dk1BStlJ6FaO7tGWolucbDJSSk1zYcWDnlQO1mMxIKKD6pCfpNCEwMwL\nC+mf\r\n=SQsR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA+O9jUmqhm4imf/0IWZkrfIRMnZ1n5k42RHok+kohPeAiAs+QpeMmsnf721WbOqg+6jJbFSsiqSo/Y+Sh3fxVYrxw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.2-cancel-goal.20181109233711_1541806781613_0.6866930534655409"},"_hasShrinkwrap":false},"1.1.0-master.20181110133644":{"name":"@atomist/sdm","version":"1.1.0-master.20181110133644","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"960c127f057be4792a467392712eb3706e498cb5","_id":"@atomist/sdm@1.1.0-master.20181110133644","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gRIN75bG61P7IYbUyZ7Wn63INSxQtxKCDj5phXUPxyN9oUcqioi6khaThLT+wPYJg8963joExXNyYj3EhSfo/Q==","shasum":"6d8e9a40974742cdba1abe954fad7d8cb1fdea89","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181110133644.tgz","fileCount":671,"unpackedSize":1605280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5t+HCRA9TVsSAnZWagAAYNMQAIucEUm9CvkPf67gP9RE\nMo4RpbupTbvax6gYVjQpGEc3gtiD5zZ6+Hf8/cX1jLQkManjzYXvnFSBGR//\nQP5mmuGdjmUHan//jkAVBGIHM/kYrzVCdk/ORDYT98J3kxOVqihOX8+66ML7\nhDoud4X6TFQtnBsCJBnGRNux3zDUob+F65aJn1BKLcV4V2iNQaUJXXUSX7x/\nLEjaWEqTHDJ8zCCxQk5iRQdmqeWbsxBZrHUrt0JrfG2FZYIbvD4hXlUTi2ud\n6Ow2iBeBOHOb4YNJGPl563W7WqazcUZ29HtimXif8+0FZgQxa4UI72FnfbBP\nZMJvFhj+0+8nkd6yVXi00FAIEwIH1Zomkeeczm7mebJmA1wlq90r/s/ofzdV\n4pvnj58+gpFiZwBjnLPWZ6uLR1w9gkY1e0mTcmLQxLtTdd8iW1rCbYFHWBDU\n6eeRJOVcFyLW6bDsdLm21dp09hTTLt3I9YkoclqrlWzf28AI0Gl+sVgq7xks\nxDJPxwcB8NScQl91CzBt72m8Rs99nfkCuQlU/0v+qMR09Lskv4f0dzm0pgbw\n4Dzcg+JOIC5rvSPkDsCY8/J964eA0iFeyc8fxrifgLfqsRaf6WXPxcXcMcRT\nfET6pcic6TPSnyGPR73FQrcaeVz5hzql0bH+xYdBzmzR1MhP3InIs1CteVE6\nu+8j\r\n=7tAx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICw8kH2DNd54yxEgeDrXTJJUJf7olX0AykHjTFCjMdv0AiAzqiAwUVQv7ICC1LzMLwGxyiTEYliP8p8GuX/sO+M9mA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181110133644_1541857157607_0.14562403066776053"},"_hasShrinkwrap":false},"1.1.0-cancel-goal.20181110134006":{"name":"@atomist/sdm","version":"1.1.0-cancel-goal.20181110134006","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dae6d02e1329932e51cf5d22c9f416ee43c01d12","_id":"@atomist/sdm@1.1.0-cancel-goal.20181110134006","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MxR0185oMO8oOFK0gSrdAXse7z/zaBhI2ITZNNmkwlonoKJcErwdRA+2R4KiHHKepqD9DgxbVtqnV6Lna6WAdw==","shasum":"7baf55375f279af39760a80b4adeba4d3fdc258d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-cancel-goal.20181110134006.tgz","fileCount":675,"unpackedSize":1621007,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb5uBOCRA9TVsSAnZWagAA8PcP/jbRr9LfImN30n0YY+1h\nwaVMoBx1RBJpMpfaeSoUSRYLNC9DjkUdugKZaRxfNAEIFccjk5PYtWGqhkFh\nY8fELiWHIK9heW9vqVgQCENNByMbJjLe54JUVA0adY5MZnjJ/OHXiklNI1Bw\npmIsgBGgAFfAGitJXOaUfUN8d7jN2klzjCczp6P9fbbTQ9rTyRVLlj10pRFi\n0d1djyYTvGUHRHaNDHlzsuH5SAtHni3bfo+X9dJZ0xexhHv8xFtmibOeQvUo\nWF5IQM1c7f8UixZ37JG6Ucr05MwnSxkSokCJkI2TP8fIvLGWiSKVA+NS5TWS\n8EIs/g4I55MlboVbAT8WpvtucUMrmDr7jYPmw92Mc/jCh5JDRjtXQI7Cxumf\na+jj0UzxWsUeNjIjJ6yc009afexac+hSa1NxKz/Dq3a899LgD4ulOSJHehrY\nvx7pfeD/caWaZM08JKL7PI//+6gtJkvtmKc6XSQ9cY8TOjU+hzWVI4ZGugiX\nZm9blkH/AaX7aaYSUEDUSEQT3NGPa4kxMNn+/ih0iDw87ufbmdvqJqXu7lJM\nPm6YcLem3XT2rMLNrlNdZjoA05mQHYRxD3opSysr8GR70ytLaGukvJQvD2f0\npLA7zh4dgDg7j+OhwjbCbb8Fn1Zk95t6PutbhQtzYWdjbB13KE1STKInbWwd\nTqdj\r\n=MMEM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIErpGAzYtq8KUjZANbgYLbRHUFY/sIVUrtjzpofLxrLTAiB2z9K8OmiqCOYWskmHC0MNQ/Qt8iltAGBG/uKnd16GhQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-cancel-goal.20181110134006_1541857357173_0.6018376902169837"},"_hasShrinkwrap":false},"1.1.0-cancel-goal.20181112085621":{"name":"@atomist/sdm","version":"1.1.0-cancel-goal.20181112085621","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9e540fbfc51a61d36d427c4ac1f366450ee80815","_id":"@atomist/sdm@1.1.0-cancel-goal.20181112085621","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uK1gkVhU1V8b4NM77F1ChsHxwQIe8R6td1wmIAlXXV9i9LR8RdJTDSTRMqvX2G7AxpIVoqZXfS0Nqd+UOPVrpQ==","shasum":"a2d9ebe0f508e062587ff4e75a29ac883c332fa0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-cancel-goal.20181112085621.tgz","fileCount":678,"unpackedSize":1623399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6UDOCRA9TVsSAnZWagAAfhEQAJV3PYKXDmgG5RkAqZuY\nUg2+f93OpWXvyvCa+TPbOoHIezD0WSedY578XHxztO838Q/j26RyLCOJ5pCT\n7ishZOXk1BuX7qEJjAoHgK2KWNEmSTN2KnMCr5HyPTQiFUyaRt7AuK2ri+bb\nyV0nIGulnOzHg6idsn15HE/EXQ2hMC834Kc6WzFGEgynsgWigYURlTPaaWuw\nrQkykmdtXYgSdT9qMTKmG9NHtw6vPsUlsMMJFPpaqEu0hZaWFlNn3t0/2idQ\nZKxbq3Ldb4CBaTpFJmLOGPwnwETOrVTbeFJKTw4LjRMEu2uQBGs5jqhjm9vx\njfO8Y8g082ec6/8ikTbxXgNKnZFNC1x9WNoxCrKtHx5Z25vseLWvNPEDflu2\nTac1CzSTMBMb3gtAhTU26ju7WzrAovvESUBOma+N/a+/WPTlh9y9naIcgA8+\n+1Tard4GpBKvWlSE5koTCJoweY91aaLD6tNkAWJvbAbOigG8Z/ZEwq1y7Ut6\nOvgVkfpSeGCpFoIwME+M3KU9qFYHH2LxFjfwoYDEDHagLXDJTkLasqbpplB+\nTIXtjwyRSVrRSRVZ+oC1G7RcoFpjb6SHzyPshrfoIONYVktTjnfVf0scAptm\neAyTYqsQT+/1pjbk0ZPB2qHi82Vr1UXUoGIOaVr23hLp0/bcYa8gHl0hQPK9\nktrJ\r\n=6awW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHFjAHua/TwXSCsxqRkkNh8eQPWih94jk34r/MqjVC4oAiAHkSuc+zI6Nn5s7sa+CgOuAo6MkbTJhEAI/k+rmRKFbQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-cancel-goal.20181112085621_1542013133985_0.05011036666231594"},"_hasShrinkwrap":false},"1.1.0-cancel-goal.20181112135708":{"name":"@atomist/sdm","version":"1.1.0-cancel-goal.20181112135708","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4c5f4cbcfa4beba5c8e5878c93c366d580ad3e94","_id":"@atomist/sdm@1.1.0-cancel-goal.20181112135708","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XCtI98yzBlErAU6Hb0MD1OxZbKorzQE6xDJuw51rKMkrqMSxhh2oQbMGMziB8DEVIkXXPLM/wBx8CJrS2i3NRA==","shasum":"3b3e601a017b17a8e36f51698d393517a82e62d8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-cancel-goal.20181112135708.tgz","fileCount":678,"unpackedSize":1623546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6YecCRA9TVsSAnZWagAAd9kP/3N8seVJUYJWsK+JR7is\nca+URolXvhKNorYuJ8jWjQ4PFKoIO231PO68PD7GLTaXDzFw9fNJlJ25ionP\nyrwOJIAsh+p6Exa0m+LamIVjymSjKo6i28KQn7cE6PqZBtdKTIGAs/rtHYzh\nkaXbfGhHsLM18IraylrUpW4INB9cYrPuXBOhY6mvhjpw4Fqe8e/EX2ZndiOh\nE/Q+XORs1csuRzOBcGCDoayfEfuEsDy7MooEqU3EJyjeoo9B5ib4dxw1kdDb\nsytq22pn12D3ejBNyVsA88Nv673tSOoFWXwsC3yFMJdD5UcuelmYwW96lHRK\npYS1LDebK97jo/yveFBVFSWxkzHhmqPVFax8uZ9b1YvK6fSPaKx3pUriJrUZ\nMWALZ3tuzAU1gZV5S1Gz/9iLzN99DNlcYS5bqXIYuMBwz0LXYnmru3bM5och\nFnel6aiV4ZRfgtFIc6sVPtyMBE/tYHLLYSglVwuQG+sKoZIqFJhTAQbc2py8\n887r1eaxHcKaHLDbos9G59nieM3ZTFNCSJslbshBdxccX6I1rVoFW0IEhh3A\ndb/M1qPNK/8KT0IjEDqYoJnyq88vzWxkfogHUNC8TuB4TIKnS9mN5rBkjZNa\nDDp2WJlBb7x5NU506XkAGfcrzRVfc45iuq4pMrGXSWnLxD7DipJ6iW0fy19M\n73d3\r\n=C7OS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFJ/a8kgBzu08uVy/B4bCi2meDG1bDscgHrJ2q66BHVGAiEA7pTUqkow7vaIuJTejJF7rkO2w8/XIW/sK8Fk4PGaBTg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-cancel-goal.20181112135708_1542031259539_0.7247147467851114"},"_hasShrinkwrap":false},"1.1.0-master.20181112135855":{"name":"@atomist/sdm","version":"1.1.0-master.20181112135855","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"49517e1ca01705c2ddf899694cbada8a9a152b0c","_id":"@atomist/sdm@1.1.0-master.20181112135855","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oh7fXhsFeZiNOe832lgXVyhiio2aKAfqMZMQttCn5QsEgWCYee7SmFpZ90ihv4I1zwv5LcyZI94IgmxXlvWeNA==","shasum":"0302ee5f513b230384f156cbaea7edb23e9ffb52","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181112135855.tgz","fileCount":671,"unpackedSize":1605765,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6YfPCRA9TVsSAnZWagAAj64P/3Il0Wmv/lR0v5zx/ER/\nDDcq/pThgUX1BThGV3wZFN4LW07OUPABQyUETBPIc0HY0yRBZxOtZf82qtny\n/Pr/tF0mdopSSZ+436eUPGM+kYKcnAMEVaa+Kdm9GQkkpZzyGZJkowdzhQpF\ntM+u+K+McOS9WPp2I3zQt1iVv34GuHckWGQwc3/Vx7yIP5n62hfacS7BRWxs\naxZ0AQcKtwCtxvi/wcOGcOPy8TbNIXvZg0gqa9q3u38+pNCTQlQ3QR6bbiDa\ni4y9h8jF2+YjYxYuyB/+ITR33phl9A2vt3CsqnAQlyoubE8bADOfiRScvUmH\nofCR8sQnWZ64czJ1FTpZTKJxHVQ+gEIgMnQsnafe0UGmGR0Ed4ncpO+zwhuF\nIUA14nvqP7ym+N6jOHfFazzVOGvKt+1DxznAYr9Lu3A0ry5Xkb7P/osX9AcV\n+YV+kzB8aW5sPQb2tnojYMetwIW/cWGybRdQ5Ofx+kAFXanT4qaVaOAQUfAa\nx4TmkQTEhj+m0Jcy1lY8rmsYZpiBrdMhdbG3SrUHtjSzNM3hsNBUL/GpHZeB\nQgrMkRcWLpysF2A/C5z4JdNCdnkJNzTnx+ikRgJ8OIyqVNLQMuj8nkrDeuoe\nvtVh2QZAC3XgBo/NXhEgwJ8n6PcKXWEusmcDGWMq2ck7dXKvyFqUj42CaoxG\n5sYP\r\n=e0w8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB0rt0E30uCRJcITf9J2n2gsDOq+PqwZQH+VrI+d9J1jAiEAvqHr29feqDKzNd+d8xRAtZpt479aR3Sq1nfk1vaB/8E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181112135855_1542031310736_0.3750758663864082"},"_hasShrinkwrap":false},"1.1.0-master.20181112163921":{"name":"@atomist/sdm","version":"1.1.0-master.20181112163921","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.0.1","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"66450a02e241fcc91cc7ebabd33dcb7324c0c04d","_id":"@atomist/sdm@1.1.0-master.20181112163921","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-od5194WnbPPedeTSVZFAOznVSH4VfWY0hft3LPSir5lVEVnk3pL1cmdXEMzVhCV7t/7atXhfoHuAeujdTMOWqw==","shasum":"162d831af49102076174c6413037e54d471c0b10","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181112163921.tgz","fileCount":671,"unpackedSize":1605707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6a1dCRA9TVsSAnZWagAA1c4P+wdI4EOw7U5SvYz59f8X\nbLseII4ocE4b2wuy4Gir5XUDgMV0GtS1v7pleOseeTqwagF1p0rC67wXreRv\n3bkJiBmLYUGr5l+Wp5HEOKMsJJxCwy1GIIm2iaRH4NsTRiYmJk+EKb3qsbMM\nqgm5T4LlTtxTTbrb2DCrGDlzL6UozDIaqTPwVIKd/mGznhTbM+Ka+/arFt9n\nEj3r2tbBhORkeFIM7KMQ+4SyngLWHuYa10wjvQSzBmYDd8IUxpQAfjLEw1Fb\nYM8zOD9Ypfef+MeOgtOeuY47wm/OT5GIfArMt/2MSlX0OomUoCLUJr/6DIRw\nuEF/aKusSyY0rlfOkLJXZLNO7snTDyDs01jaMG7BZyxlHLc/6O0oDlOhWeNi\n7egWlafvHI3llsWyMXRPm2KV9URizqvrM09pViKVBa61GZLjya6ph5AVha5w\nIjt+0I5cJkKHmoKwqVd+4RPS+tLwa1PXGcrhaAscqjxXVX/6374LIiZaKFon\nTunuvRMuLKbLm3SRkuxNwTcyyeIshRFVPBF24Eqt7pqQn4KUAtYD5ZseuUSn\nT54n8JGhSleWcYCC2VDYqUrvIWPSqcINrdrF4OV80xBnxXfrbfk6hCaWjsdF\nclGeGEOaTy5PPYpApZ2poeKSRBStvEoWQagZGWXugKtUTmBJKQyO1/Rbaavc\nJ2d3\r\n=sjby\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB7UDXV7JwzpqfHcreyjTnN6fh8gQw6/7PPvnW4VZqHLAiBamHyIzgvipi43CMOMADfKc+SK0hcAE/5MKzirr4CdcA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181112163921_1542040924133_0.3411614110936545"},"_hasShrinkwrap":false},"1.1.0-master.20181113110342":{"name":"@atomist/sdm","version":"1.1.0-master.20181113110342","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ef5283de3d5c662d5efaa18466c337b13ee2532","_id":"@atomist/sdm@1.1.0-master.20181113110342","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EVsdrMnGBHMaSFKTTvWlF+ovcDOAWSa8aRVTB+AqfRRLmvv9G3Hho3p2Z6F81jJQJTeWJlelOMB0b3QXNLMbxA==","shasum":"e86c837f6b29cb4545a8a6896453b06688dda1e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113110342.tgz","fileCount":678,"unpackedSize":1624089,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6rBZCRA9TVsSAnZWagAAR5UQAIPTlAeo2LiZ18FThM9L\nhmTY3DxoVmslUF/ntYext+Wa2JOVwZKUXK0xfTfhWJ/VpJzos0CUVLXnFxGm\naVA/L+r6hapKF3TCyEBTtAV2ICeTMBd6TgXeZJCukvnrE6VO3RIiEzc7tMf8\nt4wdRL2sIzWVDM30TbklbFNuG0hdpGTxWA84lWP6E/zwQKEt5YIulMf9TCti\ncEETuR2pqxBtB5TPEz8R4aKj2tT4rDjzeTnq+fV6m9asqgLEEfTaYwINsR95\nZ2t5oV4WCd2MKQQV0nliCMnQEE29Adqd6nrIu5rZlCE5ElTL/Ht+IptylUM1\nM35pCmih57wLvqctBGsOsqq9nv4XIbhFEUFvQQD16VQXYrAtre+9+FBWX1Cp\noQENg+r03/nwFuTbE2a5JgJrUJ+GXoRC+7R4qKf6wXuHz6Rd4FGVIXYIoWns\nZcta+JA/asHIqUdYvsZ+GdU/87ErPBP2KU90giGuGTzYz888AoyjpQKSXV/Y\n91ad+B1cOtMkBo68nluNrEThxLDqEsQ5MAQsH+u0xdepgW0FRTD4bK5cbJzo\nyOfn3VMAMp/nSTSPDAn2MwzEzdPvtVqBisOS0RNWP3f0XPK92SDaAlJEQQPs\nqeA3qLq2Mf82nTNOAxNQwqpgvCcpYZeCN5bZele4vt20rLwrZ6mM78hpou5Z\nFo2C\r\n=sUBV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDjJ7w/Z/6tSXARW6waIHsHRvREz/Z+wABJtFJ5jbYOkAiB4UQw4tl3fbfXRVAHSy3m7ZBSR+OvDyKNmNLsnJxmJzQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113110342_1542107225181_0.2815822049595187"},"_hasShrinkwrap":false},"1.1.0-master.20181113110510":{"name":"@atomist/sdm","version":"1.1.0-master.20181113110510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ef5283de3d5c662d5efaa18466c337b13ee2532","_id":"@atomist/sdm@1.1.0-master.20181113110510","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jCxqfCo+U9hqCHkkoXUTdy4A4PR9w1c9WxdBf/OasaNMjOQrY0SUrZM1MA7vW5zf8jF2QHE9N0F0qqoFYOaNsA==","shasum":"4d25703c434fa4bfe7962e3f573298c46d2909cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113110510.tgz","fileCount":678,"unpackedSize":1624089,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6rCiCRA9TVsSAnZWagAAtMUP/1akp62CbqRKCfXJHa/J\n31NfCT5+a81Hbc0AkB2YUK7b2YqI9b06Cw5XowXM6PmzXogmdED8cmZwYVa/\n154WzQB3gvkbF6b0lRbONJWBhDmXCTaZoFPpuHZexYqMjwjsDRWgSKIxiSic\n0Otjg4/EiUTIqiqzQCc0vuk5ztdAqnXawgLpPrbSnLHnZvRJitBZQGyOVQ+K\nr7BkK7XMr3IZgAvbCYzVBPlRTU27CU5V+jmA7QEm+DtB7jddbZ0uUoAUO8NY\nHMUXh8VwjPGgvQhrvHwoW8ReUjy4KNqiRZD+7WUhGaztmuv3feTVskfKUoDV\nOlQua3KpCrNm8SH5ocwkDM55oDrFjSjO17pJWCD0wl0t+zA6FHMSRAqYsy4O\nU8Ly4VNsuodJHYzH2S/fn36FgR5XS/5q5II4B6QlKeG1TFhL1DJoAcNqF4UJ\npQTdqiKwJjQwwya6lP1qr8Z39tjfA/3nUr5YiPvB/poeCh0JUDnS3hFxpiSc\nNfjsq8B9vjmZAxCrn7prYpO/Q9ux0fhIbzUlXlk3vzdLccO2dnsQ2kz9RmC6\nGFc5dfzUVKUsmohk6xRSkOVVfR+2wXX+Qrg/RBsmgUEGP74sTqaKcKHbq7zX\nxzOUDsNDJQ4bGo9kTKT+A14wM3IpLy0BOop95wCLnf+QJ5MJk7iZpA2bwUOC\nKNUl\r\n=1gIX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCKBHNPsGj7O+Psn3RonOUdQ6MAPAUNAS5gvcQ69uF0UgIgPNl9lvMUfCbxf8JRzhHQWKBHn5oz1nrp4Sy4wRm6erQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113110510_1542107298210_0.9542002940186196"},"_hasShrinkwrap":false},"1.1.0-master.20181113165254":{"name":"@atomist/sdm","version":"1.1.0-master.20181113165254","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181110132646","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"692832743180ec8e4155cc31fe3baa9fe1476f1b","_id":"@atomist/sdm@1.1.0-master.20181113165254","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Pw6eKDqqQgtNtSaY2E0QghFiN8I+GeTmXGYQ82TL053SkQ0hjYuzvfrYbo/pLCZ/OMSrCnH116vMyZrXrnSx6Q==","shasum":"7f087f6f69bce52a665ad343a4f450d5d5092f78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113165254.tgz","fileCount":678,"unpackedSize":1624456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6wIGCRA9TVsSAnZWagAAVdQP/0JpG5si0W2VWzqXbHwi\ndR3gNL1cHc/aMWpREIdfY+VGMOo9m615+vPGGdksO/jDajzTTSy7Kq6rdA2U\nPWVDrLs3XHLGg8vRaKTLgBlrumkHbS63NkREYP4c9V+BNARYzVWJnqbh8/ex\nlnkfwMn9u2W97kYHEdygUhi3z8mvmLm/EcZ1I0fU3zZ/aeNqiA1ZVUHmia7d\nPZT9nBH3CnYdnf7uLsu3ZKvWwmvPQdqCRwrW2BG6Ad5NxzYgJZG7AlNQUvKe\nFLe20PhMD0vR27LqEU5Zk9SjhF2/nSbFihSH72XU0hm8Lrf8+St/MfLtjnhb\nGj4k1Hv/Yye2gukzCEXeWqX9/oYuRbe/7MzfzE9hq6au+b1jvRc4Ksz6ChK3\nz7Cu9sosxIVlAy9lTfdZ4n4EsID7gy1SuOnsABES+INzMRynWGojjHzl7ihm\nn7p9LSd+LluUHpggPQI45ADLgiTIAV15CZtOKJJA6yD+wwAmUCjo/iypjD1U\nCg3enyFBG9x9PgLqxJRzvCQEuwNCQQflBZWun7mhY03DxMI2wGwjOB3n9vuy\noOptC4LxF//KQIQ2ycLUHlEpB5WIlOnDidwFhKQNN3KD2/3mBeFAP68w2tbC\nQNhMHjGUacZxvpGqdu9nnY0hDtqgaU+v1mnpZ1P2QzmBWiyNYzi/97JCsuw6\n1DQp\r\n=SHGU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3oyfQtotuYAP25Cliq21G2N4SwIPb1jbUzub0NyxO3gIgMAoxtoMJP1MxbQyL3dXTXZTwn+TVpqmF4L/THBTFCYY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113165254_1542128133191_0.6565057815843118"},"_hasShrinkwrap":false},"1.1.0-goal-review-listener.20181113195827":{"name":"@atomist/sdm","version":"1.1.0-goal-review-listener.20181113195827","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cdfb38de3b467a6e659b467779a46c054800516b","_id":"@atomist/sdm@1.1.0-goal-review-listener.20181113195827","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9Bw2hLdXt7RJcja0M/EKLzUFbQCRGj5/k5kEXE6qazmve0QM6CPhRWM92Cvr6RX31KIeWBx2vrXqqtq+tMgAmw==","shasum":"cf634228e517a5e2547361e2ace8950acf569480","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goal-review-listener.20181113195827.tgz","fileCount":680,"unpackedSize":5013143,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6y2bCRA9TVsSAnZWagAATAMP/iv0uNn/a+46qwt+k23l\ntLstP+/6UeTGPr0e6CWaOJ71qpkS/cu7kOdb7ZzaKOZdZXFbubymr6u9CjQb\n6KjMBPE56IieHg7Sl7ZdFekzEJnUvfxXHgO9GqG6JGneMNGhOOdGhgI0PW5U\ntDATWNf9RCj14nELp6ZY5Y71wtMJpDySDq4DQ96UDJcJEZq6Iaw8qd1HUH8y\nw4ECrmPsl5clP2Irtd3yfcIdsGLZWOsYTSZuSwLaSmT3wNpZRSa+UqKsq+8s\n4mz9+jZ2/InvwOtFlmuuXzoTntYn6hPvCivWwlMYoPbw9Y0FK6Q1RLq9+bEs\nrpksHntoz8JCvHA/bZzwInc1ubu+paA/ad0sRpd+zYFPTw6HOPJEkFQRPsR3\nAN3p8Tl0GCzoMRwQfhOCYn8w9/ylIQTpTZF4huCrVQFBK6Z3AWY5C/zKoyVG\nxbFwRRfcIIAZyECx99lYz62E6httEGjTO51pHCGpfa5wHcVlSBGTTZ+6qF4D\n8PjDxSO1MrWfr9jK+omuzDvdnxPv+580dFjFxzGcNkGgfvEg0gARoUfdfIOU\nOhahDSgruavz6D0aKsepzQSep+G8M6fI+u7ldvFYrZ+1xVi3eMO2hbg0w3sC\nDPSgB2YtXi1zf+omSGbPMimBnO8+iUWuXZejLAn9khJsTekwZlCjU4WoIMKn\nKyH1\r\n=xuiI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDlRut5uDgIBgtAcCVEcqpPUvVcNWoG/qT5fyA7VponbAIhAPAmDM2Zma0ys8iDLLfYssGmMqnVRGfS8YIIVnEcy9We"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goal-review-listener.20181113195827_1542139289523_0.06995767407972253"},"_hasShrinkwrap":false},"1.1.0-master.20181113220303":{"name":"@atomist/sdm","version":"1.1.0-master.20181113220303","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ee6663911e657dc9d4f1b5fe61173c8ca79e6037","_id":"@atomist/sdm@1.1.0-master.20181113220303","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1oV47ziPEx6wD8JQhszzSmdmpAy0MboP1nviAhpe4MJzeISFy8BMtxSPpO6XsVvvXYtUFRNaTPZ8Z+f4Wq+1Kg==","shasum":"765ac177772ed86382016702f1d5764933817e8b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113220303.tgz","fileCount":680,"unpackedSize":5042707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb60q9CRA9TVsSAnZWagAArOYP/1m75/ScqimVNzu199ZN\nCE7dkM4wVqfVZthbMrCePKecfX+YF7UfICTMkNmQGn9+jJEzGMY4b9rD3R5G\nymfZqLklWyjCxjWFNBB2+FAIVsXf60LmlNPSlczq3MAHy9lAtGs/DCQMZl44\nfpVdsjEjs5xDSLtM9vNzUSIXMin/HykKV3ljp+DMLPSvZ1snCkwFhwNJPydi\n1xOi/M/YjIwer/GpjOGoUGXJ/3B7oCVd+B35OrxW+a0mspXgS791FfMq/F9N\nzPD4EgbyIeUIxLDhHVtGeCZDuGraqGKyDp/R5uRY1oFU68JLrMK1Ih86yNh2\nznHHFcax+7b0bUpmhwm80v4vzGz46tZZ1xgQPxBykO1fgEac/9uNMs0FghHx\nXGROjnhMHsH+Ku4TEZxsKbQyufHU6prab3VhoUPTScApacgFPdw2YXOCwypt\njhg8kb5vwWAjULQtxiOyjywlxNDoIMgosyoI1fQdawxLWAUzyphURYPxtVXD\ntkdzlciskchxrPBpPSY3wijSp9ydtcUv2Efm/qbiBV/t0TBCN+4EzEIoe2Da\nVqseqCXXzyeh6yIJzcEt3defDr2McBGKlOniN+HdCp1szwaHr4/fbW7gCHZT\nW9gzmZDBmfKURmgYbSqHw/ZNaIy0mW+fQnMT2GmpbskNDyEo36sGJcNPteKH\n4P++\r\n=/MR8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF4pyS4N3PP2dYbk5MJrDe74sPcrXdsOc5ZQ9IYUoCSdAiAlr+epxa5p23PrY0O5E30n6A7AOw8CdRahbLvyvG29NQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113220303_1542146748224_0.9927524706033468"},"_hasShrinkwrap":false},"1.1.0-master.20181113220448":{"name":"@atomist/sdm","version":"1.1.0-master.20181113220448","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1229b73f3e1e229f2b701973670e7c3f52d866f5","_id":"@atomist/sdm@1.1.0-master.20181113220448","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HG6yHv2WU6KqNN7fvbjKYP9Q66/YtxkkyLr58Uk3CaR/BZad04EqJvF7lhFe02+TSL15zdof874UoINe8XIPtg==","shasum":"bdff425131a71fc4e446b4f45c9bec4b86ec8d65","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113220448.tgz","fileCount":680,"unpackedSize":5042881,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb60sbCRA9TVsSAnZWagAALdUP/1lPcUToniQGI+yCCuFe\nLUHxmoRvE2DIgcLza7G+mFnu518QBV0K83zbQdC8xM4YdAP1DHAPwif/qeq1\njX5jr3Cx3o4Lcbyn9oSSHXyyyuouQV4xWLbXljVUu1AEn+QsGGXgKtamAgtV\nCgLRwcS7kywYMSfYH7c9xt+XAN1CJZZihAc1YTlP2iruOf/6+LRAvhJrgU4s\neIqkAP/wK/pACzzGhaxosWzz54G++j6nl7ceHYvICe9hHeQeZmCjWwfQZ0e9\npg+6YiorIsALw2QkB2ATG7iVKwT0B3AcfcKaaig/e/RbPT4G/qQd+tVloa4O\nU8EO+YATl45550hXgBZi3xkLn+ZhAd5Vw3dfH+7YElLhbXTVqcapZTn+7g82\nKEZwfpvhYJUft2yF5Fwebi+EszcNDkQggp4ELRZHSE0UdlBCEbostPLCQ/aL\ndyyiS43u21YGNmW4ZM4sv7uAzlG3SUTzJTfs7RFhtRXXFx7wyti+9Ls7erW6\ne/0poZdFqkzfHxyUeQ7VCtCaK1uz/s+fD2ubbhUfl+/DEUlLsNbpQUv0Faq6\nw5qsaBhIIU2ppQ7fTkpp/OhRYvl+cWm1WLaclKdCdaDOXOaaQBXIcBRPEEvI\nSCJ1TDiLWNBefa4RS4cEmrD9aft2XMHmHLTPRIbMiPw3L1RwxaYWBoyzhz+n\nikwt\r\n=W/oB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzcTs3bJL1dbqaxJBByKIqwYHU385KvTx3Ffc1dn/kigIgL3END/aoRwFwfeo8dgVrVSqZOGKg9DfKkfEsRI2fvMo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113220448_1542146842199_0.4723678546545207"},"_hasShrinkwrap":false},"1.1.0-master.20181113220725":{"name":"@atomist/sdm","version":"1.1.0-master.20181113220725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1229b73f3e1e229f2b701973670e7c3f52d866f5","_id":"@atomist/sdm@1.1.0-master.20181113220725","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-URl63b85Jcqy0T5A8jmpDmVLhfu8WfCHvCtK3AmVshZir0HJ3vYNIyXAuL5o9k5hi94qXTLWvHGZaH+f9O6H7A==","shasum":"ef22e476b6c5ae5b3cf2f9ff963733af025280d7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113220725.tgz","fileCount":680,"unpackedSize":5042881,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb60u2CRA9TVsSAnZWagAA5QQP/2GyrNBNit2I1Yz39QQV\nMCLOmIwU09d7sen6fQdZdW2mmqPyefJTZiybHSIlYEU0i9q7PNAUmzRiPPfq\nhwiwisxuYaIyLZs4ksKW2PY7rhun7JLWKrvh5346X7oALg1FZA3s6a1WouAw\ngfS/3dxD/MnL7N2SKKTloKNSJ11nxtCZtBkVULyEh1oHwlYaqFwkaTwVblUj\nHZYQM4C/GHF80cLVR30jK04x0F/H84FHuL2GwMbnmnIYQPpKEPMoZT06qpPo\nJAC97CCw/ZTxoU8cnc/c2WBWMfNbFZ6a/ybSOzpvF4pUTVaJNktkNHz9ub3j\n203M3yIUGyyCPhjKqho3IhM777g3U9UfMLo4GUxyHZDR+McPy8G10iJVAzAx\nuwKa/63dMehHQF3TowNfDXmlM4HhihUZRvdkqAABwq9PXkd1iWWnvg3iIekf\ngh6A0xRSkLUVL3Pl5U4GHWTQXPwQoqVQ0aVaGcSRBOyziY5Sq8Ia8JtjcwTR\nTi4lAhkRoxoteFIfJQD6nVC0ZfFdTNWSIwY8VeuMBHC0Ux/y4aolG2jEq5yy\nS2knKipe31ZjmsBdNKT9YySxBsouAi7OTKbETPsBypTuPHui/RuTFYVHUF8/\nxi8Yg/NOpuNs/7kwnCb36uk81SRzvFU/t9JW5IsW7gpRwHXoYP8dvlOlEXIR\nXc8a\r\n=yxuB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICcityDh3F2gMkqhJVrcdfrKN528uJAl6uEAgATOo/AHAiEAsP3FhOGMCIf75vpUyalz4d0v2/BaTk2yj5KVXphuGXI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113220725_1542146998088_0.4836287933305179"},"_hasShrinkwrap":false},"1.1.0-master.20181113223011":{"name":"@atomist/sdm","version":"1.1.0-master.20181113223011","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0a67c3aea894e9b7d0d46fcd8dad1c6797ce1f64","_id":"@atomist/sdm@1.1.0-master.20181113223011","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rZbakDDAs+SQjvcHTwuKYBjG+6D7Qua2n6ZSf0oE3TX91V0haU9e8GnFw8dWFFEHTXfFsOU20W51C7AUuL8K1Q==","shasum":"9a9f6fd2e76d51be024a27bccdbe5392b56dede6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113223011.tgz","fileCount":681,"unpackedSize":5373727,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb61EvCRA9TVsSAnZWagAAlRcQAISOvfQZKqINebMxCcQK\n56IZ1P8E7Bfkkh734mA4zURk5zz5H5jUWkIQG/aav/VbKHv6wmNO737U+iQy\nMoL+zgBbgtX6+FNUODW4HWfgxCu25lJnn8C8GXVGYiNMMRVw/yOerPHDPBpC\nQX9LknBPYLGPAliG9pQm2uajUyijhL8Gj/1GmxfG+qhQTl/5ENOpJdQJQ+NF\n2HY7vLl9uPH3v99J0C9Op4CmA9n7fOkwM8cAsOKBg8Yiq5K1Q2KQfcQDRpkJ\nHL58sF8laiM+Z0PS7TNST+ZUPdMgPMD75xPJVXM9T7RZs7eoplJOMyIrBIUe\nkCGSKpZb/pBJCERJ3jaid5iVAnaSVWaPu8r8iGL3AiOuFgneG61Ip6D1kNDP\naVtVT1tQQ6sbYLo+LBAhvVlyoLFrG1ZRBCJg5J8jETVacJjIHfCBTM9dfttD\nJ8POC+AW5MOf3xpTUINcmNPYcKi2MjoTlDN3co6QvdwZO16nWrCq3nQab/US\nN+aF58LSbY9rbp4wLotmIBGp7bXwakDFXJl5o1zxQFDz5D3byAjnMi6XgIVA\nLf+yVgpW14rpLWtQhmDiKn9NXdMGLXz/WH5vrvbjqzNOJZ9JPMY0w0bxdZds\nzyN7mU2JXP2KW6ycH/9lJ51Xn6lLYzAZffdnkcR3aH5h5mFMECiss+7W8v5P\nB6Ev\r\n=YB4B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCH31VTkf8++cJMpt5W2uypRPGoZV5VtQdyDjUXaMYL1AIgVpBv6r4u76YE/cad6ZDANezaJCF1SiWkU77P46tG0zg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113223011_1542148399232_0.8479342235175475"},"_hasShrinkwrap":false},"1.1.0-master.20181113225717":{"name":"@atomist/sdm","version":"1.1.0-master.20181113225717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0eadb0275c13e20b6558a4e8a1273cb47fd167cb","_id":"@atomist/sdm@1.1.0-master.20181113225717","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-iSjkPrt4yZQZeI3TAO0cl2ay+AGtA66kyegTrxX/FTAh21eHzLyKu4x6UbwF7QzDNEQ+9pK7EC+AH+0TcTdIXg==","shasum":"c35f742b86e02cc54c283046864f8ffdda2264e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181113225717.tgz","fileCount":681,"unpackedSize":5373694,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb61dpCRA9TVsSAnZWagAAIE0P/0nzZu38FI9/K2dgB1MT\nVzQciefR05dDQO/L7HLSykF/dGfXrhteVmU04C6fuwKHEJ0HJNd/nHKa+YHO\nUPOuwo27tfabZ/k8OvVnbRXns/sN9wK0sKHeZo7VE/Wrv4yaz3NGVIcUHFLC\nxU2P4sB4QiJ7Sq6CRRm25+/Yuy4CdJohruJN5c2ETrcyFOdxti7o/YuhZlnJ\nPSgOjDL/6zCumT1O+UhgRKEWsEwKiR+rZagPtbnVwsRovociGCGdS16w8sGy\nVhyreXab6fzcA/9xaTZ4zA3J54Wjm/aRbjlg3pzOanELwQrtKXY4AFvLw2lC\nUXT+wd1bjO1jvx9310Fvh22HIsSBH5NJIP2wcb5ymzUiHPwbX0qFhlH61V8M\nn6q2Y5rb/6PfkJYOUDSrutyQXf4knNQb9O309oUhR37N7IeG1sQq9lFnsMod\nAzaCDqkVNpbPBVqaKw7yXRrhtkT9OpFT40HEbNOA7zUeOgjU0Nas/6ZEWQ3C\niFcvEqKhNEvY9k5FizJfrlux0VLs1c8Kr34Tyk+z5Rf9YUtoi354+59hQFjI\nxFQ/1O4UdhYuk/0wfJ/l+vqM7XJuUF1v1vl2EfWQWN2upz7N/WL6nsNtWR3p\n5pLm7dsCRnEiDGien8gBRvvQc7QljMTM9EU6HA6hVWHaNID0AX3U14wUflEU\nyOs3\r\n=oYkT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCaamI7GnINLPEb6Afxo8waaz4cYmy75KBH09/+5MfEoQIhAPbcwiuRehNowDxbbOXdEy6ewjUqk9kModVXPaY5G1dk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181113225717_1542149992430_0.3003568831860839"},"_hasShrinkwrap":false},"1.1.0-master.20181114125422":{"name":"@atomist/sdm","version":"1.1.0-master.20181114125422","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"02a6461757cb792771a327cdb4940789b8e67548","_id":"@atomist/sdm@1.1.0-master.20181114125422","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ImC0D1B5eH7fpO21S1/AvM+nrnR+6N6Zsj7E7MOCLuI9TkZegQ4K39rJ6OO6Lv8LJxf29+ErsaXXm3Un95zcFw==","shasum":"f4e92a984a958bf6b3306fbcaa7746d4afcb1ef9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181114125422.tgz","fileCount":681,"unpackedSize":5373701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7BuiCRA9TVsSAnZWagAAnEwP+QAjsgiyv9tDuXSHeXxO\nLI8SHHghoPJVAokYTmNmNeP2f+YE5PekW6x6JkON1CsduMGO8iWOb7eKjQts\n58iK0v+dWY8ydY80111pnuaxqgd/Aw8WuA64UOJfEIunua161uFizoWWV3IY\nAk8zzpZS/vmrvh+YwXJ5+yifz5ufbwk4o4gqnVIuybnn90MZ3w0ic5lrIkef\nnMC+9xzRisz+mKAsdE5H9Nfn9mal5CoVmI/w0pCf+Xw+BkFT+f88nf7iZYRr\nTf60CqWusbj7DXDnUSmeuDYAraV4GvvLsXhWu15R2yD/Li029bUGaNCTDDew\nld5CPMkIR4yKf4mdlUBp3RHRVxUfcmk36C7GgNG7Qo+sO5QK9dRNR3J8/ywW\nPKNAkXjsm97bUuwgSFlxWhzgKC2pmq69vcMl2eExC9a6gct0ojmh0QX0gZYs\nQoGZcp+Xd7lAmNBtInlgQdImcCx+jfnT0CuDBeQpHlPqThZ2wrpdxTf9IlzL\nTUE9uS/E2SfGWutHa6JAPJw3jy0HMqN4QBcE4cXYp36Pbg4EoDypPKELFBAi\nk5NhXQBYN7OXBEBcezXBqjcWy+kzWISrXjOnh5XqmFrRc1iwYPMnhMNXHFJZ\n/GbROH0FD4qxVzpdKOFAsN44reBREniVOa+d3DyFCl28jgUnIuEfcFxXCzN1\nqxHu\r\n=+WuM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBW7244/vglzMayOnH9Yr9ejFyfG4+Qy9ORB4nzt49B7AiA7E6hUGfO+kdnbMabm1WYkrERgRMsjLtKHZgs67x/l9A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181114125422_1542200226087_0.26679009612065996"},"_hasShrinkwrap":false},"1.1.0-master.20181114141651":{"name":"@atomist/sdm","version":"1.1.0-master.20181114141651","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e7c73bf84440f2a5f9c2ed0bd9289548eef1d263","_id":"@atomist/sdm@1.1.0-master.20181114141651","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qCA7i5G9d8pmC7P4NG28DKNQR5FzoNUl79iAesUiLHAMCWR+4bVJgndSB+LFOYaA9RTMRemVEjYzqgVfKqTZZw==","shasum":"8a77a72dde539e63c3fb0b502649decd6ced3a37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181114141651.tgz","fileCount":681,"unpackedSize":5373740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7C7rCRA9TVsSAnZWagAA6tEQAJ38K9YluTQQVGsdlaj1\nwkIUNTioU8zWpYKrx+D27DZ4Ui3VwkFjsfEprhg63CSnGLX5EVTi49G7ytXA\n5CdhVA+AxssDtdfeQe6ny5OibzSgil+1gZ74GCpUTl/3YgWZsXv/0fuZYMqN\nqbTlTExpgOEf417gszzR03Pr1/joljcGAVPSMwZqjTUMc0G0jFAn+1wc0BUC\nFex+uYhnz8dpH3P9j0GfLDvuzF0izP5XIYaEt0QhcqddXPx94K6d2taybqmx\nOEt1bsmfgJwHGIq+gNdIIlSah2nMsTOseRfov5Z7Bfp3Z8UB9Ijz0zvydU+y\n5fR9m1HkMx/+55t8vjPuKQhHPQcjdcGTcba5w5AEhCXmph6ocX/jR7YQl1tE\nAqlH54aembg5i4GF5ijokewT7sOCJ+1mT+1d4G8zXFxjE0c60hSb0XwQmLDJ\n3XoD2SRQxOalU9yZLxaJwToLaqF7uxLJnWQovNTHYZo+FbZfVWI6chKMzMfh\ni+yrIvg+fU02tKwePqfnzEk/X0TV18Up1jYrbzmITKBiyzgjFDFwMF8HNUMr\nvGYGGU/m/h/uoetI0VnfH0Ea292X7tRgy82Z/MuomQCACRdZteo3LibTNcur\nhxAZEGCUSPw/gFtbZk4Mvh3vrinPZm+QGCY9C+C7RtIXeMvdQhf5vloFo1lD\n1pko\r\n=vpIj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDzgCC6e7KMERfrdYbr3du3GkeQx/qrn4LRmMv/7EF4LgIhANXJzWXgwiucg7/ZCnOObWgPrnSC2v6I8VZrrusBLegt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181114141651_1542205162328_0.8562376551388744"},"_hasShrinkwrap":false},"1.1.0-master.20181114154353":{"name":"@atomist/sdm","version":"1.1.0-master.20181114154353","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e7c73bf84440f2a5f9c2ed0bd9289548eef1d263","_id":"@atomist/sdm@1.1.0-master.20181114154353","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-PKD1OTaOv8OO0ts1I6jKxCmd5X9b4cg0oNZoy/9aFZBJyyvTLX3gKBz4egLO6aoQqOJkxUMLJzdehWOR9KxyUQ==","shasum":"a242fc093b0f0b4f73c928e9c7ca778605bceaf6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181114154353.tgz","fileCount":681,"unpackedSize":5373740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7EPmCRA9TVsSAnZWagAAXrQP/As7D9xaPFZaSM8jL3TU\n74dluBBQgp2l72fp3vDOipTG6JnTp9hplgMQd+cujjDLSzd2galyGrwOQPSR\nohegbuw12WnY2FmqWxi7C4kpLe2PY1/ZfRBT/ETfsds/5ETkh/qRfzcSy5cM\nLP6EB/BQUlvNfgoR4zcXmCAQW0xUCB/MfnXfk1PghIvKB7VBo/ujVzdQM83l\nCpMSTzJO/qgeeZJTNjybAnz+FNRTa6se9Ghf89xYh4/5YuFlJrhDTW+Xqagx\nUKDbT9FhXIibP/v2nip1XdHABoeKFNElpxRxpRZngzM+rFQepEYoV6/oGnwx\nD1UKXsTMJSz0NgtR1PxMvpUR6i+NpK7K2SdIsGKi0K4rCbScAj1gB6xHADS2\nVTFquwEqgv5i4vXLWhfe2GmNm8hCh6nqlL9dOCNtp8rsvcvFtu/XxVMUJM05\nnHuCD5EBgVbNcD7v8Hmg/qQ7imsvh1Cypuvd8E9+83ohLDSH6HXI5KXbB1JG\nqynT/cfVbk6OstMk7QwBMvnmgODanWNnjOHEggO/eLpmV/zcHIJPsZA/DHl7\nlGeza3xTV18UnQ9/jMLS46S/W8VBWGoH0QVq21TFLYfWJU5ZPbBnGi/zi+I2\npAoBs/cHyDHRDLi2muBoIDvJ00/QFWJMrcdeIOpFc196fAWFp+42YRyymrL/\nio7L\r\n=2Na2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZxSqzjKP42KzJ/EksMF5Osd3Puk/YeaSlQlPPcurcEQIgbGqA9vmmiqxj8txPNsUuIw0e9LoKYsLGzI8fEnnhYUE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181114154353_1542210533287_0.09645166313034892"},"_hasShrinkwrap":false},"1.1.0-master.20181114165510":{"name":"@atomist/sdm","version":"1.1.0-master.20181114165510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"55c9bd94b86e9461c64de22dd45fe06367b57e3e","_id":"@atomist/sdm@1.1.0-master.20181114165510","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OWrRoBftF7UWnV5bCjDS7zdNGwRAgAL3LtjRURUZpoUX7gvhz36dI3dgENlLYg4rwetSlyLC3U+kNup7vybG7g==","shasum":"f40b00424024aae522df06e72bec893bd5515780","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181114165510.tgz","fileCount":681,"unpackedSize":5373978,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7FQDCRA9TVsSAnZWagAAvrgP/ibkjGkTL+2LrhRElyFA\ngdYtwJxwDn/TtSp1tYktN9LNpbym9CIhxg5kFL59I5f3SRTAt8Wg2PeJeT4s\nfO5uQprn0zQ/JO5XD0wOumdaAVxt28ReNEFCLXtFBq9w0MmtK8Xq36c8A7tQ\nPOqFxcJGX5UPTjAswhO+HveUP7OHjz4dgx9JYOdqMvqQ+jjSzkBrfYDH6FPb\nShqrvxC3VNsprwUiDezWzn+4BFCfu/nKETSdWrbYdpEemsB5M4S5MCjf5MGz\ncKlyproZt3FPujFV+AvoquqVyNN/bC4+uCktkZ40iKfLH9oIiTwRN/olpS/y\nSb0pMvE+HMsgjrTVLcKuI+5pUKcNkhNbIRK7QKwi+t8qUI4qoU02MManNfag\niGid9A75/TqarKaG6xvs50VCQVyBzpJu2Cuk3+Anj1drSGq4dDL5rf+Pbqo5\nyBIICDuo062Zwi0fJoc6bPbIeZ7UzHwdRu4pn+HxOg46d8sqipfyH5hgHlXg\njy7764/S1q8Xb0WTKDw68+VDjyVZTfZkQL6WAzFt1R2iQFpaYp8X900jgLEI\nSupG3KnaGhGaDjS/AIlx7MlamFblsOIPIgiazsFG3AKY8JgfZ7ylvrm//TWd\nvugFmRIxjaIuClw+PKoO0F31ntMg3nwn9C4j/QCiL9oM6a0ofTakMF9/smnh\n6i6F\r\n=j6V5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHuqXxWGKXWRrdVeG6OrjC60Q7NUs1NhC8r1ZsSk9MRnAiEAxQ+a3dknA9tZ0bMdkvBHTHSTwyV8pJVXT6e9dRHo1js="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181114165510_1542214658461_0.6965581385042365"},"_hasShrinkwrap":false},"1.1.0-master.20181115095747":{"name":"@atomist/sdm","version":"1.1.0-master.20181115095747","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e7e933ea43d1695ae7157594b59043252970c850","_id":"@atomist/sdm@1.1.0-master.20181115095747","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZQz5mF4xAsPx+cfSlQpZ7h6XywggS+0Gh9hMvXwFerxMzcTnhUVyuBdP161xQHEBLD4d8tXEKKnd3wHx+RSGww==","shasum":"e31629bceda2500c948e49c3addd09b112508923","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181115095747.tgz","fileCount":681,"unpackedSize":5288908,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7UOuCRA9TVsSAnZWagAAovIP/30iOsQOtNiP3OCFfBmQ\naQ781yFUgbJHwH/Dp4RermUiu4qeZQldi8oVnCTTBqiJuxO/GNH1dRNJCUTO\n19dzjF4EAEEA9cw9yIeoXh19DA5vsQx5dJMHLwRykfpI+8ZN4SlLGe3lB7Zw\nh5ltcuxBdskM/rLl0bf3UbGzaGvBwNdZMfB2GM1L9SDeJHVWJADdcQiwsCSW\nT++cFmFLkso2S05Id8JDo7K/ZTA9UKpAAx/7hUJCI7T6D7eP2JrTz67kQSnN\ndaIm3jgJuADrS2fyTdYZOj3g4+5tzwsQ2O9JuWCUz+bGMf79HJzd5E7iE9OC\nMni+vQYxCHb//fT9Q8ok7Q3mA77e6/VuVkfYCTMGgXnuvT7Xpccs1r52+H2r\nHOB4Fezb5+Y2tjvSa6sj8AoGF6xJ5NXUJeTs4hjqIiZzOtqx8ULJo1yOKhJO\ngW3vU8njpYsKCy7c95dEs5QULzj2wv3umnBjKa4TQeqGpkVSW/pV0b1Iuaar\nj6rt2CenHQ8fABkt2xuGJGQ+R+/bz65SyDwT3Af/JMPkPlFCKzDD91wb0km2\nw0S/IBSsH3SAdEI7bcwlqV2gb24dub0Nyo+uwOurzBbBU5q8IC+pJCyAmWxK\nI7oiWh2xn3UpJahmim7e2hK6eTNaDcj0C6aO0wI9JuxfBQOCnFXFyjPHOYrM\nkVrZ\r\n=SOQa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtVkF8/55ZSqU0kAA5puUqMNLfA1JqTnGpHM///AkItgIgMh+OdqJY1ZJnoIhQmg+5MbA+04ol7KVNUc9WExUbK7M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181115095747_1542276014033_0.6198680520959501"},"_hasShrinkwrap":false},"1.1.0-master.20181115110328":{"name":"@atomist/sdm","version":"1.1.0-master.20181115110328","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f87e78937143d66ac26830f5e2555712f3d99e8a","_id":"@atomist/sdm@1.1.0-master.20181115110328","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1qQ5NIvCVN+L7E+uKtsd6Qz9CPTMvW9JDIhFF8sYuvfOgZW7PVN7LPQLWrkUJr6SATQ4/D8eFN0fPy2fEjumeA==","shasum":"66f1c29fab3783a4da22e1d9fab722a3cb5ef427","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181115110328.tgz","fileCount":681,"unpackedSize":5289411,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7VMOCRA9TVsSAnZWagAAL4sP/Rs2l1PJzFGz03ce6F2b\noDAQTuUrBUjwQHljwyJFuTz8itRTPeqdFPGIRc5Z/yhyzWQHLjahs5OIG4ov\nx232v0KGKDErKbETmyQwG62Or9T7oQQRTZnIzEniteQ2ngxoMdUfbAtNm80S\nfyg8Fp+AW69XY1WURkB09OgcatEE7tHNxONu+NeMVTdNiHqXRXqE/dBQUHOX\nGzdN3bQRAc3rQm9/6bqT3yXLqUudVOt/cE8WK5V1V3BXC2By7grx+dy86i9L\n4G5DCTkubfsSMQYhnI2vLEbiVbWHbuCLLDPXv8si+wT2OTXhukK2aeGSRVxs\nJ+MoSgF8qNYz0GPPIllcO6Aye01n7xZ11mRH4PiV2OFcLHsHeXO8QNRSSyPh\nYftQB2lbwsr70xP5C1nKSC16JD+BaE7ZLMuXmY2ZX/PxYMG+jX0xNbVdYR0u\nmv09FQjeG+XSqH4VnW/6yELowAntCxKKmAXNJt0ur244MXbRMhz9wdM/sGGq\nd7PvNNNOfC75pVZmM8/z3gWqwQk+AGIRHxP0mkUlLJTjz593X6b1DqQbc3GO\n6n6KljqViHY5QRmPN3WY4wHwo/ihtzR1hppIPZcp7DyCyEZ0hARciLWsPEMk\nBH47YRLx6CEnWiegjYEedPxCSqtAMbjDQ0k/NrxZYpsZ8WSbBSzoZstruxHy\nXPHK\r\n=RxWV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEGbbUHJA/NrACSKqvJTDH/d3WF9egK7fJ+mRLrz+xIqAiBl4nBGzoJvxGV/G4/JS+nWBl2FD6LpI4Mm/Q/QVXMcTw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181115110328_1542279949548_0.3541080260413856"},"_hasShrinkwrap":false},"1.1.0-master.20181115112041":{"name":"@atomist/sdm","version":"1.1.0-master.20181115112041","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b01e992863d4e29f76a928494f09bf76fe15d0fc","_id":"@atomist/sdm@1.1.0-master.20181115112041","_npmVersion":"6.4.1","_nodeVersion":"10.13.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-JJukvbH5IwWzws8LwVMbw+riPtRBmePAuXH8O1dCSxNm6tYgO9jAw9oIbC9wYDv7wynwKYoAkjSAFGMyE7NhXg==","shasum":"eb008990dc4badeb56eea9180b59038685fddd8f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181115112041.tgz","fileCount":681,"unpackedSize":5289410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7VctCRA9TVsSAnZWagAAyJAP/jFiSwH70pHMp6uHZ6CS\nJTSKTLCa6RrAje6aWyq9YWWP4PqQbP6w+0iYDnlSp8OniInVvppphgcUn5TD\nhSR7BvXU3TDCMI4rCcu+nmFaIxn3IUZ+bG4kuY/QUJ2VJIH3s+dZO8PeY06e\na1nccAgznHM8XbwLezqxydS+smtum1eR5fUdnVKopYYmOU7gHmPGt45r1/56\nSgs1/oTigyi/uV3MbWzO6w3FZ42UhuUHR/Koe3RYQlNI0zNrhnNl9BtdcpRZ\nw2AR0PmBIpFzyHRMyOoQrUevd2eNgjdQ9HWGiCnkekt9UE2PRhaQUVtmid/V\n6Dijoz7yQBZ46zCqb2F+e3c+zQb7954I6YRldTkFCT+Wu2NoFpovowsbD0l3\n7/LSpqrrIYr0VGOHBBO/og0MYzbHNHKX+7HLRV2PqRNvwcTyaX7e9CUdERnk\n60PDtej9lxItSgXwAoRiFiS5MSLt+aih07OwR0KVNyDmsLa8eFIjhHJo+6Xr\nlK52aDnXLWjAKHXUHIA63jvwzX7D8Ojw5ynetMNhKFAV1pltEyCuUsCtSYSX\nO9Uf7NdBus3wnzAF3Wj3KiBkjDHukjo2y0HZMAvfdzvTjvssg1vfcNrFTCMQ\nhW9I86dSk1TFMjDF9MywaB2dYuEq8Cz9XnvkTJpzzx+MoQJ+ErnwM/ppETeI\nlMgF\r\n=b77+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCaZuOyQ/Sa/BwzEx+Qem4VbtDnzy7uAu3+ZJYm6G/ZOQIgfOzpIKNsHBk176+/q192J0l4+lNlndkORMLn32Vx69Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181115112041_1542281004607_0.9818575177113942"},"_hasShrinkwrap":false},"1.1.0-master.20181116234208":{"name":"@atomist/sdm","version":"1.1.0-master.20181116234208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b01e992863d4e29f76a928494f09bf76fe15d0fc","_id":"@atomist/sdm@1.1.0-master.20181116234208","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fMdJxMADSDL5BmKDQNyp3ne2Ngbi6i9K7zuUN+CFvpFhUCf7QIDybOyJiPQSm7UMFSCIQ5YtceccPMU9nAUXjA==","shasum":"e38e45737710bd98fd097d6e2b84329b0ec5a4d5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181116234208.tgz","fileCount":681,"unpackedSize":5289410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb71ZkCRA9TVsSAnZWagAATLMP/iAP0t2ww5P0A770i5hi\nGQhUxzc+qgzbzFGQJ+lqqeEAep67e5pBjc+7GAS8wam3tSqbOYpTsF70I1EN\nsfcIrPlbVAUAr5oVEuUmT0XA+JY7gInzK5fUOD2r1u1R/e14jCetGAK6CV2y\nJDmyoxVPaBFrNIu6v8u6KGm7urxgpRUJrRmnKv2Dx134IqPO/KDGDgrOuLXQ\nsgbl0XR+1Z3hBTFKZ0SDvvhJNZanWy5aknSC30o52LNjo0XVsOVWDs0HCabx\nrHIiH+V8dX0vayBOtiS34/pj+QOPeXsAr4IJ2MzshH+4iwXRz8mvn3PaJoHl\nrd7yG4Goll5B9/s0XNWVuYOQVz25MYvETP91LMqDKwNhBRhKtmKqYxtvK5qt\nICLABXP5no9zaATkT24ZwXzvV+YO4B6pOTdgziYmsvFdzj1EHF+D8qvvrLFb\nNyBU+hquZdDIgwqEO1uDR70TwQ+uQ8lQ5VgmMxO3CA9f1jR+wrrNK9quDNzW\nzeLSCTV7nahTzB2g/bh1seljaD/mjyxO2R7+btrVfB4Fl05u8Vkn0fN9EpXG\ne0P95+BAUAyIAsK848sQ3rnJjfpMYUrQdcwpMUhNijmKwRxUae446ciBHUX5\nwseEbrTM8H3tcUK+HhZkNMLLTffpIRkPPPhzYWclYNDuMmZ0e3rjZhRUIA+8\nadsx\r\n=cJC3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/gQsYIyFastKYPHxMz9X5MEqS/ZdPjegN9gKnO7ZLzgIhALoBZdFyNZJqQln2OAlyba0fy/zQZtOwGC6OZiMJtfek"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181116234208_1542411875851_0.16297563605455734"},"_hasShrinkwrap":false},"1.0.0-pushtest-structure-more.20181117002331":{"name":"@atomist/sdm","version":"1.0.0-pushtest-structure-more.20181117002331","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.116","@types/node":"^10.7.1","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.0","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":"*","@atomist/slack-messages":">=1.0.0"},"devDependencies":{"@atomist/automation-client":"1.0.0-RC.1","@atomist/slack-messages":"1.0.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","apollo-codegen":"^0.20.2","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","graphql-code-generator":"^0.8.13","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.4","npm-run-all":"^4.1.3","nyc":"^13.0.1","power-assert":"^1.6.0","prettier":"^1.14.2","rimraf":"^2.6.2","source-map-support":"^0.5.9","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.11.1","typescript":"^2.9.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"(?:(?:build|deploy)Dsl)\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --format verbose --project . --exclude \"node_modules/**\" --exclude \"**/*.d.ts\" \"**/*.ts\"","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"nyc mocha --require espower-typescript/guess --require source-map-support/register \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"nyc":{"include":["lib/**/*.*"],"extension":[".ts"],"exclude":["lib/typings","**/*.d.ts"],"reporter":["html","text-summary"],"all":true},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c7518c57c90aea24a6a0c2141b15389f8c406783","_id":"@atomist/sdm@1.0.0-pushtest-structure-more.20181117002331","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-O4XbaLO/USh6Z0jReEfBVogKwEfAOt4OgIWW57w+Kd75YAWpyt1uNkMLh1VAqG+9dZU8Z5Vbs3qdloy61NSOhg==","shasum":"0bba1107277dca61449a6cdd0d0a2ada1d75d059","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.0.0-pushtest-structure-more.20181117002331.tgz","fileCount":662,"unpackedSize":1570707,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb72ASCRA9TVsSAnZWagAATnsP/A15fjtBvt07u+UsYDP4\nh5DbWL9D8XfgW+4jbHfmWt+tmx8tQlvZ6h7SUetlgcL2tg1ijSRfqM9bd1AZ\nVFQXM4ziATSsrBWI3YM4CuPeFeONjcX4oOF/95HpiriZWHl4D39w1DQiRubg\nFPCmNh4Eknxi3xJNWD1HMSsy4+yD8A7dNkrWS2dr+7xyUEM7/GKkryXYXYWV\nd0oWu44w/k0rgcPBcTp1kOy6XxxakcnA8vO6Hprdr8lHkGxxSCU6uW0QhtHN\nCTFpLr2QKuC5ezgjoqZzD4V9o3/JdDuF7/wuvgORRg5zjfaUOruLl+bpsxGm\n8eIH/poG1AwmTqFLFHgUd2mWENvvgsAcTbQ4PplrTNnFSonTt40pdYqpo3Cj\n8pmKzIUDjxr7kKrUcDLjmNgc6EKhUiPNiYcEwgU4J0ABNPOjdcSo2IxUpCTW\n89a744tvnc7v1+7edPKmywoqhGEw/mbUdXnA4dpHdbto7s+meX3RVEHYiLgm\nfhH3FmcaOKjYsi98ItHZSUGOOptwSCsNfpsvU/niqxnMq1I6EAfSa6YCigVu\nGilTMQpPD1zU7lFD3YLmUlKHiO593wNMg1n1v3rKWPcbR/zLaXTXqSbXrflE\nv8OroAroE8Gj0Zc2P2f/NnmQSP7OCeTPaVe6LOzlx+EyGF6w/0v4fuFn+sAJ\n+ipk\r\n=96V8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICO+mFFzJG5/x038duII8rPGEpHL6k0nkIjutaNvvHndAiAdD/bGHwZXIeuUHoOZ99z5libB/FPKV6PQFMJ9f4y0Ag=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.0.0-pushtest-structure-more.20181117002331_1542414350748_0.611094991884096"},"_hasShrinkwrap":false},"1.1.0-master.20181118082921":{"name":"@atomist/sdm","version":"1.1.0-master.20181118082921","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"07a5f3b273fc5e284d76d605de742627761974cb","_id":"@atomist/sdm@1.1.0-master.20181118082921","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kW1rCOy/bOPLxjhUyKL1g3f4BGvl8wWP0Fx8UqV4EschHsXaYt1cVxvbJZbJIIWiHfrXU1F185p6cZGxbI4vYQ==","shasum":"71ac338cda322a0d9ad0efc3cab96fcd4b21fef3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181118082921.tgz","fileCount":681,"unpackedSize":5289611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8SORCRA9TVsSAnZWagAA+P4P/jTBq+IrmldqTV1+14xD\nEd0IXigNlxF/gtKFpnnGy1yXgz+KB71JAv+kM3kPll978TvKKpprcV8CWrj1\nyu6gtHTUR15Ft8Ulw+ypsD1IUGgehOnRBB/S/iHaN+svIheCGdejGCKbCMpQ\nSyG63Z7Mb1KlD9APNHu1FtlHh9MOZpoKjvH9CUreKh6o++s45zuAi7twezSr\nCkc20P+29BS60Yx+vOWtiUIiX8Gl6tw0tIW9FcJsulMB7POmHCrVVL43jv3z\nsE4IQxFxN6rlsS5h+9YuDxNp2M3V0eLcbWzS+qwcAS9JJbthATX3FOJ5CfJZ\nbkd6fY2VBcitLWRTHTEWUFMID9PHI+j+a450U9LEaxJ/mt+ZaTceWXa4m5wV\n7H1e1hFk5SpbKkzsJVAlM4cBnJ8Bz1anqjhLy/EP1hQ7QRZiOE1iZFO7JQN+\n8YEx+N+e8afG7gmvmJfQ3iJ+ZJMJUj648l3pti8CAgxgZ2ccao3mYqD08Osv\nb4+E97ApK8E+Ii5bu+yEUcbnwrmcqByAlY3oLHOp7eVaBH4TMbWNiJuBpQdR\nHRqOqR9W56/vFljs0dP3fBiwCedq+y9ziJ2frU+SA1E33qj0T8XC7hQWvEhA\nr/mJTl/w31ZuDVWTclVl9gJig/MMyYyPiYB0WMedOtXxGSt8cLb+LNSk9CeL\nesur\r\n=7JOo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEdODaiu54R9hU8nTIDdwUrQX0LSCBrxjOyzr6B8lu0sAiA6zB/aEoSErxS1ViPRU19Xw8cuA4a52vNIte51naOO6g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181118082921_1542529936301_0.4000101456228893"},"_hasShrinkwrap":false},"1.1.0-master.20181119070450":{"name":"@atomist/sdm","version":"1.1.0-master.20181119070450","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bd306493f3bae2da6438dcc1b5027349bea811da","_id":"@atomist/sdm@1.1.0-master.20181119070450","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ESjFZ/086AdZ0p904HS39Lp+9mY/agj9zV67Qr8Xj+ZKs6dmK/ucx9C2F7VHnceJ2XrZT2bzP05uxF0jA106BA==","shasum":"68e6d25d26a4e4d992c49cd21037866a06b66656","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181119070450.tgz","fileCount":681,"unpackedSize":5289915,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8mEeCRA9TVsSAnZWagAAf8UQAIUFGk7uU+qKuZQmRkv1\npeQ0fqnZB7noG5Zs3yx5Y5jbEp4YOhIj7BqD1COS7/Me5eNnHcDmYV0sYv+s\nwK1vPYvxnXg43+plxUOl1pqlGtEGmYO2Dde5ycYOINZGTZ4qFGeJy0ymN4e4\nseKYic3YJBd18HRFQR7pGrcB0PHOECUjXMFuIxGlZ5SkowLTM0fUyUW9WjNJ\n8DRw8mKGkNcQ6/nlunxvCKxsScWkiso64s40jGXz0J+yOzaAMvTU9pscS5Ur\nmg8aisB/fAqG8sREcixK/R2gMilTj8u5sMro+tw9gU0st+2G/kzFJfuGkLs5\nKQvfferSnNRx9pF2C+JlZIxCeDbevkI9/ozSM95BG9kWIxxlgK+kY98kYTFZ\nVETdddNf5v200VEJ42+o+txr4Tx+kD5X4faSaa0n9axeQUcCQVV9A/DUZKO1\noN3taWHiNWtLdeIHnPUX7riwksY65QQbCNhyY5/u09ZTbh2USoHX8LKAJFPL\nYUO5jwXjSdEbtDJwEOJVJtGctX42+oqOEooWJmeH3Pam0B1483p0UfSh1428\nEfJuLabZQ33yeTUWbHMiEuigZMJhBBwvk+MyBlUxIaGsK8IK/gxv3hXVVbtD\nShEknEzP2eBYBIeFSYelrnkvvrEy2wiGVQxa5MgAhBNw8eumdtusZCwq4FEO\n+IlE\r\n=DGCb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBHawlfQ0AdwRlznwPrBALPvdBcinlkHAjqfbiPfAGf+AiEAp0qXZ8FDrymBKsB1wNxvTOHHWQRzk2urwU1IGE3UikI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181119070450_1542611229891_0.0028899872510994573"},"_hasShrinkwrap":false},"1.1.0-master.20181119140839":{"name":"@atomist/sdm","version":"1.1.0-master.20181119140839","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c5527b1e9550f7edc1dd9c148d7602ffb70a4a86","_id":"@atomist/sdm@1.1.0-master.20181119140839","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qdB/cI2cHFWuE8vUXWGmPXymueXUq1idJAIx8vh1lqPnInY3NvHYGxkD6UnbBapZUVIC6JqXTiqTLNOcJcsuYA==","shasum":"3f6c909b678641f5cc8f4147934b08098805b822","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181119140839.tgz","fileCount":681,"unpackedSize":5289916,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8sSACRA9TVsSAnZWagAAnq8P/R6rHP4EYIHo3glDDV0g\nc8u6I/TdYYCKpmB9ujJ+TPAxPg2/Gnz4ZMUv4/leSA8sH9KPKt43u3W8c2V2\nUdpXpkK3IaDa6OCaEVmmQP6THlP0PvQA71V0sGDXGyZT511MPNvP0WQd2bdw\na5OzPHzUujHYO426n0qPOhcVVNpRlo7i0xBGAXYv1N3oJGw+DRmrxk7u41DZ\nM30Cx9/gAfN2Wxezgg2kGbvBH2HpwgbZTO+mWZx6HsmKvRe+uYxMnxmVPeZH\nacVU4j5BM6qB2dLXrmTa7YRlhwifp82ld5YjCEIGviAv4HHlgE74cVEErrOX\nswJW0bclSF6Q5ZbVKvFOguFJUHvL2QmIJgfKCxy9n7BAthLSq4C1U2F2AuyP\nvNShKwXWER4ZuBA4NPwHu1adCh06hzlFXrurVj0Ab2SPeGXWTRAmKLOmORD/\nNUECSoxa9byRg5RnVjMY4jmKY7CrH9VRnJ6qR64RDhIBx/B0ZrGQqLySkUWR\n69uz07Mipq65gfyS6BoMk8YHrqPZJfvMGC3sqYpsZ9M8kE3dwasNNTGkvH9N\ndn5DRqEhoCXOffGLopdvD80VayaCkQ7peUybRZBrtJuncJT77/QLlgdkLNz2\nbY6ejm4rovqdVfyreiSUH1KD9UTb4E5csZYQdicrftCg1Mwf6rFNPw5mEWyr\ns3aZ\r\n=xtPC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzTaxwwKdONe+UZnhpx1KNwM2vKJub2jCeVlPcbfW+kAiB/BkR8Ce5cr7kmJHlERK0t9c4+njvpLT2Mz34zHg79jQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181119140839_1542636671108_0.7571435332230771"},"_hasShrinkwrap":false},"1.1.0-master.20181119142637":{"name":"@atomist/sdm","version":"1.1.0-master.20181119142637","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c95a8bcb2cce6823e5f5379bf8f1e43e973c1e3d","_id":"@atomist/sdm@1.1.0-master.20181119142637","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lFc//OQudBab4ZMsHE0p8CyGGxV9WIB4j1T6R6/HtVqYm3KkTGbg9WSOf+0c0L+SFOyhaH7dqyKai4pyp/Xxbw==","shasum":"2714c462f12457e5f611f2c6b064f4aef6aa372f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181119142637.tgz","fileCount":681,"unpackedSize":5289882,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8siuCRA9TVsSAnZWagAAIk4P/3oyYxHeiPBInHSdwYHH\n+1GfqKiUIDYIL7q16ZcCz0mXpgMQBdjMQZJHcgQEp6GBya5i+GsWKcBxRSuY\n7LVL1f5rwgtQrSAgWSeTNt6y47o8piLKkPgTm6QFWN9hR/ajeRxyaSNFprk+\nhyc686Q839Pc2TvX7KpzSNk/8tCbVMkNHSCrgwYOMJ70JGm64q8ArqLI0nEn\nahjvAu3AewbA1nNjInlakzp8GUx4EIxor5DzyC7hs8A+WvyP54jFAgAZct60\nfD5mHFjjPZbxzm0nCncEALUdhRec8gkiovlxlW8CZHYzWUN+MjivB9vOrrhI\nVtDsxuEYdmgE/djg9LotF7cLeCwItg6qOHIMmbPypIdymAgGeUG9KPZrG9ar\n3am8Q1r6yFDByL1BAzH/qu+UTrcdvflbhukgQDb/tOBkckDaRBOFo+PXzo6Z\n/SAUjJdhHO++C5bWSlmpYX45Lt4Bro5fw8F/C0rs3NTEGqfZ+7sm9SWyLLVT\nGI57iIViXNG35DgnHfICLYbhizTnN2KbLEhBrbfqaPME6wFS2JvXZbZytc2+\n78rEhB/ooA3bPK1vZfXSd8Zcn+VgrtkeFAx7EvHLJ1p4sXkIegGgikfafAla\n7GEjr2RGSoNTSdJNznrcpPaaXAWMPi6wxzCOQXoy+GDLD96evXjWsd7MV6IB\nOwor\r\n=8zPT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCihcP5ZZxbXDkoYx3OhvsXWZRiNk7+XqMxjPg7AaUvGAIhAP6XVuFHrYVWFhOU9Y2wdgAyd7lTHBLi19ts2YsA7IK2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181119142637_1542637741756_0.6137634614674794"},"_hasShrinkwrap":false},"1.1.0-master.20181124164852":{"name":"@atomist/sdm","version":"1.1.0-master.20181124164852","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ebd23769cb783fab9f3886d365cb2243052ad538","_id":"@atomist/sdm@1.1.0-master.20181124164852","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-31APVy720z1gbOsH2ihbbwNTr1shw9pz7cTtZt36hQMZFIFIw62nYrK9VulkjtXyqaOPDB1nw1u5lpbc8g/Xqw==","shasum":"41af66126cee17558f10ed969256274f215332ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181124164852.tgz","fileCount":681,"unpackedSize":5289668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+YF8CRA9TVsSAnZWagAA/x4P/AoOcidv5AAdCbp4z0iM\neSJinK9bNsI9qfJ8ztfQz44iIdqkVtHD51yX7FPkPSwCIhY6KdJiv3y4GbTd\n2iaXjGrw+mb1f+RZOI997zBB+htI7cIpMDxg5LZNl9yz9oidy/vRqbhEeKmn\nkAts4LL5kQ7IqCv2X+qG05YYkv/Nbb0mQNeN96Y+9hVu5Be7xweF+DzOYEMa\nDMHB7KYiWZHUb27lFmdMeFqmz+4HKJwIXelZCTxAqjLvmAlXDZsBi6JfY90U\nko09OKmMtoV89KaQL5XXFCO32JDgPcm8uQDBv9E0oqf3lgr+fgiKeYwk0WxR\nSc8hB7w5BSgwOmcFcFOL1MIG1LFUc2Ute1a9Nx3UhHqo31Nu6r4d8S+VkONM\nkLhaCHTYt1g+J2Zbrjb6zKoTVFC9SSlevUDEjAKEFCet2mCSPZUs1A80Vdra\nnnYiXDCx60oq2TrELKNR0j0w6qyM/FgRfYQqG7VZcHUS5KjHYWSvVpVyJa5A\nFDbCI/jK2OC//ONH4B7ZgnDWa6VMpmGYWtKPpfqV35FFUWmhsn2SOYFoEAOF\n+U6aNG20POWBciKDbTYG3g9L4D4u9K3tj1sRnQZftKjXyrvThnNNL//ErHSn\ncSJxzOEbe70TOt0LHdKYyhSqBMzwbCE1JQ8OTrO417bCNp1Y9iKRatHaV1Dh\nptoD\r\n=WCot\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHCLOHrMv/YhRoa9oZKHtlGQoAZdU2j61F4Pb8V92/q0AiEAyA6UUxdKdKWnSUpmWryR+C1xhWtXWc4X5Uy8muPjhNU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181124164852_1543078267108_0.5050295209905609"},"_hasShrinkwrap":false},"1.1.0-master.20181124165720":{"name":"@atomist/sdm","version":"1.1.0-master.20181124165720","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181113184417","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e8b80a53aff28668bc95f36da387d5cbf5f4e247","_id":"@atomist/sdm@1.1.0-master.20181124165720","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6aI76TQzTdHB2RYWau/b+/iGKD6zQIKJTkcFF3xt5WTxj114iXABkR4ymaZhIvZKXmGb7rXJ1vLlF8W7f1J81w==","shasum":"656661b23be795dfc3b1418919ba793b7f2ab833","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181124165720.tgz","fileCount":681,"unpackedSize":5289756,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+YN+CRA9TVsSAnZWagAA9QAP/3onbOCvXC90iB9HUT70\nQoz9CQvrJhMzlUpgJRSOWoU4SAxbkJb2kGqcyHBNbEXYRYV9B4tYHo0shk8+\n9maeeel+zKn4KRaCMxh4gdhl7nsF9Jw+n9qSEm8QN2h7B7K1Fkevf17x/yKK\n/C4D7h7KZ38ibLNqb0vdUH61JGYYHWn9WF4g9EuLBxFSIsbUWELcMcpob7Oj\nn1AtEn5OB6ZQhC/pBcliFS9lM278P3Uim9exMWQfDrhn3xIZzqjWnSGWAtmz\nEsV43B5PXLn1D2uxxiEnA9w39c3R5sAYu/xyvo8ltQWHgoB9Fpy2+oukKJ3e\nPVXl6LCDwUeLm/nclY8AM8/BiQHgca+ZSCuIrPXPFvd7kAbERqoxVi2d9Y9p\nAKO3/KLDowMD9MKUCKnNJE49vdV3tWqS13SWLGWm6zJhfslrATKeVBdV0V02\nBXIWp4XSlVDpAm2zB8zgc9Pkppm3ixvCzfahm+glUi5KvBxmm9awVIgmbQdm\nVMEHt3yxIWqCi7w7gAjCcBbPjjr9oh+1z5wb/a3rsCivRu9n2fteLQL/g4tx\nJrJ/a8F5vUIzRBTbmvis2IG38/gMtmO3UP1Em/9IBh+F8/B2JTsmje5v1c6x\neORQBgH6FqUHKM3mEWuEN+PGRxaoanPY+AwY0N7jHjZaRdAS9vlu5eaUrvbM\nIogF\r\n=erdz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD56VeAlEqJCfM5TlnQqqPchOPjfh0ncz+pDQOD7t04gwIgReBqPnZuf4NFrcKIgtrItQRbDtrpQxviro7cso5RpRs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181124165720_1543078781513_0.1075633569106158"},"_hasShrinkwrap":false},"1.1.0-master.20181124172708":{"name":"@atomist/sdm","version":"1.1.0-master.20181124172708","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.3","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"549692aa54970e4d3f8ecd603760bb4b7481d7cc","_id":"@atomist/sdm@1.1.0-master.20181124172708","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Py0qt3WrjcFMq6Fdw1vRjtqXYZXaxsno+ftVHEwZULMs3KKUPpcObTSwcQOd7UQhysc99wobSp4n6zCWrIYWTQ==","shasum":"8bcac87f633ba0a266d90e8309109cd8e1b17d39","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181124172708.tgz","fileCount":681,"unpackedSize":5289884,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+Yp9CRA9TVsSAnZWagAAZ7oP/1hfFmPSy8LdaMe0u/ph\np2bygQyb926u63FwTG87T3O5/fG7zuSVcthxPO3OWMLomwgbKtlqdZTQn192\nV1NFk5VdOMcjLMuRDk0JB8x8h22ip4d13SfBO5NR+hoYeopzYIVcpHO9QGT1\ne48BGPlaxuR1C9FZMso1pcoFomUah7+Xr4dQXLUBxXhqhX6+M+NUP2BO4Pxt\n+geb73JXi8jBnwIkBzakK4YUi+NBuItNk+n9WNQnkhg/FOwEdqc17PWCr8ms\n0LTHoQ7AaXKHrBXOWxSCj0tzV6cOIvlyr2punko/4ZtyxsjfJDHcTq4mcF/+\nPRkAUpvXPeEFiw/+ZGSI/DW3rlfsnQY4RBgbXlHIavH0n2xB9pBCLwlS0DFo\nwbMqyrSQU7wg3aIkX/L/Jm4ei3BhUFR2T/MSpfrYLeaKt8mnJAD3zRSvaQUt\nivNVm0RBSGdJAcNVGdrA/G6GVeS5Rm6Wqw4l4+iNWKsG5oj7P7jIYe1Sov19\nKP9L61+LgxijiEaiit0QAdZ87nkShW856frajRDrS7P/RO9YGkZA2R32SJWN\n3XMxGHOU+2ComzpWYAFXsBhdJCxu7BEv4C3PMCy/OMtalS7I1oSfazf0DP+3\nwKxJg32YWteaYLka69iYJGGlwwNKn5odSOSrgGxj9mPyhF5e/rV68+BY0D5x\nRYlt\r\n=pxaG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEnAuhcIeZ+FmQqDWmm8KVnFb1+E1IzxvKS2DHK+Oca7AiB/tiGeCfA/uPlBPiaBlH0jMyOYl/am8EwbxkkLslyqYg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181124172708_1543080572627_0.6051929890487391"},"_hasShrinkwrap":false},"1.1.0-atomist-update-latest-1543307364811.20181127083331":{"name":"@atomist/sdm","version":"1.1.0-atomist-update-latest-1543307364811.20181127083331","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"02bf2a7977bd0a0acc84bfee2ef983c476b466d6","_id":"@atomist/sdm@1.1.0-atomist-update-latest-1543307364811.20181127083331","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VWa5apldvi3Rt418EP/FJTgiHxuKXSbZ5nWlSSMSRFM7OrSSalupeevnoG+oLPCdyESuIPLF5o16FORFZ7SCew==","shasum":"927d7b070093837aa15f504f7dfc333a72936ff1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-atomist-update-latest-1543307364811.20181127083331.tgz","fileCount":681,"unpackedSize":5289913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/QKeCRA9TVsSAnZWagAAds4P/1wAgFyWFJxQmEkgBs7S\nvD9oqlwum0xNafzS35sZusoqMOyNE8YGBtmb9us+rUdY1Cb9CzbA0TEVm84O\nJAlgZZrdTUu3/T0bPdBikvYKYVLH5omnUDxwSa+0jfHFaCQ1B2unrFH1krBE\nNJXVhk1npKrL9cLnVVwapUw2G/9ss5BSZcIVuTlihrjOXfew4r6s3RSaYWZz\nihkjfNAeX4YAhfW0i3GRaDUvWkuTSrOfDTRAt9huFUNeQeb142Ie71vUqXZf\nyJonDxWf07M9BrLorXarYtQ3Etfn+vvi4vJ0GrKXIuYn6iLsQTbf+F39s26p\nJdZJjOgCbHQgP6Us7Yidyi8BpdxFe+noYs28KVRAV0/KRAeE0I6j5JMhCaPO\nAx0PSOwL1mfg/IZujCZwThnSNPiqYgFk9B/ovB2+xELsZMDh2GeQKNaxB0n4\nZxJkQ8UvbI3vzfWZm68tW8DNzkeVjOjoglz2XRlum4vGdOlO560at3l1f+2C\nm5NkYO3QajA4XntBdqtk3zxxlg2PhK0O98QfYMG78BMwtJDFxSD19lDI9IiI\nJcP6RW8u4RxvZL2LDSTJAYJ8z4ePIrF52gGyAuJQf7i1dWCnkk+0PvHJiIkc\nItYPqd9NCkhjW3zErV70XBjiQ8zFK9frmQeLFW6hQukfud/OqfROuNEf/Mry\nz4kI\r\n=F++A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCd8U2QzESjxFPRBMmntDZmoMXibYmjUIIPx4XpUvCulgIgdaZCRCJwcErSRe9YL4F6gsCFOhpz7Ts8pAXnnIphf60="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-atomist-update-latest-1543307364811.20181127083331_1543307934020_0.02817905032323398"},"_hasShrinkwrap":false},"1.1.0-master.20181127084510":{"name":"@atomist/sdm","version":"1.1.0-master.20181127084510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/lodash":"^4.14.118","@types/node":"^10.12.3","axios":"^0.18.0","base64-js":"^1.3.0","copyfiles":"^2.0.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","axios-mock-adapter":"^1.15.0","barrelsby":"1.0.2","espower-typescript":"^9.0.0","istanbul":"^0.4.5","mocha":"^5.2.0","nodemon":"^1.18.6","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a21abe967c41b0f2194dd650d7b7bdda11af0138","_id":"@atomist/sdm@1.1.0-master.20181127084510","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+sJPPEO2S0XnU4WVo4Q9utmMgXa/6ni4ABzs8SVjVIxoJF/iy8jvlA8F5gqGUa5IR2/G0B52aTKyDhAsYvm0LA==","shasum":"979387fff829bac89d49410be77e63002581dfac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181127084510.tgz","fileCount":681,"unpackedSize":5289901,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/QUfCRA9TVsSAnZWagAAjE8P/ik+b6psp3vXBdpwhnGz\n3NLl8PB1LjyfoSrJ2Z1Y++tmirK6zEF723S0ZM1+xHHfmG11yRVDZEW5lvPW\n9qRBJtb07HR6hoZzwft7u6hfODskbgK7/HeZV7PPtksLtJ979zvlfuG1Blmp\npF6d1OEjttSzXGYbN8JTRiTSi0Bf0PWGg3nWR64quU3gj7nwDHHYbNX1drXr\nXRiCCfr70gM3HatRW5cwYN0EA7owS9m3mg3t1BELnKdhS/a+pT7FWD8BwFbw\nMV0LRKHU8bBgQL3I2LWC8HnzO42P+Rx4h5B/NxubKQABPCak6wxR8KYqxnyf\n6tr+KfmiVUbL3U8bXTjy2ph5Ph8NDrH8ipoGhxvOFBPiKKLbuZ3oclMaFVDK\n4V+d7UKEIhGRlTAAUMjLj1b73mBiAj67HSuoF33y/0t9H3Rp9wMzpmApMZzg\nUym5hLV6Qn47FwfrGlQ/owRHhSO/1gugiUTYlo91bB5KRAUHu5r4TaOORQYS\n7yusI4ChPsNNI2YcI31BUHuPq4KdfiWi4zE3mbo939Kq3mrq0AOQaZHS0BbQ\nnQVPhACCmIJOOYPZrnMCnp3aS+GE5rhSoiFngbB1UrxKuhebve712bVBB9lP\nbswdOYmfn0Xvh9w/zSMZ5OB0/vSR/lsLo8JIsuTT8L2nujZyzIGvKyU1dirU\npSNF\r\n=VymU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH9tV482x1skAf85izHqbEK2CeY1gVTJz8CqzR+g7yEOAiB65xNacKg/XbUCIgpEHKi3PlGdzD93bSeKfKFrY37bxg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181127084510_1543308574334_0.5569682641591713"},"_hasShrinkwrap":false},"1.1.0-child-process-575.20181127205825":{"name":"@atomist/sdm","version":"1.1.0-child-process-575.20181127205825","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b461ca766cfcf171da8dfc98cc65c26ecd8817b8","_id":"@atomist/sdm@1.1.0-child-process-575.20181127205825","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7Y5jTUlHFiyoQ06GGwg91bEGemiW9W//8R/Z4QXjEIPxMuv0LBvd4PKyfNRSFQkeWinua1ntItJXGKPraGRzOQ==","shasum":"49ba7e1c35faf7c0102ca902cc2eeb1f9c83a970","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-child-process-575.20181127205825.tgz","fileCount":690,"unpackedSize":5313653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/bCbCRA9TVsSAnZWagAAaBsP/R678b1hHUMEGhzm1BqX\nOfdDxJWWLi2jg5AEEWC6KYhwH+JAXS78T7aCPnBpS+FxefUkz4szNxMiEyJ1\nGLR/3lvKxoQI9wmitI8HOES6EcbcC/C1C1UEdpOSO+cxlNF6T2maCwvKKG0v\nAy0NShpbpMTvw2CJsYpSHKpa0wMhVp5fqRLf51E1Kptu0sP55lealpPeZg8d\n+zxrw6dEpdCuCqG3I1ZUNj5ZQ4uHUkxvlMeyxibOjddQWpe8L5p+3QE/ZjDv\nUBJmRAebSY+uBtfrzImuCS8UVF4WJVi3rW/qD7ivBp6Tt9KyepPSSDzlnWs/\n67ZcGTdUHWztHg6hKL55xS1mzyI7rweLHUZOHzBMiNB9zACNhcHRebU6dLry\nC4qu3o2n4pNOSQLbpDmMdetatRsx+6JlFlf1kvNgehR82VdLYn6WKJ/Esl93\nQZgs1ljc4mWgAXQwvATjjylQfwgTaVdYRycUKUM4cJnMeo7WpmNKG0/8T/BQ\nWz4HaTVAdw3MTX6khQlXcS0/akqSxBTyiTTaTGPMN0z2PKRbj6tyVA7/r56f\nV9cgp/DMBIOevUJj2uzhMKGpRFA/Ye812VnapoOiNvQXXY048NAApzygwc9L\nnPBvpePZwVtjZ1RRRTW16SBFeKRdJQNvqU3fAa6UA4mSGDvTgo2Lxt/z5aHB\np0io\r\n=s6g+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDMhXHxOTAKsJJN9ispqhKVYaN4bc3Ifke/ifd1nq57vAiBxOFwrFhwa6Dg4vRJM5lyUWc4lDlYgBMwXqEVhvmuCTg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-child-process-575.20181127205825_1543352474837_0.1682274138940043"},"_hasShrinkwrap":false},"1.1.0-child-process-575.20181128133819":{"name":"@atomist/sdm","version":"1.1.0-child-process-575.20181128133819","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bda4953d47d90936c60d15141fa1d278c3f7e927","_id":"@atomist/sdm@1.1.0-child-process-575.20181128133819","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/EIBuDTBYJzfpjcFxFQluyREgk9QvqZ5cHAQYpP5iX3Dlt6DzONO7AHxzQF2U/sLl/eaBxYv6oxqrt1s3Wk0aw==","shasum":"d41a48bf970543c3fa18a155db01198c439ac5eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-child-process-575.20181128133819.tgz","fileCount":690,"unpackedSize":5313805,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/prYCRA9TVsSAnZWagAAOdkP/iULOYnN28PidOuBOk31\nLAYC3zhbCheyrkxMwjCGTaDQ1gJQkggNrHgk6SHjDb5/yORNhffuChbB/Npe\nFdz0aZk7syYzPuyUuJvqf1AE1yqzuioD3cH0SRsWhOAa0SLjrT17sCv6GA7Q\ncmikpFtFqfjRFz73Cnnq7JegQL6Bq/tab/2Kd3vj0gWdrRp2+FX3cIPYBlQh\n94S9KwM2v6wIwST+V4frif+aSB0wogu3wifhoUAT6UOppiCh+u1Cim77BrIf\nsjHWhi9znkmZ7rNnT4QfhPYcsPIzzX3MT3jiTdan/bt8/oHoGCTqBXyoDmnC\nvblkW/ClouQ+MwHhfUlZ0dz06jkssJSWa1ZpQy3JYf0YM3gXKGSL57gJD3Ye\n1I1j/fdNulqkAHK7GycK62PSN5eF6diUO5Zm20bobyaDObgAg1SRa58fj1sw\nqki/+/gc/MjFvs1hO/VTvPL9mf0RmiaCQoihc6WNun1p/qwgmCsJhREUh6+9\n4DLsAzeEcsRCYFxcTUoc4EG/ids0XvkuT8q7q/gwHEqAJBTUl7+0Vkofp+Tk\nftZyUMhs6y6OQSjZjPi3CgahYZUgS6V/jlFWWHHF01xwPLpczrKsYUX2HIKo\n5p7ysGowRcc0076E29olA0iaA55xIcTEAT3IdZl4hdhONwPOhB7GYS1VeNsf\nPOD5\r\n=whYT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCbViuroKBl6pJkrf7soM3A1zetFhlrzVz1NS9al49IJgIgNcxHSPQncb3L6TT7d7HgwKTHcaUUJ3wLPWJFzcUO+V0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-child-process-575.20181128133819_1543412439337_0.8067132878935663"},"_hasShrinkwrap":false},"1.1.0-master.20181128135825":{"name":"@atomist/sdm","version":"1.1.0-master.20181128135825","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"82af1a845b870eeea47465f94c444d7e1b22aba6","_id":"@atomist/sdm@1.1.0-master.20181128135825","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nxe2F6BggOl5o+G/ar8mlK2LXCFlttJfGQEIKXudj20b54tl6IWL7Aphl82Nq02VxQ/UvI+F8+n/t8j9xIxwOg==","shasum":"85f08b019192147824fa89429ae87e3340daec8c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181128135825.tgz","fileCount":690,"unpackedSize":5397427,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/p/CCRA9TVsSAnZWagAAuwwP/1t4nqSYiwdE/vCtlQxq\nmlGILob9fhAz524LIaEsb7pmsMN3j8iPWht2ELB/JuqYgxk2+s3PcBM9ldfZ\ndP43ElIwhuiWoovJXf1aE/qOVRHGQMfvfAcIT22KD7FtaEb3ufiQfsw1/a7Y\n/mI/IaIoFxCsqGrtoG0Fd/UtDfZ5NPcnFJwGU6EWTkkifUz9ptQlxmYOJbnb\ndgKTIA8ce7yRTJldFliTzApKyYEbqmv7O5Sm6v1h8Jc0EaqDkOxE9kU7d++8\nXNfhRjNflIHNIJN+C8iyfxeSmPPSjtgNJ7zAjHOVv1yDsIuglsgwY5bJSpGf\nQv7kTVJZF6Mk8u+Hl4FuYt8w3Z1uOdppdp/rp+WPgC/pOGVf43ZEHgsvaEjg\nKGZVD3zBerXuzmpiW1VGi1pjqvxvZ+wBqfvrUge7+0lTOJ5Fh1qKnxQWo9b2\nbFh2HMGRWVJvagOF40Brhc5IEbBnXwUC8Su7dzUN4rVeDXtnGbjto5vkWCpM\niMksj9ziqxYzs+iFd7GlLuheqakU3W6jFNfGjxCmClNiZvrwXDx4jjWFyP1S\nxxsNCJBmHbr0bjo1yBH25pqSkvwncIN4I38YR+nxF4tKZZ+036HJYDWLq/cw\n1pm0jAx7WJKUoNWLnkstnD485WT+5ozFFvZKZq9OFstrEjEjls5dIvD41TQN\nX/R/\r\n=EaV1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeJUgZ6SyVuHUgyIbqxrmNL4liUpGe5DmUawX+OOBKPQIgNBcpCo7JDsMJwcOWZeWxmBhg7fcBhboe8zkDVrgNuyk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181128135825_1543413697990_0.3472855028734396"},"_hasShrinkwrap":false},"1.1.0-master.20181128135959":{"name":"@atomist/sdm","version":"1.1.0-master.20181128135959","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"82af1a845b870eeea47465f94c444d7e1b22aba6","_id":"@atomist/sdm@1.1.0-master.20181128135959","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-japxmAuzMCyv0TgOMCkByzymOcAXFHF3pAcaAaqraWf9ypbM+UJ3yQeimWwGTG09i4KSfWUN3dp+ad75E3aw5w==","shasum":"14071826e471484bcc650c16a79c3ece37ee8e9a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181128135959.tgz","fileCount":690,"unpackedSize":5397427,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb/qAaCRA9TVsSAnZWagAApf4P/iaR6MACeqGfyyTM/B1A\nUE7GRFOXtHadPS85VAQnAVJCF7g9qBI+/4E3WISWfYK5vORLSuk7oYkXTv8q\n+wxW9z9PDyOoNA/jjeNVhzV6Yx5NBFcnkuc5kE3ukHSTU5ymljJF4ECNfonl\nx6vQLcp1xN/AUojpnEyQmfsTqyKRaGonAmTrTIQllOfOQhzYDMBmUmmQbaDA\nxEfpiKOw8LpCauDEPHO7TPL9W7QjkTi51Kipnlmfm04PInKlmfuFvelYp8uW\nBjC684/OHHeYYap/yAatqFdPrh+nRPp41v8gCU7sv5CTk6GP1FPlaURKrrFA\nwEOtA1XrlS1+owLNHvBkf6/uI2BWYcloIwug1Neyc42yWj4CIXxnfe3S/5H8\nSKcEREzUwY/4xT1xumK58hHd5X8eRYfxVxijTWpLnVlfCr/yNYKkFCA1wViw\n6FTzNc4g1sEWcV8XS0keArpcsJ5vvuTycxTIAy2PXtfD5WVOZ2XbulmRifMh\n5E5EzkYLzhjqM0F5prEvaKYKiAKlJUx1fIttAsk/tBF2puBdwExjMOfK0E9k\nYxSMcWErGGXlx6wHu9jR876qPFO9DmJhHqhlm+wSaC3rXXDHdiDMbRfwkbyv\nYXyJYv2OXBSmHvYnp1VDbg67eKvkWIVdEZlyhw4sWJrgd+Hvmy1YpQdWhBwY\n6Ib/\r\n=7l6R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD5vN4DJEWu6tdnswJbeXNtn7nr7qqNOCcbIFoocBudcQIhAJcc0ZIfC9bXN3x4PgYEiWF5AdphlGm8LaJceey/nEXI"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181128135959_1543413785531_0.18498318143965586"},"_hasShrinkwrap":false},"1.1.0-optional-code.20181129141121":{"name":"@atomist/sdm","version":"1.1.0-optional-code.20181129141121","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a70611fd994ddaffb57a8f0e99c943dbfd9d5d38","_id":"@atomist/sdm@1.1.0-optional-code.20181129141121","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xynR0DxvuOshtkFiZLVK1cTOXATq2v6UrAzKCTyhQH3Nn4uJYRbhRszVZRMJnr7TE/8s6nTDCU+K6vWzrrbrsw==","shasum":"c0e8e75961f01918f6ce638eb2ab432976dd7de3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-optional-code.20181129141121.tgz","fileCount":690,"unpackedSize":5398764,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb//QTCRA9TVsSAnZWagAAclQQAIVAdlolbKDGi/i36aaC\nPinXLLIwgR1+f2NxSrMGvPCh0YjdSwM7E6Oaav1F6KzGu9aZdrMFAQn5gAVF\nsPCvrBWRYWO5C4oD7WsyVTV/eHhwxkkhcY7ueLvESe5IGmIQxmK6UGpM6KSd\nZNsd51VYsx/9LZNUSyAbeqBLkOjjIgXn/Qi3VSH9GuZo+Ol848AtSceh5ncZ\n9cWsCme32v/N6ZMjBxcZ2exmBJYZtCvryrKH/nF34aJA7VPOKVeO3sZTOIb1\nNnlRYqXimLDUyVJPAKfVEUXxdjC4iKlH26WCarBpwWWToTpSD7sBbT3ajuWD\n7kpY/r+2X/Aq4VsqjQSVu2CTeuJ4qDJeGznzB6FynjBO/01/q1GSUWs6I0yG\niDzse/wF3t7twZzXhfQ1P0dYlNWtLPdS1e1FR+9/QeY/jrlgcJdsi4fVICVB\nTgbAtLALqRtkTQIIHtMbyha6aPO4UADA0yHZQheSKZNFruh76LUO2xPj3uTo\nUkv6NttJToO+JoY2bgz8nju89om7leVLECw237CuZwduhnNtOREQXcJ71LYS\nqeCcTESfWEx6q7lLm1WAxUgD6/HJIF3Ris9V742Aek2MOAZPmeauE+Aqh7w2\nqqqxBfKkfFnj/KQ4pi9dw/gp/dYLZepNJlVXQmsHzDu2I5/89M1qv67iFDGH\naTH6\r\n=N+OD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtp/+SzFnm9WuZOi1CfFiFprmIvLtOfqc+Q7+Dv+rh4AIhANWKbv4RrprosJU7D5jlnOX0OuNGncbOMQbP2H6aFHDU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-optional-code.20181129141121_1543500818324_0.30745630442679794"},"_hasShrinkwrap":false},"1.1.0-optional-code.20181129142328":{"name":"@atomist/sdm","version":"1.1.0-optional-code.20181129142328","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"42cd9890f324da43349fde73c6c12b38cdc2691c","_id":"@atomist/sdm@1.1.0-optional-code.20181129142328","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ot8r6AOOK3JUCtIcslAqkqMiM9GItnNk/WWaHZjT01ppTVeKg7VORVq0VCT4Vh3JwMxoYTgaHGBWJpsYNMYq7g==","shasum":"21a57fa511b2ddbf1703e79ff28f1b2048e8f34c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-optional-code.20181129142328.tgz","fileCount":690,"unpackedSize":5398732,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb//b+CRA9TVsSAnZWagAAiQoP+QAB7C5xBaM2DGfyKFfr\nwF0MpylSreVHehsJ9AsZ/0a/DR4o/jVfPBtzh+FYtHfo+ZwRmbMQvGP972D0\nqlCq+ZeWHNMsyLBJX+8aVFfiV3GuKuEXo4rkdGsrQPf+72XeViWrIxqF0BRT\nn2nfIPo30Xx4a944/uUft30ux+h/YescjngHqTYiH/Icq6sgsbHPI1oA+z1W\n3xQBp2NMENj+L6NHzgrfyHf0W7NMavgzuDtItu6I5BF9ozR7BMnYzYbw+dfF\nDRgSOtCjDyAfbx1z5Y9ONkG1vQLMFsGAoiE75tunc6wnWpVE8+Sj0qFHkuoL\njgxG0P1UK0KI6mYSMhUhkpwVaBww80vcntnoSvey5oQ91XO70bBgQqJgh4Dc\nSwk02DLJMHSMp1hWxRCImVoxrS8yw2DUVG3kab4Ffplv4XU35xRPDJX9mY6j\nvvKQSyyTvl2k66VK3C21c6PVoOxpEj8niw4WXp0YQ5NqBj36rqLtlGg486DF\nSt3jfjfTgwSYelK/D76RXnBxbqXM2fXmpLxF9yXyUcM6GJZQTtQlEw5Zef1C\nBxfmcFH1oSXt7EckWwkCFZ/20rCwAxfqotYjWd+04vIX5zVN7ttYgcYpdo1y\nFaPxuKeUk6ovsLlGBqxmxSVHuSjrK0xpP/TIg2a7cYa6NWMMNzex/8ayGjne\nKdVe\r\n=Vjs7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDacMt/QTikNwIiUoUG4M6TqLC0ftl5+7yvHlnllQDf6QIgN/KrGmPLvVGoXKZVjJyCR6M7z/SyTIP7k5g72rz3cJ0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-optional-code.20181129142328_1543501565543_0.32223626197684574"},"_hasShrinkwrap":false},"1.1.0-optional-code.20181129142724":{"name":"@atomist/sdm","version":"1.1.0-optional-code.20181129142724","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"041b2f82f04f2d6b28d6ad0d7a5e93c99bb1a969","_id":"@atomist/sdm@1.1.0-optional-code.20181129142724","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wMVf5YAVe2KfaujSAY/Z/1uc7lwtnZ0yuOWNCbYTtmWao11rDNz76OHO21wuXnWX/KzJEaNshYVL8sC4JTHGmg==","shasum":"a4e700ae6d1ce936b677d8506f7d48c339415f46","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-optional-code.20181129142724.tgz","fileCount":690,"unpackedSize":5398868,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb//fpCRA9TVsSAnZWagAA64oP/23jRZQysvgcj/wtHnU7\n/u+g/vF05wwtANfdkbKWZcE/ogQHMlHjRjO8BgHu+lygjEkgnElZVuEuyjGv\nt70QwAClqKJ+IjstJRWoUB5nDhKnxtOMvuk0RXPcXMGYwbsDc/EjtkzSyD+v\nRD10oEpyqn/d6t0El+E1waA/ZjVDJpbjLkBw0qv6ccq1o3rGKgOFUWT/xuRd\nKZjtCTo1vzn3weUn4I/JEJxsZmlIYZAF5V3W99bAGp4Elwk9SxQ2vnGce/4k\nZUiHkSYfGiEEibrEnfCKAv8h6uYlkOnQPQ5VApoX1Tue6lIT6JcTNOp+frM8\nj72/LbjgiO7vKwtPi8ZBBQRHuS/IcDhycT3duu1RB6FGbe1APNgjZOEEfvB4\nQtlOVIhjogUjV2/rNAAK1buY88sq8r2Vm8Ne5Ulf4F3uraKu3RmU9um6Uv+r\nYgHyyOkCSSkTSm2RmPk0B6wEDv8ggrazCHWE0BLq0CgS8UbeNaNt15V60XSx\nOt4XJ+HeJoN/9XbRBCO6n9+XUcEUpv8yqITYpwcg8VDcjRc1I4stqCx6Pyfo\nC8RY4exswfYzc6RnNDNguvGeO4MbaIEkvWo0M7XBfxk+/b3IirqmmjyBkZ/V\n1kt9TBZmAqaQNZ22JjZPFNv2m99IoiJPPgRWsj1WsyUDebzbGsUrrANICFtc\nbxfl\r\n=w8aL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDCLcUlY2ekWFp1+HVqRDPZzfxEO9/c9GwuVkw9zYsCJQIgBLGeVq+LSgusinugrekr7QznGFldkq8F9f87gdQNa/s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-optional-code.20181129142724_1543501800559_0.5064642242958786"},"_hasShrinkwrap":false},"1.1.0-master.20181129143153":{"name":"@atomist/sdm","version":"1.1.0-master.20181129143153","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b999d5c446faa3004f120ac7600628ddc7797203","_id":"@atomist/sdm@1.1.0-master.20181129143153","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-117D6CJuOAWotaT04tHf77SXjBLDqvylay4ZQZNRcCT+73tfjWMypQXe3uFea/Ah9TyekNL08SI4C5NmzQ2MPQ==","shasum":"e5eeed0522dc749a0be6ec264a42592c4732a0a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181129143153.tgz","fileCount":690,"unpackedSize":5398974,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb//j9CRA9TVsSAnZWagAAlKoQAIapnUGut+p+7qX7SN0V\nIrXVoiDVA4WCxlSLFb3ynxuYNHMo51a/vvE0CGY8ttBUmIPgIhrhBjcQJBNu\nmF8YojzGaQHD4lw9nZHX4bexjRmL9hpKQF98NigdrUqdJHDwBr4szzaZTR0b\ncGGmu2cGQeOuD52NuktCJff3Uq+OI4bV1zAWGA3Pa+nIwU0kexIVuPh4dffj\neA1NtPZNhRkRFmeRQR9D01SJQb6YDXSLXAJLXMiLAFSwuu7ChC3FlvTxSIDs\nj9OW2bIbRzDQtsImiUSBOcU9C2bCg0zlASh6AJYqLgR8uiF0m1TV1fLwsQsW\nZiiqQA1AozsWPAcyusGAsmX/siC3m0MLrUN/FYa81AVM574zlvw9/qDOdm7h\n35N7Fry4AaMrl5nCXqnaGW0ej1thh6zfCxVVFb2tweZz69pYa3RPsAG9UEWb\n6ps/AAhM2E78jU6f3a8y+kpuwgop0xDvi9mDSjq/RI9S9YZpeF103ZuKo7sl\nIY2G+9YoIIg+i64fWSYTehwXz6voqcrXYtXSI33lJHwUDh1FozP2VYr3PYYE\ngWwt++GJJvvzULT6ca7NoFZagNyILm1agt/2JoT38v1CWJADMCt/Xgtr6g+M\nTQ7fVgAfdPy6grDQjmBxVoA1JBg0hrfc+1oy/fXWvJXxmHkAvT/8WAN3JPUl\nuaSe\r\n=GN0u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICOPkdG/E7R5/zubs8+WBnqpT5fxo60NsGQNuZUfLBcfAiAfBKRN4FxxT0Dj+gu1R+zaTbp4q0uaHDOqdfnmjxc46A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181129143153_1543502076474_0.3666135964001156"},"_hasShrinkwrap":false},"1.1.0-lr-polish.20181130030555":{"name":"@atomist/sdm","version":"1.1.0-lr-polish.20181130030555","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"13decdba60774f72b10ad9a438c3163488f53711","_id":"@atomist/sdm@1.1.0-lr-polish.20181130030555","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gTZ5U/jluiy4GYEaPD/q5P2FO0XzXWaoJ4vtMR02deGwD4o689eokxvudR+52UGBHms3mY01lAzhFrT6gKaDeQ==","shasum":"7e03acf23a7b213c193a423478bfeb364e1278cf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-lr-polish.20181130030555.tgz","fileCount":690,"unpackedSize":5399235,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAKmlCRA9TVsSAnZWagAABzEP/2jplHOEUAsoSzKTeWBB\nJu3S24atC3tBOwn/dTNRG+BslO3OT9xr0jazRFWDQu12rmkxRWx4zRLJmg96\njRI69WYvdngA/BUVh5Xjfn/F4XNwXgj6LxVBzNxmLIjlAeHrXo+JjM982Gtj\nVHJRsqpwfC1hqVGK2qRFZVl/L4hjWBNIamKbD1u9yRo70jOpCJx8RzWW/wbQ\nteNy33+ElWLFrTDuEmY6Bj51LZgqwRxBwoC9EuQkoVmtS182LFGzu7gV7pyA\noa8ZAF3FImgqkCuavw2vfciRAk/vdxZUPZt7wcXKQlUAFtOhOjXrvB7IvZu5\nVBIa40afORkl3PeHOHuSYcjY/mCnQYYpghSQ5Fei73CeYutKcOMXE/9IxccI\n2t4+gG/KmMTusISHrNDPlVWU6NqEq07T8B49H6YI/1F4rYntqgyvJOeggmWp\nrrca1MhQRxQgzJmoJMwG2zoseipMutApbvhOd8hHAUtf/b1Yti9aKoxaGJwI\nMvCq0aJjNjTcx3HVYNqJHJfK2L9beUId9ReYEuZQ0nMbBYK3V7OCA+UYMJsz\nOa45qmfW6m6wI4WWtVkrYwuD9riWW3MJK5B9wTzudvy4p1ikU8c7CziUrlcX\n0gt0LK4iJUmyyMdaa15XHWWm5HuXf2HrMJtSLk0IIBEG3G6KsjwZ/mK5E78g\n3pFP\r\n=Bguz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC+hhlNeJCkIeB52+yiHZ+JtTaRuHK6ulm75eXN4wAbSwIhALeAiBVc6Ra3eezcuPd1OFaUyCD16uK4IlTzmZSXKH2D"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-lr-polish.20181130030555_1543547300619_0.4023369602278888"},"_hasShrinkwrap":false},"1.1.0-master.20181130062949":{"name":"@atomist/sdm","version":"1.1.0-master.20181130062949","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bcb3bc342ae6b0cbbc15986b0e02033366abffeb","_id":"@atomist/sdm@1.1.0-master.20181130062949","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-82PnPJiXgnrRCQxxpUmWQdWGA/YIO0NGQBlsyHEqLHIJbXP0mmXTEf/pINaJn9Dna0T7bJTtjp4Xa6J8gfivWA==","shasum":"5db8ca89cd2d9179d913fe4d2c4d26e8dc9197b4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181130062949.tgz","fileCount":690,"unpackedSize":5399232,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcANl1CRA9TVsSAnZWagAAOfAP/iac+tuQ/RNlC9ateHYR\nL+DE13XrxAeh/IaZ4lXGdSRL58c3GEuNyxBZIqAPeLCtwv7XFP09JwoEhhoZ\nO3V+EQ7k0AqfYBlIr3nScWTAG6Pq05BVdgn85zn2Ta1O4hfi7xTo7SMK+IlL\nf03PkoSUdKEfYONMtBk6Fjzk0L1tTE/LWGoOz3s5HalAeK8NgQ22eM32vIB0\n3JATI/gXaaAbi0g62508P+yIp+hEVADdi2osih1YZr4T3WIYes1jq/C2KXwQ\nr2sp4K/C/1KG5B3MzLG0qbZeYCwD/mEkcXhlwZMdsd0gJpvsraypXQ/PcO3o\nMIwvEgWOwZFqxpXAWJO1Dg+KTG18yG1DZo3j/+cF5eKYpBUu0n/CAXEwUm8P\nRDiUfNOhSxW7qhSxGxxypZjTdMHBc87nuKn2EbT6u7BgnJ19UreTTagzHNa9\nDnJMe9K9PNEMp+YoJToIBlampMT7cggtnhlCy5WhWzTaepF7uMdWIjifSFLx\nQ9gvRF1qlj39kQbe2kyYQ+HfNDwA13bsAT6hHluTp2XKwV3gTxov3dZLUHQU\n+WBWhOybQ9SzKTYNsUBI+V8fO33ialjjbP4f5AbXrKghGdxeo1N1zk/tmd0n\nmQ0JysMDM87J1P8L4geetGum7t2aJlagZ6b7eAZ9z6k0fXRqLWRblKOAje9y\n8M5N\r\n=5UXe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDnZah7I4PNppsKinOEaFPfm9ClHt1MkzBghJgTphS5egIgDcJejF2FBrdsWCqIlG3x20/b9cEXY/x+7YFdjr8n8dw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181130062949_1543559540267_0.6241338371611378"},"_hasShrinkwrap":false},"1.1.0-master.20181130091621":{"name":"@atomist/sdm","version":"1.1.0-master.20181130091621","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"10ec0c8fcd6e76b759b5e550ed0eeb3f1d5882c2","_id":"@atomist/sdm@1.1.0-master.20181130091621","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-8kVY+O5u9aHOUvq4oDfLMWFzxQVTgt1IbTsI1vCknb5z1lbUKFzNiIMu/EqAwKqKj3dkvZNUIuhm5Sfx20qf2Q==","shasum":"21f6c10c57e0010867d07178ed62dd2db53953af","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181130091621.tgz","fileCount":690,"unpackedSize":5443747,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAQCWCRA9TVsSAnZWagAA9ZMP/iUIckUPnxHocdO0rnq8\nVYrX15Db73DUa3ZIue3qaVrGtzQfHEpMh7JosK60oOEaxdrgs6eB58LrOP9o\nKxjJet/4ywX1MVa79T18BzhvlrZ1dRaS7E3VZ1pmliNOjfYKyrB7R2C05Al+\nbt/7rZnYlul1iOzB1g3rghgs/CduIt39dYRxNpc4/N4hJXVNawmjrACG0/8R\nQOzBhLuKDRc1fS3XI4yGZ6L1LzVzGsE+KgsQRU+otqTDFLbXSoRt9GYOINnD\nuH0V0L3fet/TM8N0JYXqfQJCIXWsrFLyhFrEcC1T0VIIRv4E/aAzyKEr1gSR\nREJHIefqBbdIgW4Ro+07RJOsM3kcdGrWNNEy4IwnhG9uy9b6qpuGPjRN/Udd\nn4EFbfXc2Qs8L50Kyif9kB32sOpwupRXPdo+ft8ku246sdU9jQnLb5jeoTuN\nWnbJnRY+Kvm/uNjn1RLtRxKWOv0OVdrCEp6/+o24gOsORa3V9nWsd1N9wOoz\nptf9RC0PT8b3j5lI0AoeuUMIR8v39TpvxwUYuz+cSDVa09zCgkr/Jqj87h7v\nRMCTXzmWDbNf5a9tB1+oHdJwtL8tNxzbA97oWvk6kXHybqRF5JbFbsOY48av\n0zjMijYG9w9aj/c9A23W415oFhVFGXxUou+wnHUxgQUruDzxPLx+YgJL7S0v\nSbbe\r\n=NziQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCludoCTtBZmUsq8H6hLvDrxiy1F09P7iy/z2NstZFXkwIgeb9g9+4N0ZPeqUoXUQN21m4Rgdp0onUAkCz2PMtl6GM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181130091621_1543569557578_0.325570081156469"},"_hasShrinkwrap":false},"1.1.0-ro-array.20181130232751":{"name":"@atomist/sdm","version":"1.1.0-ro-array.20181130232751","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2226a276a62cecdfe5911b72fa8e6f295d9dd040","_id":"@atomist/sdm@1.1.0-ro-array.20181130232751","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dQh4OqP0HJEpeVNMIZSZ6pQFdu8gI7qWLA8Al3KPM6YhCxMEekSdVepmCxBPrHCp/my7EzalBO62IHRNr2T6yQ==","shasum":"207d992932eac92403cb1c2640ed40c815e4903b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-ro-array.20181130232751.tgz","fileCount":690,"unpackedSize":5444061,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAcggCRA9TVsSAnZWagAAW8kQAJU5LbTO36f5AmXzwvCG\neWoH8urgETXDigs/QaWgT3+npUOBmLnw7xvKxidNdDLeuF1hKXVwP3QcuVr7\nKG3rHgY8pGYbXSBpq5GEOCCV8kAt8LDmOVjSMp4x/6ZdO/egnXdBLIStgef8\n/nTmrvVjGX8vy4l/JovaECDVMg4sg3I88giv2Bgl5l535iPMM6wVWBxERWfw\nvEC4j9ORYMuaNhvMYNivUUOsS4n99K0OWS1RZEgRFQrHfiPCa7ZBPa5cOwRs\n43u3E8acqoZ7Rtiw13OkO4JhUwK4UbwP0BZPBtWVg4w6eYDghYJdFwSXj53Z\nM3lGJ4IshIOXgEcPdgxyr2iLQ0Ul3s6d1LRKL/MQeaRMlknJCc0DctzcZlz/\nZONcNj2kn9DKCRTS/L7i/zUv1wtcqwKyu612gKDnE/2jlvjPAZrGeV2z5jXM\nUaG1tudk1cDm5YBguoGwR1McgAzAvZOgAXefhiyO4kY7OrCcvTUyASJd3rc+\nYe0kkP471mf/ahYhAwpJRbanY14YhKD6ZxDM0J+QayqunbRzwdBJ+1EA/g7h\nK9Es6kNZeNeu231mIxS6sTTnWDQZnZirvSS4cns4L0xjgm8woU9uftuucA9H\nMUHMs4UwBo8jPWV70wtFiERbPFzSKuNgugoQ16XZS38zw+ETs4jgUrtCxbxK\nlubt\r\n=NXh4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGcV6BdhOLke+L3Hcipb7lWyIe5G5GR6/iO0epAcJmNTAiEAs6ukLhG5uMxkr6k5Rn8qXtr25Gqy4aTXrfUP/0oz3Us="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-ro-array.20181130232751_1543620640049_0.38625903981767284"},"_hasShrinkwrap":false},"1.1.0-master.20181201200312":{"name":"@atomist/sdm","version":"1.1.0-master.20181201200312","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"07b9770857afe6a4a06b5ec151926b57293e6d5c","_id":"@atomist/sdm@1.1.0-master.20181201200312","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aZ/cDKrA3XDxUlIu6IzuPPVs7UjGTexh8rBIKzB+tiHNqWN1zNXf4F02Gm5Hajmn2AjhkEl5+w6NzO6q5CJkYA==","shasum":"8781c491db38fb68938962debb9e3c35e2649884","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181201200312.tgz","fileCount":690,"unpackedSize":5444145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAumcCRA9TVsSAnZWagAAAvwP/3cLCYjXHZ0MRlx9Q6Co\nibg59d4Qwu6Nf8U977gPUsKsWokCAUJKBzhl3Qr8ErEDInK488sB3Fwx63MK\nlDFviS3DMJk4L7PKTZrkz1zeIoS3DYoUvcYWsW9mMxyxzROLzz8IFZdYJKo8\nRH5I8k5JyrU6ITIPFptBkRscGVJCZxSYvr8ygi//1WcsnQNZiE+h9sLNiF5c\ne1RCOSThzxLYAjG7co4D4plLw7gjSg727YoyrPgYbFOejbtSVIHze6OWLsw5\nIFR3/3mjvPzTwlORb+JB0E1NzTQzqU8T+83FI05Hk8KdBn3aHChJpTVxqjHF\nTTwWZ3X4yDMm7ykSXbXaWmw+2axMsSjK2ULJIUgH1d5LxDQ4SvnDrqlvaVxM\nzgv7jfRf564fBbXlveSv2fQaJezWnmddiO+nldAA+E7z1b+FNYBYtrrSZcKu\njwbS5rk0GMvanG/L2G2V+JOkCgw2bGIHOglrB/R4hPPVJIM4tb0MdwPz86kI\nc0fANbEVS/9dC6xE66LgOUCLr31tep+Aj7eKsc1dzK9J/WZZuKEdxjrT5Z5G\n2t7o1Wopn8HTMdQw8YMPMz5GEZm+qjE+KiaEryg+uHc0tUf3GFYZkvbceHOO\n6l0yUHksItz5U+wTzQ0E1W/aHxjxpkxXlbGOAtPOL7ii06ObEld8ACdQPV83\n31oc\r\n=SEcE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIESudplYFPd8SJyuPh628mGquldG/HA3cm6cUzRecddVAiEA9npPmC8qRMgCb7IgOh114Z+Td1eSzgLuGwFAWKSQsQI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181201200312_1543694747359_0.7478556581876801"},"_hasShrinkwrap":false},"1.1.0-triggered-listener.20181203220611":{"name":"@atomist/sdm","version":"1.1.0-triggered-listener.20181203220611","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f7c74832688d8b1c3a5de0fb62e29cff127215fa","_id":"@atomist/sdm@1.1.0-triggered-listener.20181203220611","_npmVersion":"6.4.1","_nodeVersion":"11.2.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cNA5gjyR9WApNeI/SursEDk6rhpTIAbK9bAls4I/SBdJSju7c1IM/AxzFJ+thsYgnSzNjOXUlEVXTWh0Y+Ln8w==","shasum":"ec102c295013abe1a2e15f0bbe50669c2a558439","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-triggered-listener.20181203220611.tgz","fileCount":696,"unpackedSize":5449350,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcBamICRA9TVsSAnZWagAAWZEQAI+KVYN1azHTXSbCeQaa\nazJ0E1oeLztKbZr8I6Ax+668yl/sFvIRqLNrGFUajHHsIHgNvndCFjy/GrKD\n2ai19bV6dFdMiVxXGVtfIi648k7xIokAkaG1D9IQgSaDT4zBBwKdD/SdTcFE\nn6n8jAtwhVvhx4diQy0XO8WHRbxk5kuT4RXmv6RTNwaouFj+8GlvqzWjn9Bg\n8p4GYAl6lg/twELm2lciSg7UqgFaEaMReklflqUGPCXE8ck05oWrpu9j6pcE\nhf9tiSmhHVbKBS2RHe2hLl/6vhsJPwtFcgVQRDKT/M5pEGls31HojuObW8MI\nYLOW3Y1LA019hduBX9QR2YldNFQ3k2w8F6h0bnRDybsFk3Mnnzf9G5sa0fwd\nBTMHWZgQm01cjNKRLG2hy+oZN/VrmN2S547XVydWoQYW4AMuyBv5q/KIs3yb\n2VpAwjeVV2joARQZC4BRcFXsQHs6XSss21i2EAhYuS3N/OTbXivqHKNSJDA/\npyxblzHcfRk9ui1TWx82yf25J+Dyt8/YRvf8mR/J7znXyDlOvhc7iI3panAb\nnG7lRqZVfbOrqNCx0Vy1xIcCJ113If2+OSya0YO5xEB1AZsnUVHP4rPH812e\nDZJATASR6/v9vLCxGb7MmqyHUi5iWrhpCl+HQ9CN0FXG1svn6cJm7bvOVkR5\nW0zL\r\n=twCC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDEHPbrwZkKIQVXyjCa0SfhtqgHn9W2x/eL/0oJVioUigIgFyzyYSWexvf55paFIbpOhCWkcFP4NNe0Uzr944/BKBo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-triggered-listener.20181203220611_1543874951670_0.24351131673932125"},"_hasShrinkwrap":false},"1.1.0-push-test-sequential.20181204173513":{"name":"@atomist/sdm","version":"1.1.0-push-test-sequential.20181204173513","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"edd770d34ef8858b621d8d3c998e22ebb8dcb1e4","_id":"@atomist/sdm@1.1.0-push-test-sequential.20181204173513","_npmVersion":"6.4.1","_nodeVersion":"11.1.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sCwzf4eD0G+VAO8mb/FCURx2Kq7V2w76X+9Q1vaWyc8Jvu4FCoHAGUWk7YOlAC/WBlT+MmUrfjHfClLOuNp1VA==","shasum":"016f4dc5a46be3c7ce9e60b3a1025abb82d3c880","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-push-test-sequential.20181204173513.tgz","fileCount":690,"unpackedSize":5443717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcBrtgCRA9TVsSAnZWagAA6dsP/2SBSLtJJVDQ96LqX6TD\nF0/DsuRHWKF97FpgnX94+yxaeNOFLUhkDh+olHsZJJkpOs6bg9EkYzZv7s9S\njKxj5HuFPlYWNGfcSBFibM1NoeeqelJz1PA25iB/XwiWmr8O4vnBk8857Myv\nf9UmUlRN2COYpN5KliAlal+K+TjbWj0WeSoBOVUdyZaDpaSlW0OV47vyesHG\nRejPwajwo9nh94J07CWqRsrHwoPz/X66b5CRgD4jxYNKxQlACgx51cUWU00c\nfHcG655QhzyqxzfQ/pn6CtBvQFb4xN1ig9sWfLfwSKobAcrObxzbjjItN6ha\n3L/81Y+ZzQkZTB2V4CqDD3DsrQpnnqkzD73W2NyqJc3ObJFu9x3UtsRc0F2y\nLj+ZHAi1OK9gnz2RsAu/67+WtaNsTK+V7bSz3x2TCAZiNba1Ns7IrJuQurw2\nmCOsnrMPz0LxvUhNt4pfTsXViA9J+Z9tExYy7mwPLh3AjXbehIs3Yzxr1Fuv\nn2dO8OYRHY8QzQCzQ8OTaK/AsDX2iWuhRil7+lIzyd5TWFGYNlM+yJe6usZ3\nLB4UZw1gUvlf6dK7PigKCtEeO1wITLaSBf5rPlqm4WZYoRAqb5E5zLsDgJ+S\nN5W3zyI3WfBnowWRyAe/BC5IiLXN68Z20gEcWkiteYr1kA0ZoyNWfg3YE1tC\npeKV\r\n=1mrW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtXBTf7kfCGae4wkdH4MVlihCN9or8bcwAbzBrgkUYsgIgG+qWQ6dWcct6QAZcz88y9cVXaTI/FbCohKxyXo44wRE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-push-test-sequential.20181204173513_1543945055916_0.2283154301928001"},"_hasShrinkwrap":false},"1.1.0-master.20181205060812":{"name":"@atomist/sdm","version":"1.1.0-master.20181205060812","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d2c62ccfaf356c06553f84cfbc7bcafb5b324227","_id":"@atomist/sdm@1.1.0-master.20181205060812","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3bt687YnxjHG2/0yBXaRx3gD6b+cai7Gy71Mm0FeyqGFqSa7pXRCbcJeFg03LLynhrj+Tv8UOvugr1rpmvIwVg==","shasum":"b2b31dc69ecaadbab5ec5d7f51c7148a6eaeaf94","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181205060812.tgz","fileCount":690,"unpackedSize":5443788,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB2vfCRA9TVsSAnZWagAAd2MP/1mfkzXJRLsgx2mrqVvI\nTFYNFvFoRiKq12TaUDBSztieW1f0YTDQnDpH00MoqJ0bbjiyP1JXBWmJz7X7\ns3Y3gN6aHckWPa0og8xHGTNgXmOKcQKfgyPejQ9D9lvjQMqgdwfb0DH3Moq6\n1g0HaSQXAZqW8q7qsY2Dw24gcK7wCM1tIDfkqSX8pNyzmvNOLLfwSLtt/DYA\n444em99WfkEEjQOQKaxtlFvhr4RIbj9PsFobJHyN4vJoS2QdV3NwWXHycBvZ\nIZ33jR2rgHLkVYRWIe5ypaaK1ewNeXS64jouEa84L/R383ZsF+N+E0s7IWtP\n24r5ZfDr8kt6Bz+8RUaLmUcnvTpr77ebBKzpOSVdbVTEMqRiHMkYQpfFwJWL\n1OZKvImpr5KdqN0G7pxp28hPazKXHdP0g/9mlpBjCPO7PnjcYafDROXuHyOi\nQ8dKtKS/6/UxvsVTCjIDM0/NNau3KJqAKaS0Oi98KSEh29z5t81qswAvKewK\nfB8paDSg9y3vNPtSnoxOnH0NaBeLEMbOF077RkV2yyQFrAJNxDZdGfGGJrOH\nmOJaVJxrffcCm3tztCKNBSHd5FRR6F+b2dY31T2E/JTjUmtHQQE9l6qxqpCw\nUtQJB05ubzCLXhYGvR+5sWWXZ1nrmIqT4M3EP+UtUf4nK8m8s+1br66H3CHO\n7dzA\r\n=vN5M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGmvf1q4T8zNzhO298hyxfSvMQK6DDG6FYtLzArmosaaAiEAn+CsLD0rIlBOzeJZr2X5HxPfismRGBlZJxAeTwcCFU0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181205060812_1543990238390_0.5006194573254625"},"_hasShrinkwrap":false},"1.1.0-triggered-listener.20181205075941":{"name":"@atomist/sdm","version":"1.1.0-triggered-listener.20181205075941","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"48647d97ca9fed00bc56a9e6b6bcfdecdfdfe35a","_id":"@atomist/sdm@1.1.0-triggered-listener.20181205075941","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BmNljDgPIb6wmIQbRh7G14oo/5oAKF/93a9MM9exDcAhQaFI5IP/cwQJsbxYXpNjqCyb2NYx+bLZetgT72+wSw==","shasum":"8629669e731f918153e09b028d7ec23355f2f7f7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-triggered-listener.20181205075941.tgz","fileCount":696,"unpackedSize":5449396,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB4YZCRA9TVsSAnZWagAAqr8P/jgPifu8AP4AGreSWCIZ\n03kJN4OX8HFUVyjJTU+H/qKLpWNU19mAZ21CjLXiwZ+hfot8p9q1UJjY8pvh\nicJDEi3MVPeFZr4e9/PA/v6MhFXw4x+FKG0l+tzHx16Vfe2ji1T9NTBaMYJi\nxX0Bw/fPvmupFiexUO13r+3Shws+Efjz3ybis1fGDQhzDUR14s3b1CmWamLK\nOUULmJ8MfzH2ZOGEYZT/MJhSt8pqj960Bd0d6RIz0YBREWMr64Bh5kIFlDl+\nxXphdprm3BfBqkYR+sSkMjFuoXBRCMcCU2Xb+Hd1fJFrsW9UgbPMqxJJ9BY3\nXBWWZPkec08tLlFiQJSPX+/Livy4hKnTjH/4O3BqD9x5Y75N1KKF9AHeZw9H\nmI+BS66TRX8fPa2/K+CzN0dyfFF9YKJWRmYqkrp/elNCfzL+9z2GO09+zsn7\n6QeyFQV76AXdkLoxc82UIwq/n2fVq1JUxKn+nqUZJ8rS8a2hVeqkZpn/fA/D\nnSxBipCoJn2uTCYAS5OqWdCN1JqNuYlA+5rHQsQgVtmP1iEecdFOYlkytq6Z\njAsRIb4LWXoMlfaNqicYakAlGFHeEjg0naQjwK1G0wDgaCuvLSqSHE6WxHx6\na2i2Xk23Nhsms+L3wkmubtduYCQODeyy/KgxqWoKvhKhMshLu2s56YAKjSnn\nZ2y4\r\n=kxFk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD7DoPvC+PnrPBOzd2V16++oDc7m365MdAH4Gy4/J3HmQIgLgae+Ef87/2pMAvqDBS1kgvDz7aAH8AUuZ+kpGN6aso="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-triggered-listener.20181205075941_1543996952786_0.6730083220854344"},"_hasShrinkwrap":false},"1.1.0-master.20181205145517":{"name":"@atomist/sdm","version":"1.1.0-master.20181205145517","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ff14723070beff3e17b8568d30a9e07b3785685","_id":"@atomist/sdm@1.1.0-master.20181205145517","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2abkKp0T/fAFQxsHxFHWtfXN8APyO30Ei7Tzon9J9Z/BYvMgTcl+hvnoRdaGRFWf9WEJ4OxiKFwi38Zqg6AEsQ==","shasum":"97bcd1e1e9c120bcce27c6ffd0ed53da632c935b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181205145517.tgz","fileCount":696,"unpackedSize":5449838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB+eDCRA9TVsSAnZWagAA4KoP/iT5iR6Y/Gnlc9ywMjuc\nLM9FRdVUmIOyCyfRYVSaOsHNnOMo94c400kuQtt6QjPqiXExEjoqYDhojlZs\n3qZHm0mfnYY7tPGBr85oU/yrO9UV/gS5d4vZdzAGjDzHjWmPWWDP2PjMPncC\n6Mg+KQUDYMP6pyN4/TecbmumjuUd5YvDJ8KQSqUzQuzL81OiyHu3nxioNnbK\nzylmdo398wbv/9R2W3PgUE9s9Ruhag5HXRSDR0R9kEpUILGYP+F+vollzQHg\nY2KUcycSxzOMun48WFf8+ci/2rvl0OM9Hl0+dv6656gjLJampWtRzFYfrW0j\nJj4sqfemQed3eu4uKTgPH1uVgfCTTihdAcyEo6u1gkvnWe1RX5rkjaqvnkBo\n65NXKLiz79m+P13AR5KhK7VCD3b3Cs1WS+xCUFHOsBYZLtwYt/Z8Qi8Idtc2\nNXrEgBs1n8AazxpcIHIqYOZ1O6iYhme82nYSeRJC7izu8GeHsJAavP7T9s4d\njKCuxIkG1HP+rXT89pxAw2M6BDIx1JidowF+zOZc46KEz7+qOCRl0y5LJ2sB\nBEdvAIvk+GoYr3p3iCmELncrdvLNm7wRASCuW8dPA2GLbyaBBdU1lQ7uJc24\nzyokVM93DleAdu4DAvzytYZN4mckbi+8DNCOXaE6/Pzkzgabu5eCsUDqG7ih\nChuE\r\n=r+T0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCmTSPxJ3qPBc7Sz54eSzQmoSCQdxAprH/yWQzntMuNTQIgIg8kByA9xGDm2jLcQcpStDjLesPIzsm6xSV3p/h8Cmw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181205145517_1544021891079_0.29605525712799263"},"_hasShrinkwrap":false},"1.1.0-master.20181205145708":{"name":"@atomist/sdm","version":"1.1.0-master.20181205145708","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ff14723070beff3e17b8568d30a9e07b3785685","_id":"@atomist/sdm@1.1.0-master.20181205145708","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-T92xl3mmLO6uWEg90AvahlDAWSG/9Q2XMgvKePnGaGmYuOt+lwUrc9wX1B+dzRnsOulswdkRibMzSuC32k7PUQ==","shasum":"781d5552521753718442ae370dfd9dded7310dcb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181205145708.tgz","fileCount":696,"unpackedSize":5449838,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB+fxCRA9TVsSAnZWagAAexwP/0eEnKpMGjBBlyANjQNE\nG6gvoG3clQKwGj6LaUXAI/Moi777WaeZKtzjSxP3sCBdt12f0xa7KFEXhGrZ\nEOfLWOa+ZYRNcdggExk1DLjWjIwm9RZyVYVaM+1TTMUOGNOeklO7pzzBnzfR\n92b8WrrnWzRiVlUpjKcAW5i1liW2RZ9FTW+V3faRVJia8BNceeyZqQjrRnWm\nQR1lkRcLiL4xRTMWgNVV4QD0heibe/jBxyJIyTG9Glymgn8TafbdoKRCHWdx\nUzES/IObdftCmwx7VGZx712fS7D/A9TYseGV4/w8ngU8arHJI9a2P0hzT5Ro\nD85myQB8hh3yODZnwAY+2BM4GLCzOvIPodeoKjCjfIK+CoHa/O30DWjH5wu4\nGTIQ/+SNWQEqOX00kazbA9OcgbMLfeWsreOhWVEfVbwLTkAO6XVM059kQbB5\n3xwz/u7FEQj5r3Oi+gEE5qtwrtE6XGKLrXb5HTMsJ/IZKsV1f26IpMYMdOZP\nlBQyEzTQKYPB+N+DmIm2ZyDUk+epJmTN0iLyxR51/+oIuJBpx9klAlqwMfrE\nCEGbgpNX4Fg97ndRo/V5AUwzybKaQEiscOHKkY23nsSHNg0hFEIH9zFb5g3o\nSC0DH9kFhCICCnEH1LGZlI6UrNwmjgT07NSY3qw05HwidbC50otmiKQY+x/S\nld0s\r\n=1Acp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCQtfPwojyPW2ufXIafD1YwkZHD0WJjI3TW2vV027PHiQIhAIwMyI1T6KB9VtVCeHeZ0OqzsD/1AUru3zl2Ao2Xouhc"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181205145708_1544022000605_0.4833138344183614"},"_hasShrinkwrap":false},"1.1.0-master.20181205153758":{"name":"@atomist/sdm","version":"1.1.0-master.20181205153758","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b56d5106a47175ba6e01afaaf3cfa9208e49599a","_id":"@atomist/sdm@1.1.0-master.20181205153758","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+h7d+KvfLNIKReqrf5jG/peIZ71UKEhSQBTbZSpEUN0LVwznzMeWLhmgKhz4zhtoMOEIACoNxC8BQzVrKY+LbQ==","shasum":"7bc2c8c31ac1d3baf5371cafcbbf66589eb934a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181205153758.tgz","fileCount":696,"unpackedSize":5449837,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB/F6CRA9TVsSAnZWagAAbk4P/2iEghon2H+JI3uf5EDq\nkv4szvcaB36zu0M2ugbrWUlT3UokNK65hwpTEjqL0gbEC1VeM+VgspddPJZK\nW2hTt/CJqcKCeJ3IFz1gDRL+XCgx4AqIRu3/m6hRvML82q1k1Qnz6jGbNsPL\nu+59d1tCF4Tz0NkwKbmZ9vGoX+TGpmGJentOUaMLDl+AxnI7/3dCDV/uNHm3\niqUwYs/d2oqRNm4d7MwS3BvsDOcL3X1CIdhvTy6Lsav7kaOeqtSF9DP42XpC\nlZsFpOwsA6+Smbi9QU6eZTHgmTBJcfahNJnO+r6arKT+FJFGqBiQ2H2GXHHS\nTRj0Vpbg8GMj3TOX4x3Sw9KT/oQWrvzF771cRexa2MnrNuYywrArHKBsUJtn\nPjz7f+wPcUIIM/bfNyLhn6T0JENLUlAbGOI0/1P7jMRe7Zd/P7OzAgH4eQpc\nF72Dz9LH9SciKFsZoYvcnJaMm9Gb2zu/UhV2k2ohnyWGB5Ls/SGB4J90XOtp\nsbxZrFHZh7VTrYq21yztdcs2RCxPd5iFzfdFCSnkWJ8HtvvVtkyJoX2Hb+85\nrjz0Lu05l1pJtwst0Y/hAbtJ/w2YxvR1TqVJDJO+tdJmHBBrEfKPCYn4MrZz\nCjFqUG/XQswIQ1TToqWLSBp/vKGX/gYbV3qU5LUcMRbKkBfldolH2O/JBXic\nuXeL\r\n=Z4n6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDLuX/d6OgBObyWJJqFkbEduvJYG/v6AtRPKPcWI52dxAiBSBPKqjNHeUn1dEmmiheQmkvN+sTO76Zk0Y1d1Op4G7g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181205153758_1544024442230_0.5519644001740931"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181205153823":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181205153823","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"84b3d73b02108df6994c8f3080b1ac7c7ca03454","_id":"@atomist/sdm@1.1.0-goalset-state.20181205153823","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3i7ec/lel8fEwumiLUlnm/K34h+W2ZaxCQSH191h5N6MreUivqOE3T2+2+Mv1agSI1wi2FoQAVBll2x1NrRv/Q==","shasum":"a82f333e289fe3376a25499d7eee10f82a84597f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181205153823.tgz","fileCount":700,"unpackedSize":5426638,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB/GmCRA9TVsSAnZWagAAlZYP/joprs6B9q/2rlRgo3jz\na9+D/9fXwfwOafRQHIDeHske7RgZsm/JQ0dcej4V0Wykr1EsHVs3vrMS+jKQ\nq5rnr80mIQMUdx3oQcgyzmvc418M1uS66zmGsEaoiZhfsqahz9+1qQ/XU1jd\nMhIBWOUhiFC258Xu5q7dPG8ZbbrFokEnfMcfiYr2EAn6jSefkDt1lzyvGT7Z\nyo1eP9fsncfLROSE4SxQIIhvjwP+wabP7RQ2kDnOY/mg+FgKHdLYK7gYnVXP\nFl548gxXs2+tJWmAWiRuJbKPFGfNuXgnGPQxo+t1gcswkBzQlLeA4LPw+pLY\nQf/81HAKwAFKAAHNzZTwB0GhbV6dltVmB1nYFjUePXy8CDz4mwHoizaaxm47\np9YUijqgsKGVquhlTL2FRCIycsmQJviuutqmWgQu60mXRvZxl1n8FW8sOnjG\n+c2p5TBphAOv5gPPizsx0PVMTUQW8TGI8pMsWh0jwgq9oEwf9OEGc6dAzaAq\nd68QemHemDD4amaRiWy+lNRsm4XjySNxfawbrf8JfZgsU6YtGsxjcJ619rTy\nxHiuzmA1FVpiJr2lKE5EWJLSPMHlWp/L/zdoVBvK0vYVawIlIOKQqANJhmFY\nF2Q4lLKt4iUtg2ytBlEjmzZ6i8MFzm/lmOp15ARhRcBfOFML4D7gyvRvvabu\n96xe\r\n=PUel\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDYZXBdj4UfJjgerMy/Us1AmBJxTW4pq0A4uXZ106BJmAiEAiCZsx4Ruq1Ug4+1D0sepl+dAma2cujQtmLL1zlfwchs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181205153823_1544024485053_0.9880882758870879"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181205161400":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181205161400","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"593247916bce456dac64729fbdd0cf2bc2dcc91c","_id":"@atomist/sdm@1.1.0-goalset-state.20181205161400","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EGEK2RBys16zkK27RWWiBbJcZoyGCgAmr/VWjz60DipWE9Tj3rwkedqg2rPzyE6LGjMIJ1WSBRga0yuj2q7q6Q==","shasum":"11e46ece2e19db25b6353db6d977815ea7968655","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181205161400.tgz","fileCount":700,"unpackedSize":5426626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcB/n6CRA9TVsSAnZWagAA1xAQAJD1mjldoz1K58uCq4bi\ndK8jz2MHnrRxoR0SPvtiWIqLMmuXSxn5z+EQ6GEjrJGY9TKX/S9KOh8QeYuk\nCUrysRph3M80b06L8SO7a8+iOnOFmAx7g7fJgt10rhfguZWBE+ASKm5pnLd5\nQINJCCkzu9G3W7LOlrV9F4oYajktnJyO67gx10pN2UKJB7QVUu7INfo70YBX\nnq7lXWzGI8u6maJy8cpbpLbU9EFmLq/FW5WwjXUVHzSYzs7liteohVfh+Rko\nGdK8CLVHC6map4/hJ1MPF9lbkZPhMV4NzOFEAcuGFpcZKzsY6ssLAipjHP1a\nilBcEZpzGhhtJ/1fOoavZI8HJJhcrc2q7ZgKDTyJMJHSVQFdRKLtcqzFgqFM\nFZpggDwSFg2Iis4uS329zniohX4jTUYkI3KQVRVwiXdZvlY9K01JK4+BLi35\n6Vf1uFrBDOhTdFg940nEXExAzUVylVcCdHtqJ+EBikk26KExfReBGlk0nuYY\nnj29HALP5a7z2d52DVhX7NxF/2JCp4lwdOsi2y65MjUEn66rOi8cPtqK0ygR\nyYR4jQerHd2jFRNR8wWirRa3nCWwxK5qESzulAQi8FLuY0gFpT7g8nx1K6hr\nk55m9OfGUlaogMd5g+xvWKVN6ZBGUkwu1QwDnz/4flzYQQaNogSYKVyBCvEo\nj3BZ\r\n=6d/Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDP7UCd7wg7IBaYJj2hqKOMHD+6MSPFClGHBLurEKL8wAIgEk8RRcEBWVjUa/Z/53LjHmI2rXx30ZAANKSWD67O7hM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181205161400_1544026617980_0.21795794597084828"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181205190934":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181205190934","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e786aa8c0ba760faf1e7fa3df24dfce008c4d0a4","_id":"@atomist/sdm@1.1.0-goalset-state.20181205190934","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kpiqewTCfLHKIi/OJfMB6f1C57wIkpTDAjgA6BROKXO0MJVw2MRIYzUVGr9cTs+ZphqJeVAzxIZOiCk4K2pLyA==","shasum":"9de4bf46369777704750b4a5743ffc635094668c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181205190934.tgz","fileCount":707,"unpackedSize":5437041,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCCMcCRA9TVsSAnZWagAAmnAP/1a41264V9QrgNpOTmaj\nXBJVEx/IMQ5x2Tj4C/juA5AjPsdux0PV0A29SRcEIDtOXvxNhAWeMx3BRTdp\nRpzupodqKzqsKuML/zTlCbmBYpwcMNa4C9nAzbjX+CRFMKYpd6G2e2+88Ux4\n58QBQzz3xIs3q3nb28qf2TlP7sxze22zuIU1rDLE8uj9kacI1+CqdEanTcrU\n58Ln0Wrxa/fx7fxQ2Bj4deH8Z5uEg1al+9YWUBtHQHkKMvZ2DPITd6GzQC+b\nsjpTtzecVJ5TmyVm43C3gDnb4V926frPallZPLdsDljhM1/hT0uo4W4ynLbx\nUV4jjBazcFEu13CKtAeKtHeMY1k4CxbXhJExL412dlsxeeW53vi+hk6IYGPE\nzjx6Cekr5OeqMycy7FGZU1Rqz1YR5KCiMop+sfL2HIsiKtem/J56u4v1dj20\nuYJggfmPQ24VTWc4b+2WIPl5Ibwt2MJDbKgBiGJ5j17NManriLZ4cpXPpMp3\noiww/0WXsnG+K+VJYXtAloxpdv4teEq6r0T2nWQGyqUimhCDxDIiIRI02jr/\nWXcBdRPVxMujj7qGNV53xuQX+JRYnVRDT3VPZGLb7bIjYpx13hzrQD4/YIlS\nRlofCUw+K3okEe1Cd7dWdNZJBiDA7nVv0BTUano5QZzzxTlEmwVZhnF8yS3J\nxphF\r\n=hYHy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG92zuQEX6KNW2+2JyVRiqqt+Ea2wjx5LvBLCWN8qZCyAiEAyEiR6jmLWDa1FLHW2OsPWkp2bnQFPLSMAyokcNahHUk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181205190934_1544037147971_0.6794639195839571"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181205212718":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181205212718","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"275246a54b0b3f302eea2df0eb23dd01c1afa08e","_id":"@atomist/sdm@1.1.0-goalset-state.20181205212718","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VpeuvO8291fv0T2d66QDRxH8mYncDZlrJRaM09+7vuDumRjygnoJmcLFIvh/wLSxQ0pVjZRlgsdmrymYWU/RnA==","shasum":"84af5a3e38fb6beaf288643e717d56742bc3911c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181205212718.tgz","fileCount":707,"unpackedSize":5437399,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCENiCRA9TVsSAnZWagAA+7AQAJzyKvbPBBfGqP2fjdH9\nJy7ETymKikxyNgywauGbYL30VPbx5qNNPjFG0OO4FZ9bnTkQjpk9NuJJkTKJ\nGmQmFEI7rDzLYaiYKfPWauuRuqnExV5/frQkwT5kFRfRX9f33wx1BGorvGv9\n04QGXs+VTH8/BMak1jWywcbyUJ8jke3pq5Dcy6IkKbqrN/MBCTRTaoQhZ8zu\n2aMuUMZNi3NV03HKpJTYeurflenYALfpXx+dlGnifM4N3wtZwzEq31XXlcmd\nPF8YA2RnV3K13eOo4Mxyv3M0NBvRf8uxgqIFawHHO5IF6q9LrTUX5B6XX4Y3\nCLhSWePIGRoiCwXW7/GXdv8EmRNa8KVDiJrMNySOC8HZ4lfKji7jEyW5T4wO\nsDtp954EgHjlb2tI1ti8vLVrlFv6Hr2tKlPpBL/Dryp7p9H9x0ZTlTHQmW5Z\nSZsVN+gXmLzqxECa0lSw1ZJPcAjCMNayF28ClGVQqqB63gXFgUlwBMCLdf/o\n6c/+Ey1w/bX4yXhHQiSNpiEZLvLm8zo5HxqykQF12xe2IIMR9y2rOO5qy6iM\nctAGfiGDak8QCoUZ8tRUVmCyR2adqt8Hb44LDm6eWeNsybvZ6NhfHKAeRsjL\nuAKUS7JvEuZf4CDr/P2Hn9oRvCP2G4Hj8URjzLCjOsyUCOX/MnQgSxBnqUjS\nJaVp\r\n=NXv9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICiAIw6z0LB5pSQNF4pDziYNBt55s9MA4chENA8rGXLgAiEAxZp0I6pWTHt6Q9PbGiaX7fbvxQ602nP87wDLAHZQUww="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181205212718_1544045409177_0.9229019200682786"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181206090736":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181206090736","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bc6fec35c3d530d3e061c885f2904c76da096be1","_id":"@atomist/sdm@1.1.0-goalset-state.20181206090736","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IOpfov5/s0q3XXBx4saLb1XIKm2PdsY7JeyY0FiTjE7frSSBYwOaZjnvM2Gidbi30X0EfPW7kUyWKaVcusKyhw==","shasum":"e896679adbab70e34e51757eaa51aa03e6fc27f9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181206090736.tgz","fileCount":706,"unpackedSize":5440219,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCOeACRA9TVsSAnZWagAAJUMP/2Tg/MnnDL8pXV1yfX7g\niMmyFSDRubdJjlGN3YCz4l0oXVODiTy52liQpFxJZfhH0IcCklIafmuDvFS6\n2ESlvRDMxw4FxLVq6ZGoUi9Y4NqQuzh+QRSW79kVVp0MFx6xKdKl7PiQxZMR\nRArd30nfWjDOewLQYAevDmaWyc5oHCmocjEZIC740lQAz0yAxCrBWJEnX+Ko\n5yxT7VqGOf0cp1hL/OyzultE4uWG251f9owQzyE8t7KBzC7K7ptbGTBkUZpa\nmunSFS10TyNYEOX3pMF7UIOHJs6prV/Nb1ijk3laaSSZSiwoV2XwKYo1OMJt\nqjhRUAP5L+sr3JahTqBKAGKKq2s7//9XgiZf554YiEap55tFnaK8LjSnr+YR\nnDZet7zh6yf+E2w1CJeEXtJq8tqi+Cjvxc9FSgg/ECv6b1wnclY1Xgk6Ii7D\nhQMyucAdtc/ZkxaHR5i7vRniWTc6ER9d6PJ+7MYE73wKhqxOFe+u4fk9H14g\nwg+YZd4X+3cFfeLCxiDpKszbaTOFb39O4wxtQvZUrtAggza8ZBVQ/L+wQD+t\nSxnomOC5GVOLDXSltArQnxmR/D7jl7awLo0hpq0PT/9qQl5QmiHfk0hkczM1\n29TJhRmx7PS/wDXWe0kjsGvWlqvKjdbI7qUaqjnt4Z0fdRYrPGYEEuCNyxpH\nreU6\r\n=5sc9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGfQBAgS+goNIxPKSUtbQhxGKPjpP4ykvZVewr+OAYvEAiEAtVLDqO1771SObtxUA5PVH3JKNyEL1iGXjjJQeW/W2Ow="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181206090736_1544087423225_0.29984945138605457"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181206101139":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181206101139","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bec17ddcca1b9a0baf174d3e5792ebe21e4e35bc","_id":"@atomist/sdm@1.1.0-goalset-state.20181206101139","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BvWHm5HYA+IoGq/cKbifRoaoAkfev1D/jpvDJfCj2qseQHxavVjhHM1rOAdyd+ZBSA79Z2fhw0FW272YWg8beg==","shasum":"814e393f6687caefcf5805cfbfcf1c609aa9837d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181206101139.tgz","fileCount":706,"unpackedSize":5444167,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCPa4CRA9TVsSAnZWagAAU9YP/1X5h7OvI4/iweeOFUNu\nTElUWkQVt6mGQJ0xawYta0N6vYZVT7N2mzCX/vSRmBtgGyILhNcfOnbr2wz0\nhRpcvuxGUL3VJPjK69/4R1SMwoRhH1J50Aq2gXmjReQpUW0IgDQJUs7akPwq\n3H8cbOjDLXFqZbW7PJ4x0QhK4Z9TEbfMiOWQ1Y4tpNWo1IaOe1mZKIqMvfvX\nw11R4F8qOFWgL8WG8Tzbiw2DsLfc+gJ+Ypuuxc6qMqjgDQIA7qNNOrNWW4xf\nF1C9wJVHHatQawvo9MQt8m9LqWyM1FJIdKOS+LCxaKf3pH6kS0x8RG98tDrl\naOmgDzh3SyYZbyic6JbtMUevI21EtFgzA6RgVFymQgS7oCSdtl6gy8yOyWPC\nas7nIpc/7xpqvSZuHFxLF8J6OTwg4xBGnde47HQi67p7pQgExqKk2usY1x1+\n4KjNzbh2W1BrtcKb7GyXiTRZC9FQoCqzUZqosvqhqiB5JBOYmlNvmnzLgx4f\nMLxkKNAXOOlg/Q2hJnSf0TjydQZRXrZvT0i2EDzpSXmbMd2B+WPThyXduhgG\nUo+PVt7Y/iLCM0xNxjD75IrrN4h/6+YiSCpcnoh2Wo7d7qQVbT9qAD9leY5Y\nAyv89CVxeik8UyuEeaJJ6Ix21oHEPDJUN/9zvoX+S4B55dWDYgZvK7pNhYEL\njKV/\r\n=Fd7y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJpM+lvywoRgecFu9AWS1pBLbTW0wu4LPHO7/7YL9ZsQIhAOJQC/tPulWxE7T8aQ+HTIZZ6r8pn5SYhob4l6qYNEy/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181206101139_1544091319949_0.6095650586071237"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181206210725":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181206210725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"86e70ba13ea1d67c5cd7561c40f340d593fa281d","_id":"@atomist/sdm@1.1.0-goalset-state.20181206210725","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-B/hYHZ/4dr3eMg59Vn/hYRbMqhC3BF7bZ8HwIeDxGcA3RZRzzOT+2DZj3pB3vp9/dVilq4cBcGETjr381Rxicg==","shasum":"2d3a7991464082b732bc9fd6aece52f2887d471f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181206210725.tgz","fileCount":706,"unpackedSize":5443401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCZBXCRA9TVsSAnZWagAAoxIP/1WNLRwLXu0EPT6fKZLr\nsnjgiAS8NTc65HWShOU8JSJ8meKHLvxtvcTWHcR8IorNyO16Zs38pBhGmIMT\ne1EabUVulL24Pdkq6h+QE8fGLCEzyniD5Q72U63uYT2XNfSASg7Y3fQMklB2\nxRw/HEvqRlPAQj4xYAcxp9TGpjw5oxOO4iSd+3DHX7I+PhDBQTV1XZfT4d2R\nHvCfeealdIqbsCwKFtCF9aDPpCHXTpPSavp4Zkr4iU5hQerV0pkOD2N/68iP\nB+t6aTleQYnWQrxrJyhwIPT4NMCXPa0G35anJrFaPKorXIkxY9GMihrIcjSy\nr9C8pwDSPAqhrtUYzyfID49t/bQYY6NrN1wq3YzMv7Nn0x7LUSFxAsToRdPA\nf2iAWwmY+C0Syc2R3GlL+In5SfJ34kci+A/1TbhqnShVJIfu9XZisU6t47K5\nFRT0Q0XqqlO3JaLHakKiRUWmzLfcKewg9MMn9VvdyRsYIYQplW4AfxVB/SBB\nMnjK554arzAg1DrvcyAtADlR8xlx5pRHySSGgKk3KT+yXL1q2nSdx5jtRErD\nobb+OgAIW4MJmIjKKRgYkpKYvJ5WGDF3Mg0wKcds1DgdADwK+OV/1ehXImS+\nWnw/Wh+i33MzdQ4WfDMMe4d5TDOjNh8oJxac+igdxSzkVys/TXLoZ6Az1aDD\n0HEI\r\n=uJbO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRcCGzeWNvjb0ZsHMHPPlV1AL6BJ4qMfX0rCzqHKu24gIhAOLs3cItSDUp/BZvktqCEiQL2nUBlD9UTJZRo5u6Wx7/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181206210725_1544130646681_0.29870823927574186"},"_hasShrinkwrap":false},"1.1.0-atomist-update-branch-master-1544134392070.20181206221431":{"name":"@atomist/sdm","version":"1.1.0-atomist-update-branch-master-1544134392070.20181206221431","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181205110517","@atomist/slack-messages":"1.1.0-master.20181115202354","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ca6ea01f5ca8dbd207dbe3a637f907ca65bc2207","_id":"@atomist/sdm@1.1.0-atomist-update-branch-master-1544134392070.20181206221431","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-BjCHT2Z8UUzt/yuP4l5Jy/FWzHbc8yGttgRqS/YlpLRbxjP5kZ2sYSRRJeqx6rCbJzoWQCd3+RXlcDbAICHAVw==","shasum":"a867f9c39853da8f4e52492ff86a64e9c85821eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-atomist-update-branch-master-1544134392070.20181206221431.tgz","fileCount":706,"unpackedSize":5443451,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCZ/0CRA9TVsSAnZWagAAY5EP/RSjgmzVIGndCN7iXSy5\n9WGFufqkvd8Wi8VYQEqhomjkgtxa0OBks923kaSHZ+6lijSqgrl/RgaqL9Lz\nShV0YWLtx73EefVEOfV0yfTBJ7PqDkfZwE1nLdIMo51H91tVdg0bvMC/xkyR\ngYf+Hz9woWTfh077DY9n25LdEBMkYsgCdWNRDrefqSPm3+lPVnBHFSx5pZrS\nI6B2P8YU4xwOiM0LydT/tbgZhW2JoQpO1ahlnthtZh7VnuXEGh64DQSpirvM\naReD2O9yI5EhWhCkXyEEkOlbhB33lkExVfD8my+R8kw2D4dAZG3Pb8Its/WI\n7b0YRqSl4J3riMIvWUPrhXIhRSrC9make08S6mETSW5PbMWUFLiDEQ8LaYgV\nTT7IotMmJzKmmT9xUUksCfiqm8yx9Abj5FGQ4lYCpOTtQsY8c1yk8zzHi6bs\npN6nsPLXpoqO5iNHwTCGPeP6xPn7dA8/79g38vct+sdSgCMxJe8n6m+vwMPN\nw88KBzS+oFc8A9GKWc4DHYHZqk6aXTA6YNa3aD5HfLOSLF0fU9+XYR9LBbaN\nmTMHn1w6cQn4jbEmyhsFZcoKs3P+79yiIsuooEgiqMdF/V1x7ReIHWSWH2fa\nB5LrXG8UcqMWsJC530Ol6nJc53zOIsHC0nixNY5zRc6v/CiFz8PDrUU4lIUR\nEKVt\r\n=81YK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHc4AS0pPMeaRWR2KrXZMZ8eLQCQd2ifgYiBWY+JRWG1AiAJ4vHAZgJN7mqqkusFY68eo7WaLKnYTh2tF6tcA6qm6w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-atomist-update-branch-master-1544134392070.20181206221431_1544134643265_0.7440310422695091"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181207082036":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181207082036","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e1a46a4ab92cc816d115aae2c990ba8aa8e270c0","_id":"@atomist/sdm@1.1.0-goalset-state.20181207082036","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mRMk0OqUOqzyIVT2g7xMdKPl3Z1loMYEMW1P0M3ritMHClopB/UglnwScI7HzWQUELGdmgBD2e1pbNwLW/tx8g==","shasum":"273f477d22eabbfdaec2d0f1858b087954da9e0f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181207082036.tgz","fileCount":706,"unpackedSize":5443401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCi4dCRA9TVsSAnZWagAAjh8QAJ9itQc/TNkehgO8MEMw\nPH4FiekCbWUStWW7kWyP+M+DJzwlY/nVZX0VIiRFwC/t7yVY1OniNYE1a1GH\nRdwnnac9vyMTMfxoBEeC6Zqz5KNVwNokhXmkEgo+6GMB/TDAB0qqkwJd/1+4\nhYdw3NqNMqskfQamHaFZJdmW5yAp9bKRyiEQkJUoQkjsTbKuKMAgUBM7Vbvm\ngBbG4DYfERwrvoGMmzhgBY1qjUyQCTr0F52lv2PtjUhyrFPiPhuJB8npZBoU\nOkzrfwbwOfz79C/jGl0vQKE3qsgmJVPCfqZja/uy9IhpKfLp8g+Pe6DViQj+\n1VD4/bk9y+MD4dDmRfRHjOSBDMNg8I9+KhbSzmAKxtEHO0qL3qqJiqOR5T95\nG0SaUkr0b2PBa8WuFqSAqT67U8LaMCAVbhqiaMYwHVzsWZj5h926yc6QSvwy\nzMDvcCdDTOM5aiBbAacLzbIdz/VWlsQf/XFGnHWrCFaRLzYmro+Bygrzstsz\ndtmk/c+EUJmioxh6W1quy0fMN9vZPGMooom4zaiOumZA3dLX4+OUBiR4KKCr\n2+tgQbujiEAueJKGaOxn1I7izXSMwrlbOZq1aginSmmkpkltLY+aSnpuL5FA\nfgnlvqPVCgKyx3+RirjP2J19wp53JbZEjUtJ6e9o+rLFkWKbc2ssGCfig2Q9\nDWhi\r\n=Y9o8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFuqZtfW5q+27gCFLbcnURG0Xaj/76f0R1DbNzrODWAlAiAdQQX6G1ARQSLrB4SZQd5vZVPhKYWTajJEnrCZBBltRg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181207082036_1544171036709_0.5583772046249806"},"_hasShrinkwrap":false},"1.1.0-atomist-update-branch-master-1544181930259.20181207112641":{"name":"@atomist/sdm","version":"1.1.0-atomist-update-branch-master-1544181930259.20181207112641","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181205110517","@atomist/slack-messages":"1.1.0-master.20181115202354","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f6b8e2e018b6af6c5403f99c78846fcb06d1bfc4","_id":"@atomist/sdm@1.1.0-atomist-update-branch-master-1544181930259.20181207112641","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cu0IvkcGDOcZmGSwxGWnexFBbrvR8Zty+J9NU+2iBjJb4M9ChAfLTROc2AAsMjNlyrYsZiGOHTCZaH54B3xazQ==","shasum":"f5ac3b478451fcede0f457d62f0b5a180e112d16","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-atomist-update-branch-master-1544181930259.20181207112641.tgz","fileCount":696,"unpackedSize":5449894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcClmTCRA9TVsSAnZWagAA13UP/0zavC6Ma3M7j7UuEQSw\n5JvQ6t1FF+4dvCty7p5YWX9B5BT6KNW/6YBbqNsXF60VwE2obm7i1oCxhqzo\nZzFFLgVMBcghnvz67zJmIxt4Nph3Y7nXYOneHe3XJHAq5dWry2S3BBjrCG/P\nzklNtf3mrszwo+Ny47OUL4OeMf7mPBwIac8hFDBTY4dDaUFWwUU8IBTe9C0s\npz6XcZqvokoZo0XqaT+5bTLCDF+JIMsI7/NlzHULt/hxwo4k1OEuQ4zJrO7z\ntx0O1rDu6Y48WeGQa/2CnDixXDCQKPm1HM1T6IaoohudfWTmnrzr50XkQIWg\nrwN6wxMa+hWIWpqAELyuhFOZPormXMbrHnjP7UCI3VOIxugHZI1/KgkXBU3j\nrv9TfeFi5seYJ1/sKDj8Ucqm5TC8XubmlTLRI4G8OWhDoGpE6y54qbG62WJR\nQn7GagsIB9EWq45Liy7vQVOa8eAXeUCGXwZ8gDcnUp5w5d/Oos1CADTd3k9F\ndYVFDw46Uh5hY7OgsUjlGk7ylkVQM2yQAkWhGuA2gt+i2bwSavnhWQCtaX+4\nki0pLC7M4/KBoS+STYdUzvCwRi+BFEGnnKxKKPDoPHvc3NGV8K83vmYG/8sl\nlZuSZYXw0PUlflHvkrlk/upQTPHgvvHQepHSnnjLuX/tu1GQT7W5Lnyk62bW\ngz9o\r\n=qdat\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFUxgH17hGk9nrm9iCYzmFxgaPs53rSQ8CG4bi9CdM2+AiEAw21hm3UN/DuLrYZ9jeUjSEhVfp4VUXQUNDF8/Vg5wtM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-atomist-update-branch-master-1544181930259.20181207112641_1544182162704_0.09394642767053041"},"_hasShrinkwrap":false},"1.1.0-atomist-update-branch-master-1544185950404.20181207123414":{"name":"@atomist/sdm","version":"1.1.0-atomist-update-branch-master-1544185950404.20181207123414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181205110517","@atomist/slack-messages":"1.1.0-master.20181115202354","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7305084cd9f989165c8534f0e29f17da41bd0f42","_id":"@atomist/sdm@1.1.0-atomist-update-branch-master-1544185950404.20181207123414","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Br1PuKP6uZ8Zs3D4RGHrG4URdK0dI1qZA+H0hy6SRtEjgL3iYO1QTb03KL2CoRinXcxweeFvoAyBa2NefPpIcQ==","shasum":"36e32223311a2bd8c3c88f2985d263bca109c76b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-atomist-update-branch-master-1544185950404.20181207123414.tgz","fileCount":696,"unpackedSize":5449894,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCml1CRA9TVsSAnZWagAANNgQAI/Et2GmMKfLHEz9WBtQ\nkMFJLkBbcdOZ8KDMPxflJXjv+VmR1VHfGQJyvlSTyQCSde+tu0w99J7ndj1j\nkPJGLvJ/CaoASIm4SjWqUva+abXJEWnwnosVgpOw9LJfvE75ZgxZyhhRNNmd\nsjt41CzL4hQ3M+fnANGjfyZ5/5NCQBQvNNQzh9+fDdsbk+rrc8kIGmRWhlYO\nf/7iJVvPdojXnUIJfJXvxLo0Z2ut/bbzOYB0LCKg60iEbb+CCyQfPEfXna3Z\nyNs+bgIsCGCP4wEgp42krawaJ0+eRBdKfyTn0cDrQFENXbQ/bhLLtRFaX28t\nBQaQNhf2jxCJprJ295iv1dpRRWxuJ6hH6W8OrbL0B7jEXP5s0LPA3Gd0IYhz\nC8IoGHZDMBJqbjpYoLFJY66NWOPqPC04HEJgCnV3gbU7tS9vOu8qwRIBb5dV\nf8zQqWUqt5GlxAy/M+nwGSpwXTh4u89hnOi6yrUIeEhdfC7LQZNzrwKHZaXT\n26Hws9yoVf6HRbQ+bEXgCryUIubE6oaIgzea0m9r6A3meqq8v653z5JVjaVt\neSP1S+8B6lh7JYju0pS+E3jRB3Lf39TlRpht18/OVnThO7x5IAeGF67joH6E\ntIdPNDq/jTX0s8bQme3H3GgVapZ70hp6sOJj16rHI/hJywunJNI8zd/ExJ1z\nHrIA\r\n=jDzf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCHw46FC2PG8+Cys3ih638SQ7houSWZ2JCMIcBG/6tXiECIQDywcAXseIYflv+dZK1KNIdvGQ0SE1se5aNPterM10Adg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-atomist-update-branch-master-1544185950404.20181207123414_1544186229218_0.6802614796904551"},"_hasShrinkwrap":false},"1.1.0-goalset-state.20181207124548":{"name":"@atomist/sdm","version":"1.1.0-goalset-state.20181207124548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"20daa1f155e13d550527d8a1c9d97996d91b2557","_id":"@atomist/sdm@1.1.0-goalset-state.20181207124548","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nO76PB9ehgllEI9rnT+80EZ4cIezWwiXQJ5hmCjd5RRY88E7symvUHynB3NEJluQOUOE4BxnQqx6fbU+OBIQpQ==","shasum":"7649c637ce0c2825ac21e653b3a6d756788581ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-goalset-state.20181207124548.tgz","fileCount":706,"unpackedSize":5443499,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCmwhCRA9TVsSAnZWagAAlK8P/j74wz1jsf4QnjnFwCPn\npdn/16i+jxQh95sgoFUW/Lu0/f+KAuX8JG6TFq8d88EHFnay0/fKU+dPZj6M\nWA2aV6z6vVwtWagNLnaOQKSQ9Ff8oYzRtEtrUv2T8x+cj/WO9M+x8i7eSM98\nsOGsUp935PyzRy14mDTaclcpqehgXB6KcpE0AI8ilswop7cSPaCrj/HXzuKf\nj8qsDQcuIm7jv6b0yRs77no5Y6ie4VDkhAmiEx6SK7vn4SiHhnGgBNz1znYA\nk2pBStGuGp7JMdT+GWjTHMuhYaK2wYO521XRrhNaiIb/N22HYlRyogFyP5kG\nPZdtHjU4FcTHUkQGaslcXv736oxtZ0T3L5RpTrMP5XexdpHhK5BP634+9svH\nJjDcq6YBPreg3ay1I+1m4rBQxnyCDILH+u+hqPutgVg0EhBEfdJlAy5hDmtI\nOSq/aIHZHf8qu9Ko6SrR5XpkLcJWz4mYWB9zExz5zNrVspFEM/9rCuBWHZHH\nUy+zUPNp4ygWwQ7YCe0xWpEjhPct1HhSd9Ob8zLelu8JuuwWWubCm2rfzCOj\nGTyFDxdocPmwqPHSdFupY9LpJFCZo9OpLELDHk96/OFNos/sOsMl0caSeM1o\nOMim3YXs7sksEnoeDIWnVfFlTw4BEaOMZfJfQN5sM3hOjGNmPISUxlwxFZ6/\nJxJe\r\n=lk5Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHXPbNl4NQgm6mDhE+QFFNJy0S4F/+d6PH3rU4Gvt+QqAiAzBngPG6Rc9XDcSnK9jg9pYGDe+IVcrCb6ekyfBruMUA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-goalset-state.20181207124548_1544186912867_0.13408546192021165"},"_hasShrinkwrap":false},"1.1.0-master.20181207130717":{"name":"@atomist/sdm","version":"1.1.0-master.20181207130717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bc6bbeda322a757942f48a0667db53a0a3051cc9","_id":"@atomist/sdm@1.1.0-master.20181207130717","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7JOXjoGa1PqiXFgTfpzhCXMSN+BhWiIQUsR4R5Q+5l6CMVFxPeU26jEX9Gd+q8b4tIqAr+vTQiJGxBzW7wuIQQ==","shasum":"fbaa2842bad3f89b0edb922f85ab00702f84188c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181207130717.tgz","fileCount":706,"unpackedSize":5352717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCnFWCRA9TVsSAnZWagAA5hkP/0OGv+R4PMH230jASkHm\nMbGle/2MSkSv/Bt6QMo6IiX3e27OeePKJgg24Lq5GZf1j+F5VZR1KBxx/1wu\nFEfuXmhX9d5sStwutfdgN+jtNwLAF7hlkZ05sqv598qDCZ3GvbkFg79+w9gF\nNa5B/+U/IkwuFKdLHAEuQCQ61JhHJTqPTea6+9iJr0LrFdhgfN0XT7KcZ6HG\nJMnR6XXQMydCX1QoBNxlvg93cAI8mN6pHOdpa2XFXMIqf8wlYMjxMkGw/UIm\nmkNK66HiPoZPbAr8/b4S61p+vZ1qTJiQJekRJCUHXtFvd+Sn0wIY+oeAl+sn\nCChqaaVP6SoHPEu0VYiwy7phxOH/6rT1lC1w5zAMm+ScFC/oKkCgupR94clD\nI/IQ+5AhNtExV1OAdbXEif/8HwngU0BSPph1H3pR2WaVeWp4usgSpCeeZGQG\nmIA+lLi4WutmRLNbE4LAMSYpPoyri57rzeYx74h6WQ2XyRBK5wTbK+uogvsw\nAZ8sfmHj9D/KJvw5GGf6xB6+qPUL6QyrDCwFUXIkUiUBH+mHBYrO4YS+ZgXx\nCE/fn0EI0viUJgXQTEIKjaEVjsWpSvaRuNu0dt/GeevJSduyD/rzBC2ot+8+\nX7IMDLuVRAoDySaGoD7foSTjYC/uGVAXXRyW3QGTnZktvhJTqgSq2IIaDCte\npkzP\r\n=ecvQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTEVpF7yOOqSPJgfVX1gFTMicssyQY+Yvtyeq9gucZnwIhANx9HyaIBaedg42qLOoX6LT8nXabdAWZ1pxtrvI65frm"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181207130717_1544188245394_0.6740358153930868"},"_hasShrinkwrap":false},"1.1.0-master.20181207130842":{"name":"@atomist/sdm","version":"1.1.0-master.20181207130842","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bc6bbeda322a757942f48a0667db53a0a3051cc9","_id":"@atomist/sdm@1.1.0-master.20181207130842","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-851gsAuk2NA/sQIV2Qz2M8HrAA2uQhQhY4i/P2U9sW+3YPCN/JRaOjWu4lk/y+aL42PifoSSHl944UcOB6OOmQ==","shasum":"4635b9be4f369e945e77ef2a973e6827dc31485f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181207130842.tgz","fileCount":706,"unpackedSize":5352717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCnGACRA9TVsSAnZWagAAdFIQAJo7ZbH1pq6rwObq3WUR\nX8iw1CwfNmW2yygh7Rs8re0mK73LK44+++TgDVJXZxyON+B0YGBq3Yy9W6uV\nMURisLij6CCepFMpJMIEQWgIFkr5mKBDllND6+SRgV017FjD4LkC5fJAK41l\nBZYnEUUjpYr7WbkWLeJZNWCZpUZFaloOrDt2syedXM3J+E2fJRR6FrJBF+6F\nC68YCHi6AIrMk0rq0YrGUKbNwCw4IzmupOzSQbUc7QLClhhrU8/OjLu0zVRX\nk/Nc+xtvMAwjS2lJOJ1iTGO11+NbF7DTItuyxDweKIj0gTQYcKKoL/P1yGTH\n8ZYuadiAFAms+FMFbkIyDrs1GqVBCppEYMKD+r1fVpCeVSwnOyz+3f6mCUfC\n0wHN2fU+14kXJF7epQbwkciQBUZC+3sClLKx/i71PM1qy13yabgRoqt7Plpz\n2IqfQlf6PJvBstxDj1IgDGKmNEcoxPlZDQgvleSgarcrRDw9eu/LLAa9Wnnm\nuJmMbQwNQiwjnc/LN7i4fVmDaUaRrKoY7vhSkR1AtM5jDWP2swKTpXKB8WUl\nfPgv/uJS07G3u+qQ2F5YUHG/U7qumUtZj9a2g1JCP0sxR+iwJIxt6xG8DSmA\nSwElSxJD2rBL+gYF/cIgvvA4c62JRV1mNuXmqZwWAp1Ex6NlRiwwgMg73COH\n+Qw3\r\n=9bPv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcoXo7ieHltNhw/NzCx5kPwhsIJPSozkVGquX+1YzjNwIhAN9PGYwIuDV2TdTmjsGZBIYnMZmxknTzuwX3QpdFll7q"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181207130842_1544188287245_0.22811652945148064"},"_hasShrinkwrap":false},"1.1.0-unknown-goal-error.20181207142836":{"name":"@atomist/sdm","version":"1.1.0-unknown-goal-error.20181207142836","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"db83279817134b1cfe77248cc56bc984a5aa0c08","_id":"@atomist/sdm@1.1.0-unknown-goal-error.20181207142836","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-g+PHan02bwlNjiIQGgA3jZNvePhV459CitwpCVN6pnJoiORljs2NnzXEHgTyetvuwjEhefUhm8xpY7iUO0sU/Q==","shasum":"2810ab6775649703fbb2671df1cc627bdae2679a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-unknown-goal-error.20181207142836.tgz","fileCount":706,"unpackedSize":5353397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCoRgCRA9TVsSAnZWagAAfjkP/AxA8XsXUxx01FKg0l4M\n2JpQZB4VgB7jmeNwrbplndd5d2o248F++52czKhDE6C/JMGe77r5h4dPDlHo\n6iMEvc+XE3YsPy7qpDBQAEp+s/RsE5A7Hudu9fpNHBg8ULNWMFwhMSwwnrUc\njEj0ztuofg2O8f0vZXA7meOquK8gtXAyzIOwWL0THdV21NHS5EczEOUC75Pt\nt4+3B8L9GtgDymPONTIiHbwYF4Esu+QuAtsVywTIkgCr9OWwlMDMAjPFS7BP\n0a6rJWlPWKJB7r2xhfgt22aV01x3pD/RSVUbHfn7Fro8nM8GGnVoKu4dnqH+\nmKrMXTGv3qbElBiIZeWuaUio9irvhvMsfqirvaCZnnWPe/Jzj70csNHIIkGV\n9WfB/dGSlkk3ozpjLPD+54WRn71ookSLiwaFeLqnXlSntdK5U45j/rKztWEG\n5w7yHS2gaIhnAmswLq5WaxPgZt+hCqgBHUltcTAEz/+APLyoeamSObGJgQfK\nHbUG7eFia+bPQgpJwbUDjCFyG3s4tqMtoUa6cVfhq26PWYl6iwYQlPBpUvyt\n9bV2QsQ9G0NHgWm1gi1r8Nu1dU2PXIlr2gwCP3gKzzGSPQkczG+WKw0JSXzj\nUogqElzlHqrSEnPdpFkb4xbUTWykP67rtRV3CGUUYiLrSHtyLo6DsoOvJHMT\njdMe\r\n=t36j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiEE7hWBp/aBZCk0Mu0v49HdwOvDtTH7nFr311K10qkwIgFtSXZr1aM5N25mSnuDIr68D/zF/TU3PMjgPMNVGELJs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-unknown-goal-error.20181207142836_1544193119200_0.9940156916781453"},"_hasShrinkwrap":false},"1.1.0-master.20181207150021":{"name":"@atomist/sdm","version":"1.1.0-master.20181207150021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b8e5976e1a1245375dd97d7888d4c4810b609277","_id":"@atomist/sdm@1.1.0-master.20181207150021","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-V3dbvjVsH0m1uSgZSRxj+D0k7HEQd5DGRScgKy+F7hghp0dWlXrUYv7dSe/AsHgu+ST0LjC+7ZQvtA9CuWxohw==","shasum":"367a15fd856512120ec67d3d78138771db241e3f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181207150021.tgz","fileCount":706,"unpackedSize":5444251,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCoutCRA9TVsSAnZWagAAuQoP/jy1bQH78NRDLRKRoIpa\n+H9w+jlMPeNHENpRwyyHqRkn5IYdDXYQGq0qiUin5R009KyGqN6t/xAJoJiW\nwlxrxVT2BR/1LzUl40408fGk/tuYeDSPA1piYOQX/A3GMe5d7KMagMoS4yvm\neRKs2tsJbnfGw+EZ4zJYmOTFT2eHNIytYegq3+aKlmqq474PtLxZVasfUOLx\nQQamli8eWL6TOYmSKsQAeQzrJzC7xyIKdnNAlJwbUgmDyNZ+NAxtUBKnB/hB\nhP3n0+twdRF6KgWiO2NoiLI8grhmlA+RH3rXp/9+v8gFB1WUgRZx84BUYWq8\nFR0TxbqK+fpGFK+E6JOgoPZEcqDO+aj0OCAxXqMoMU2M6w79VHrLoSf3Wk62\nrsKsINJ+kvpev0OsPq/cEhCXMvryRzhzY4FhKC+932Ax9R0JBREZEp3SdAex\n9cdLqSWRHoDDdgNMnBmfc5VhQnl1G3LJcreVp98Snin2NFlqsU7N265jFXfC\no7T7ZJaLPGBztDgF+Nr74DXLut4wqIThM0jA+YwBagJbB8hh2+hGbRpQP6Ay\nDuDGyr/baQy1Rw/qqaqDecIVsX+dyI0MZ7xo6QojRPl/PSyd0wF7hmhbmprV\nVvu81RGEitbD5SfN5PVOsZPd4xf5KPQjsfzGmtEHuPWAuozCddXNjWJ9CfiE\nzNK2\r\n=hD5n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAnF8U8ZSgtBwf9jdqJ4BwpUB7LpbLm/XdLLzI3C+k/VAiEAjhKJtD3x24NWlUJj5y253joqyoW3IVtP7mH0wmxkU5M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181207150021_1544194988304_0.7997562848213788"},"_hasShrinkwrap":false},"1.1.0-master.20181207154036":{"name":"@atomist/sdm","version":"1.1.0-master.20181207154036","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.0.1","@atomist/slack-messages":">=1.0.1"},"devDependencies":{"@atomist/automation-client":"1.1.0-master.20181124171043","@atomist/slack-messages":"^1.0.1","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b0f50c108494377fb12ba678489a49768661e158","_id":"@atomist/sdm@1.1.0-master.20181207154036","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KpfASXEhXg8p/VMJXqngnGzKreRiFNZFAVzuEphSbLw3ZoxEFEj7HV+a0CJXGbFcSopthva2Y1kSIasl0oTMvA==","shasum":"66db74f31becd15deff9e6513173b569063c4e88","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181207154036.tgz","fileCount":706,"unpackedSize":5445785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcCpUlCRA9TVsSAnZWagAAosgP/iIGph+53Dxeobh/NT5K\nwtUeGX+eM7Ky2LxBU560Sv4F4GC2rIjXCqB4CPBaOIdiXUQwuQFb8ij3NR+A\ntqIlCST67a66ntqI2Ij5kyIHWwarr0jdJV/eT/xJBmJZTUP0+uouyziCpeAC\nzufZDeOTdwWSzTe2rhLpA8GSZr9Z7SFZcmXWN56p5WwHarVMsHMjYBKD0+Ot\nreTo5E7jrq6vPBI2q8fb97eU9WFUgwpQiMUpKdRAui+G4WGbC5fHf6pAgLGM\nY/N/beqgFM+Sj22SSmDbhgG9BCFYaGSoW7FVjEYZJoIIhv2IlnRwryFiB5LX\ndek7QUnJEwFysYGptZ6kVzX7/7u1ElqRX/geYAluqleRggbgWs+P9PPDcl+I\nVfd+e7oEoU6vecBNk7NXORwER9NS3dxACzB/9MRnetPx1I8M82JZfp1U/J1u\nn5VJw5lE1JLc75s8Z/WjQihfHqMDvjZqLVzjE6anApg94dbZYP0b6sen2dFE\neXD6tKDwalvqShanLAFI6qbh4vYBjIzgRKv/kKEAbCHSdllJRv2bydg8At6U\ntq9Mq+eO3BqD9yL8HAcwn8+2YeDeX97GFeOPBYtywbDiSky9fh98eok7Xgso\nkeb9GrJKLbWwDZAM5yKEC5XhcXYKU8CJxTRX9dhYkEGePWzjLwUZhpWEyM7b\nkI+7\r\n=U8Fi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAOBUrW+jSmPhqhksh9UNxxe8FhPRFSepaDxHNtzElUYAiEA3omT0/mK6cA7+wnaJbdQHg5mDO3mz9e1lgwPlfWpxi8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181207154036_1544197412549_0.4681392760657066"},"_hasShrinkwrap":false},"1.1.0-master.20181208220140":{"name":"@atomist/sdm","version":"1.1.0-master.20181208220140","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cdfd64bc2aa6c9983cd3c3977a9e007b6c20a293","_id":"@atomist/sdm@1.1.0-master.20181208220140","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tS+nz+e6m8gNske+lmJdiAOxbjIOGgNZZV33e2ieHAFlTrevdaX2T1bR7KMEO8a13oQqq4Qp5zj1cvtEygIk1w==","shasum":"4bd941454d0c1a26771de3dd289ffbf58b126afb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0-master.20181208220140.tgz","fileCount":706,"unpackedSize":5446957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcDD/3CRA9TVsSAnZWagAAIrYQAJNIJ90jDF+2ZrlEswsS\nMnVz8hwOGmyP5fKYDEZHfO1h6xs4ELc0IrYy2Vby6ib/pp61v6VRlDvlwutm\nqhvDo638nQrjMN6JFFn6Z7sA/FPCI2PoLT5OBC9ZexK4aqJM5Vtdw0YBx91e\nfR0MZ80Z65wXwbm2scXd1bCZ4bsiwy5O+rMf3RmLlU3qWKr9VHv4On12y1oC\nAq9iupJa00XQ+6PDv84Axr7Bh4lmYekE8+plLytBoSqYjpulqYxbuNX4xFh1\nY5g6X1T9/6XaUu82h032c/ta6cP4xCQ9/e+cfq/tqUmPk5Q3Ln8gRhPdiQaq\ng/MA1oXHXm/fRFmpvadEgCcTGl74eMtDzMtMIM3XTi/GRfMiYMcAos0n0mue\nFXLQYui83TkGVNok7p8bIRCQb4QfoBsjnNpn4prPKJAD5XZXVip2OSi/qSYQ\nF3vezNk01CIDjLn+BRA3pruOSo6DPHaUFTNA15Fzx61TI6glJmtbUM79xafY\ns7V/8fpBXS+Hy9hp43koiis9ItjhvAS4OClHFfBFEOPq03KHgfkRoOiExDX/\niscAY4vO/0zAyQ5Qdrli3zLAmOvihvS6I8FVe8zQrt2tt7UKJlpqhmMrzKfS\n4eYmem8HlkMCJqBATTEdEoD5hWR9+F62yhfcxaHPUzUNGLaCXgWw0GKHP/ng\nTeiL\r\n=0egM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDPO2ksJka45QhHdccywmoAkIX0SP+XxqBl1AC9A8HtiQIhAOrz/mnjTI/vArnE6NZckVa4JCv7ZEEUERwsFA1+hVwC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0-master.20181208220140_1544306678230_0.8152781052452798"},"_hasShrinkwrap":false},"1.1.0":{"name":"@atomist/sdm","version":"1.1.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"7426e6ca38fad147a511c28869f56e54ab29b478","_id":"@atomist/sdm@1.1.0","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-CdfB8vhmTRhw0StdINPk8K4xqQRL3ooBkRChvzVFEq8YvpbUze3ep8K7SDfijozdf2bVkMCq1ZvTFf7soa2r5A==","shasum":"bd5ab83a5aaa9be1191837f288511825a2913885","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.0.tgz","fileCount":706,"unpackedSize":5446935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcDEEgCRA9TVsSAnZWagAA7ZUQAJ2Wyogrisos0VLZBTgK\nlGLjV/pjouYPegSB5c6GN848e73ESnGigM7BHNrikr/jnEsqz+H9WW1pEPBF\n2Kx3vqrCOgHHF7dsYY/de/NsokFCevVtdO+BaJrw1vE7hSCfc6I82ERdDx2+\ngP8P7fJFu2iLF5bbHrYjoTrs/h3W2QrJhOUSP3ZrrtqFoZLCY4cUh96ZRB5f\nTLLt9bL3pvKpqWj4RvxXVmO+FR7vvoXPd88LNhiHFn5h6jOl1yuztS69PoyY\nNA6ZtRsLBUEutttbgLaQ0bfBjKwcTPYkkHajjoPn0dYZCR12Qi/hTYoCwE+D\nPGhJvcwO8xlA/jdCv0VlHcg6w8tLpx8PUk6Wm2Ho7NUhqJq2j5k4zbjg1+7c\nT3Xpryv86d3RNRzTzidqHRhuR5f/25HB1FlC/heGaQ6hH7DdYU2YlMLAYKax\nkABN4ZQCuTjTTA2C9XwsiAHgtRN7rsFblIGwGeRDN/DWpHSEeuuCTD1gu0eH\nSIfkfn8fM5hM/eihQvUJV1vi75LWah2Bw6CvkKly50v/NaazTGkus0RAfk61\nmUd4aqn8hhGSeID2AvNI1XKS5w7zWmbD6Z/7Yu1W94uJGYaA74Ny7KsTa8CO\nuxv8MtIcxprFhB95JnOQlmp5/PsVwlMUqG36kibiNRdczZAW84jLk0pYZ5lQ\ntE/0\r\n=KYxa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEBxjzJudLlzu4pRU4YqBS4ezLFyVYiY2H1oGPoFK+CsAiEArL71mkeqrO5wLlo3XrBodUV0dNSV+ClrmmsQUW+b8Dg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.0_1544306975736_0.24765146867423726"},"_hasShrinkwrap":false},"1.1.1-master.20181210044844":{"name":"@atomist/sdm","version":"1.1.1-master.20181210044844","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"627a0b8d610f2ad184118fcfc140d70f1e7bc5b0","_id":"@atomist/sdm@1.1.1-master.20181210044844","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mDEgfD4ET/IV2BtCtuEVpevQeoAGSHfzAfGF4H1v2Rk/thHuOrcGAn2ycde2KKC1o/fx3Jdi5jxec90vPpWVzg==","shasum":"a821581e28d1d68b25613d9d73115f5f649a8d0f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-master.20181210044844.tgz","fileCount":706,"unpackedSize":5447052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcDfDfCRA9TVsSAnZWagAA/PgQAISeyfaKTfM0/fmxu7RR\n7Lm41bdvlgiSj5Ec4t9RtoEJyBltRmjpNW0b7DdQIOxR9aU845WtteHaUG5G\nIspp4Pkzd08kgtLpa1T+gF1UXUfmtdlrv2vEKUbEGMZjCA/98DmD5Y/sqwNx\n+T4B3iLvJ6zNPNI+Cd7NQPAQXn9qhTTY/uWjGhjj6FFhFxoCFvaDeB2jXUYZ\n5rCGGjXgpR4JQGCfoV/onCZhbZDBoo07YEfFqe9btokRtgKqUk/ycLYGgf9U\nHOqXPzGJ8ab2q2zF7n3Woogu8bZuBLuZNH7aAw84rHximVk3eKHXIH3OxcIM\n+oatOTpRBWdWpDKUokPANcNsJSOnSKe3JrzySHyWBC/UODqGFa93YJ/ah3eI\noq0zpaqDKD2EshJ6SqJbQBCCFQvI1u7ysO6ePRIHCPCY3vH5Btwi9GlnAK28\nBPd3IYTvdksUCh/YuCApDgOVIMAJHU7lu4FJCOie5MfdLMPUbX4qj68z8TnO\nIFtq7G7YogDNcZIzJ3UHZVj6tQYyXG3ElJwzwpDo2OdEMsghi9pzK06O2LW+\nYhE5qcre1EoHeCFc3hny4UGfUtMyMLP+FfOuJva4PG88l6VJKXdNjQtaDgtY\nw5asCQU+daI/M4MruaCJX8VRNv3DfcJ87NN8vq2/uR6wuHmVBNz6fjgp1vrb\nJI/n\r\n=ZY9/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCpjW2aGbz17Y3MofZYmlAzN3rDUs9b27ECjex2UrTZ8QIhAPD5iLdB/AIa/wBSzjXsvOjF7mj1rxbUHkZ8uAXJCMC0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-master.20181210044844_1544417502436_0.664503412907975"},"_hasShrinkwrap":false},"1.1.1-master.20181210121555":{"name":"@atomist/sdm","version":"1.1.1-master.20181210121555","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1b94cb5f09b7176f85249e6b7dd7d1111cf0d6dd","_id":"@atomist/sdm@1.1.1-master.20181210121555","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cYtq/vxbdxlfCrxSHucIS+9QiZIurPHtQFP19juQnntL8LCL0YrxFQJ18fpZ9jC6YSx6RUf3l9y3d5ctgZk47Q==","shasum":"591dc833bc72d582a8feddd42d56611f0aba1835","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-master.20181210121555.tgz","fileCount":706,"unpackedSize":5447536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcDlmzCRA9TVsSAnZWagAAJUIQAKS8XvTRQT0oTYUryetP\nUb8akhQwdQgtrXSz21q4HwCMXAMlc40+OzlxOb4+IpKgfKkfRjUQVwQ0vE1q\nS6PIu76JiC42nOkNaRgG8RZKHri/stjesmjzkyt6QpfEeP4MDlqI/GZwgdIg\nyGeMjzYX8X0kdxYIhX6i1e6pF3NCEkq8bH25FbH670t53pABVWrozHiziaQO\n0YLvqZJ9DVRVZEnoVzSrw/cPDLScUwc2Ju/fSr/9MJw+N6BaZi5BpVlwlON/\ntVtBvclMwSdXCaNZt5xRTa+/TOlzVuMbfWBlyVbnSXE5Hq/+qu9cb1UuwTM+\n9+jbSa7Fmts+YHp3kpmplSF0U8rGll9su9eSYhYKL9druAWo1ju8zfisGkKb\nHAsTKV66eMLdWICMx1noQaJbL1eb7JTav9losCjZSK06meDR8WINC/4xXf3H\n2AvgQhqP6EfrERboay4uCpYIjHpfpHOOV9pUawnecXopEbp2I82CjwkSRXgF\n3y0gkOI1V9VhbviS9zStR5QKHE/wLp3tkNBKlw6qHOh7dLrMptsG4f9uHk0I\nG9E2C4dWlA/zEvaEZ0Hr9fXL0v5/Sy4RAcIjCnl/W1y0WtAe23BnJy7zcvc3\n8XOWR9hQ1BV2E07N9iHrTAMgIUbfzM50MFT0O+c46luaAeczipMfyHBB1HEk\nuczn\r\n=b+VV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUlxG3ic6/Nbn/BMcHh3TJPabsdxUMYLVuShHGq3BjlwIhAMdfoR5flVVSy7yn0IAmFjziYs4wzVHWcpV+5E05cHF+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-master.20181210121555_1544444338748_0.18620817960663238"},"_hasShrinkwrap":false},"1.1.1-master.20181211135728":{"name":"@atomist/sdm","version":"1.1.1-master.20181211135728","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bf87e21bb55d64653a4815403bdbd844c780c0aa","_id":"@atomist/sdm@1.1.1-master.20181211135728","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rOAaJLRQBdxASh2fuDkJS5uSZt00uwtJ3HKlSyd8uAGC31+js+7mYx6/5XbjSRGXa9GSlkWNnSdaJv5cbfSssQ==","shasum":"f150b79eeba7421790e6809d73770d0952ba4944","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-master.20181211135728.tgz","fileCount":706,"unpackedSize":5447864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcD8L7CRA9TVsSAnZWagAAc58P/2M52g8IJKBJyy6V4bpq\nPaHx/b7rKvaowKoE8rFEbdKmS/BZnILOb3Sj6DIUPU32C/fTSiB3Bnd0OdAs\nlFp9jlzSVDXE3QoCcMvS0eINOT2V4vf1vRuoK2A9ApWfqTiJ779eDcImMqEA\nEGk7E53Rmsi5+ougSxD/rHrKuvhldmDnbZtSf3qCSsY9iAbdBrixxu3veNy+\nbAV2PEuJc5EvgV1tX6oJ9W1tLgE4s9yddjwd86IZtUwyfYzaF2YQS4u+P03n\nj1Kmj6xqNl6C8ov1HbAG2hKdNfv/WOcFWAwe1BJ/iDWnm8eRh8wNe9407YsO\npgw6wW1joMtJ4mZTzgYY0z72IAg6fdp4tSaM6kSFRTSoIMrffYp3g9D1y9Sp\nZYI7Vj2xYRaEU9fqCibkO6m8QLXzWzfHlaC8IbZ1VU9+H+ckxTgeEjHloZSY\nW16tHm8rxzVapCpW4orU5W2T5qPABmFBK8kLbA6799YmeySehs4bMjy4uPFM\nntWDQqvYRdPUhqEHpJmCbMUdK8qt/9jw+/Q+96x5aY8e0k33Fo/ELDUoF4HS\nf6AaMG84vY6aAyp+JvMETKeKpS/GJJnckcrxUzcGdxliTW6H32mp38BPkC/8\nkbyelb4HE1qx717wMyQe8M9pXMUmhcghQ9GUojeXPNprOZUZAUrUZ1FMHVLq\nBiJ1\r\n=lfdV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4ehNKN4gWUvs4LWg+jclRoUuGj/zW8pC4jArAz58TawIhALPdGUP7aaS1tUIgrl10Df78Fgkx3Gfnt4h6JMKfJ5wz"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-master.20181211135728_1544536826877_0.864754310882955"},"_hasShrinkwrap":false},"1.1.1-code-transform-dry-run.20181211200115":{"name":"@atomist/sdm","version":"1.1.1-code-transform-dry-run.20181211200115","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e2aff6e965c32cb63efb40db4fc3db8a7e17704","_id":"@atomist/sdm@1.1.1-code-transform-dry-run.20181211200115","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yU1GSsJFEfaSGElZDSaMOXDS5jf6x53Tqa6KkR7qp0AFmtf6aHC8UC7Cj64iJ4spSHLcOXcM8qNe44JqHijMxw==","shasum":"a34d448cbfd0a81976c29f340eb149eb5513bac0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-code-transform-dry-run.20181211200115.tgz","fileCount":706,"unpackedSize":5450333,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEBhACRA9TVsSAnZWagAAmrsP/jzKaqyPxSZbC96UaklQ\nXmnTb2nVWUaXWmK3yGL9Y9Kc2TyGyrZiWr3Yx9JmVZLDoBq79+007jasPcw5\nUxhxHT9Wxwjdax+BsZWKBw1JrJ/CBMO4Ik34AvGwwjmOoJIvGGtTQGBPtNbc\nhk5cn7Zbw7sypCIxXjYEfnTYetIFVGKfy0cRZ8p/bIlQ7L1iTunnl5adakBf\nWa3XjRKpp4A3A2VlZeVGcdTdsGuRg0OUaWu/mkF9wbgB0THSFDGvsLfTSvzn\niz/Hzs8YlXdiVsaKakz1HBquy3t18c1rolveUkj17VctZDEEntBUAD3Tslgl\nhyfbYAMNEhfcOnvoxIAxiKOMvyDY+kJF0KLmgYue0dyKYU4Y33/LZflWmtkt\nKfu95AegtuyjEgbdPSjVOhQczi7HuVlkGiC7SpNNhKPD1xCIXVzxWFk9aFRZ\nqG+qGXmlmWaL5NWGqBiJJwSVnTblvd1qkGAaQBwsKVyd+EKxSJtdMqYAx2Yv\nnMGLMqi7VJpqRlzwdTF7Gq7/quDMNqcTVEHb8WhVWz1rRyQWRBIviqUbjkLf\nM1rR9oDu9cb7jU3X/gFHcDMvMk06JWHgLlY8wUQYtwWpWrMFnwcRQ8pV0nbn\nZfLQnHY9YSZCP9ifa/hph/ZOUYkQh+epoxYWWAQpl7ZOnvbs6q1CUazCry9W\nDG5Q\r\n=Tw30\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCnRcRSwvv0LldKGevWJ9bLDpEQj2gMpBJvcXP5q/xizwIhAPOvAay1oWiiaTOue/LZn+ebT1pahcRQtmW81Oc2gS+o"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-code-transform-dry-run.20181211200115_1544558655764_0.859549389452843"},"_hasShrinkwrap":false},"1.1.1-code-transform-dry-run.20181211202316":{"name":"@atomist/sdm","version":"1.1.1-code-transform-dry-run.20181211202316","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1cd85ab9649133f1f8c1acfc6abaccb2ea8627e0","_id":"@atomist/sdm@1.1.1-code-transform-dry-run.20181211202316","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-o5gmndob2de8Xw8f3R8D7KnerUD45S1Bdz67c1BO3cIAQUM2MdzwuOMUyGEcLRsvwsA6cp4Gon6zEKOvNkVqyg==","shasum":"5ce9864881510191a0e039c89337d24107b9b762","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-code-transform-dry-run.20181211202316.tgz","fileCount":706,"unpackedSize":5450606,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEB1oCRA9TVsSAnZWagAAU+IQAICxCyq46oXY+kQzIE5K\nNv870E1MkQZzeqUjYicputz2+NsRDQb9MvjZvSFSE/otxdF105ws9oXk8h2T\nezlgI3d/jXOpuWjpFqjKJjgycIcWrjl6FrN1g001oEW/7VO23O8MPHYEDhRd\nzge++auFUxdQKKx3Gu64t+A5xd3WWvNf9UYfsW2m6x/ecQb/xIGD1zD3zgqo\nPWsgFNadI8Ay9jx47sx1Pv3jifUIaiBbN1tDAyQ0LlgC/pKidwjo2QjCR7ff\nkpCQKO4p/ttggLIwKX9xyqFlTqqtNr33dOV6xgNZ3kCIpGRtC7eYHXsDoK7z\n0/tRPwZ1B02rekO/lLkxyuWEbK9kjZkBiE6xFxrHpfnbYO+JXEz4JByPi0zt\nWkX6mECL9+egA5ERh5JB3TzoY3Ybj0ut4rGNf2nLOFjvJWVgJeE7+4U4mglM\nQqshBJZQcBDQAVMCygE66TigqZ6nIEZTOLygEm20RlJKxCLhWKgNRJ0dTrSj\nxsiJaXzmfCSxUqWu/IjLs6arwz4pvVT2gySmEm5OQJ5ZScxu+2CiK5rc5MA5\nnfFFgYajhhvzps4p8HUScJp+tNi2WBRCCDwhESBHKyaD98dXQp3s5ar0dkDv\nM69P6oKl9r/uosRFQny7T+2njsTHrNuAt0wC2nV0nI8h/Uyj8zY9OM/OgXTq\nuFUj\r\n=kzjK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9qfauYj7PF8AJYtau7Y0i9G1CxEv1KWGcIYxnVNWZHwIgd3bhdtc9yJ0NfDzMio1tS3M2Nt1aRs7U0Fi2Su5akDg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-code-transform-dry-run.20181211202316_1544559976033_0.20442139748411092"},"_hasShrinkwrap":false},"1.1.1-code-transform-dry-run.20181211214559":{"name":"@atomist/sdm","version":"1.1.1-code-transform-dry-run.20181211214559","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6d49b8aa265a9b51cad6d666e0ae99f168bed9e5","_id":"@atomist/sdm@1.1.1-code-transform-dry-run.20181211214559","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-WGFU+4rE1nxXGrGKat6yo+JmbbjjMhpUzEH2htxKZym+R8U5tA2KeNUuvDixbQqGHb236LyyhOL0ZMC135aB5w==","shasum":"6f61be82b4c8a275692f7c135d1e4a99d21d625d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-code-transform-dry-run.20181211214559.tgz","fileCount":706,"unpackedSize":5452544,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEDDTCRA9TVsSAnZWagAA2PoP/3XydbBend/R61AaJlJh\nST5XqRdUMF5e/U/wiFMrDfXKdAaZ//S6YVobwxhgLACycxpJ9eiMMcR0bEd+\ncgpXF2WABA8+EJpxdgQVWox6xrw8HABPI3WQDKv6RGUHIeTbioE+4LNp9MxB\noJdcY2J5U+UGMqXbg8UvpvQyN3Ii5+i35FvZabdaNDZPBSl41JSoXON2K7B8\nwTT6xVhMZ/bVE/Q2ezCNrS5I5aEyAVJcPs3+PECTbccFmDkVsgnCc9jV/5xM\ngH1CCRZkK/7tJWu0umGx2f1W29bcJlu7dnTzy23DXah/VI35hroCYsglMrcf\n0CPSxTERuKRpJJmdbd/K2kH1v1BZLvMKcSDnJMj3hQUlly1mq1oXE0k9Py41\nl8iVBljJSDfTJqBSDzvpV+vyst3BjSA/RN3TQWt1alIqYW7sVznbVzK1o8EV\nRwhFBrFLEjRdgfDf6dcFw8bGscTCd/ixjPQHEM/f3jGJBUijhjWzMwaz9YLk\ncrEM7bcFXUEIgLGmDT8GeRs8VnXEcUelwdWgipNpSpPEpfv8xWuo4SZ3on2c\np6DWOhhKr6Cv9yW9Isnh1lGiIkA4ipObuMSZWsfCEdNHOTygQRL3LM0u+pxm\nVeFt+NjyynUlGilSw1vb9l0NPMOT1l8Gs+QFhI0XIMGcTf3g0ZnwqWnQ4ZoG\nr6y1\r\n=mFAD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHou0PO/UiPqa+jwuV9vM9xxSE5MF675beHxmyahS+RgIgM51xiZD/AHOQdS5jD8G+ZHydU9ZkYG35TYRibJRWlbE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-code-transform-dry-run.20181211214559_1544564946682_0.4208716762283373"},"_hasShrinkwrap":false},"1.1.1-master.20181211233651":{"name":"@atomist/sdm","version":"1.1.1-master.20181211233651","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5890d16438f007a9398a92fe71c8615517d3e077","_id":"@atomist/sdm@1.1.1-master.20181211233651","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4TWt7btsDV4VI81CWO3ynHJFWbXq4F5xDP0MaIPSATmBoz+IcCTym7xNa2re1qvORnoNfX4p+eS5kvha8y0leg==","shasum":"92229ba30ff048660f0485fe6217d6d429f54214","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-master.20181211233651.tgz","fileCount":706,"unpackedSize":5452762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEErRCRA9TVsSAnZWagAA0GQP/RLR6/XoC7rhks/pEzNY\n+enQd7K2cbiDQJETn6aOAC5KpJexQJTVJ7RLTHUJhPP2nd07fiA9lRhZKNy9\n1ofc9XBdEKm48R7Ch9zSwHmrQouxFNApYJkKylxpo4Tb0pD0OjA2F2i66Zhi\nW5WblJNLiBtWoWWVoKHjmlcT6QrN0F9ztLHuyoLCDwsGaTRdjcAqnsefL39a\nG5yRHFpHxU3ov9sUMY5rDmuqxAcSXpxNTZxUcjGB69j+QvhdoyPBcWZvZD/0\njvaMerRCd9pS5Oso1XibLHxfEe1GnhrCtGxiTaVw5fYaoL0saRVUBk+oc/33\nkZmjGTgEQW6JduPm7MFKvEyY7ksc7dIUnkJExlFV7X9TaYH2vLfsTUhLDJcD\nO6Rd79hP+O1/SW9yTQwYTsvhwxUn+Y5STu4YQUC4fdNeCnL7U6lNMt0d36Ve\nqBbh82shrzWVmVoSbI5BGjDxoMan5Nex0GmzCKYTUvHYdEllp8e6+sPn30lt\nQnRRivLt8fInuOhnsBKAAys8RayvKCg3IjnP5Rv6QF4DL7kjmOIThGgwRl5/\nYcgc2nwAVjc73YZVMW9eT36nXBOsstdTzVJ8jgqryXt/eWGzGAyr/PQ1q0h0\nZctzw1UifRj64zRkAUOvyaQenbV1Z2tlRMmEW21VdW05+2FclKWJkJnnYYz1\n+88N\r\n=BIQB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHJo/59IbiJase2EvJ5IjDp/Tw7kWABTYEY8WhfZ9IkCAiBkFrFplNdZviypnepaWbrAQ9/B8b9eukf7A0Vy5wWJKw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-master.20181211233651_1544571600424_0.7072080413565303"},"_hasShrinkwrap":false},"1.1.1-with-repos.20181212053139":{"name":"@atomist/sdm","version":"1.1.1-with-repos.20181212053139","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"27b2ce670aa27c87c6af8570d2bfa38e0bc7ed4b","_id":"@atomist/sdm@1.1.1-with-repos.20181212053139","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DLZEEQHeGbbnd6Ic0AQ14C+iz3EFvc0mDjmnafFNBVFWqBC2eI7xF8b5wf/Vmo/CibdTCstHPtwejneUHIj4gw==","shasum":"85dcd7ff7952c777eaa6cba4f1c5041a18916879","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-with-repos.20181212053139.tgz","fileCount":709,"unpackedSize":5456548,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEJ3sCRA9TVsSAnZWagAA4OwQAIKMbq3K7NQDJWSdm/EH\nbWa3WQjL6FjdVmzjwYO25BgVtc09GB8usJcwgYzeeQ3pF+FQJGZ8o23mBft9\nM1PZeo3chxji/Rpp5gcmX2SinBxpopt2CH+M83YqGNCc/g8IZXIEW2c+gGoS\nx598hCbD3vwj/hUaXcnoT8g8X4cABWBUje3G4z7vx68//r6nBOusQkPn9lbt\nhL56i/1XYDYsM4n0SfocplgEoCYsdioF8NTkNM//1m4Fe0zwNNBj9+lfA/Q/\nT/QNMDe1H7Hvoa46X79GpK0mZ7yq7hdqkgiIo22tuJevNXEy3HPielw+pnxO\nHZNTHQMph+BHYrYsRcHRToA8JnzGU56x+dRybg/NeqE/HEunU9ab89jLmNCs\nvib5bHAQRocUPprRuZjbelPhUO2wHWHCavR8o0MyF71QJ4KfLLX0vfa5raVY\nQQAJVtx42oVWl87CzfiNr//qj6dggeN3DqTQ9WA+MWllnzQOXcnhiFlWUHok\nwzfVbnMyP8LJbUXq+Pt3ZsbX2hW+0Ott3TIaplKFpbtsXgctO5KjU+SaXMNt\neUSObHUkeD/I2I1rwQjKPL8R8IiuCvBmMv1/wdsQbzmHeNpiNWV1WTdamZvw\n55Z0Ic4H+OEo5QGxV59VwtjeTBki5jLQsYT7MqMDpqCEaYmjmu7uoyRsqJPK\nyqMY\r\n=nBYf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE7jG9kgMU7q4jNycS1/RqfiaWszbGF8QRjksDuZ51O8AiEAotbCEHYk3VclZaDvVCJUriaiVbWcWSA9U1Ot7L/gyX4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-with-repos.20181212053139_1544592875518_0.9970980623050816"},"_hasShrinkwrap":false},"1.2.0-master.20181212094407":{"name":"@atomist/sdm","version":"1.2.0-master.20181212094407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9464b80d299cb00b0e146a7d7b668f7351ab41b3","_id":"@atomist/sdm@1.2.0-master.20181212094407","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EhHXUitn/Kfhcxl6RWzZ+EQs/9WOj5sc6lYdG5VEI02NmN/514fYrhTVnAmSwORtK4fiYVSS61gOkLYD5cIstw==","shasum":"1208b400b468578276e439b5b45efdc846eab49b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181212094407.tgz","fileCount":706,"unpackedSize":5363623,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcENkZCRA9TVsSAnZWagAAECkP/iZMtjoueUY06eAp3kau\nMpicyxpalPcPUgg8hLh8Pw8uxQ4VuYgHthLlxheXYQsFG8QaCNKyCI1DDa/O\n6lc22+cKBl2DGXw58UbIK2Pc3YLDCnmAyxOtqmtBmNIkcYnuxZStLqpqUQur\nmFzustZCQGt8ZLt5WvOzOJx3xqVE+jwj6VGlzPFonMEIsA8KUfmDhYJei8sK\nF4vqYrZB5iiwWnGuTtgYH5Bg2yxgDVu6a1EwmRs1NClNDk2dE9Mqx1qHX29x\nDRK+SesmuQC2krGhLiE7sP0P8rqIVCSvJK/W/kmGQzxc+/yqYtIvfeO8fNV5\n1xHucVIUhGG4VqhjVEbQwJFBwRHqvvt0sWHlhIIwGlz2ZdTALM4eh1H2ACKC\n+RSOFWURez5LLiWYGs3iqcXtPrAyaw9JEWgHIer2pdnL57UVl2vr0JKDK3N6\nEYpGsWTNOviQe8P1FW19v1/pPTZiIwzscD7XTN2O+z1kziWyZNpNfgYu7t5g\n7qzisFkDqus1C91rf/OzYUM0iwp1jxQLQTGajjSLj73p8srAtj44+R2crwgk\n72j6UyE2MX6zHoFb4hbdS69WmMVr9sODBWNpTZM93poVctznZ6xZb5U3HMzD\nGKv1Qo83Qpn3Hm1POT4rmoyuFGC8uCWyrfyrngv+tyCaxeXJlLUoCxzOTTXX\n2Pj/\r\n=WJPg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHkQ8tkD1yVxvemT/Gomtw2UH5eoNaCHQakrAYiTjvoQIgH3DhJk9DP7ID0iJjKLA29g/E9QY3mW+b7ybHSx6ZjWw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181212094407_1544608024734_0.08292857945563958"},"_hasShrinkwrap":false},"1.1.1-with-repos.20181212132523":{"name":"@atomist/sdm","version":"1.1.1-with-repos.20181212132523","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"60301702a49c93e8ecac21d4f69b7ed9eb683bf2","_id":"@atomist/sdm@1.1.1-with-repos.20181212132523","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lRMChpBKv4+a8EIwu2d6X892jxciQNHV8iSJCzNpyCtkC4B5tjEkl7vWRsf6NI5oKkc4/FNsNsQ6+OTmDXewEw==","shasum":"0f58d593c8179f358ee315b4280273977012d4cf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-with-repos.20181212132523.tgz","fileCount":712,"unpackedSize":5459279,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEQz3CRA9TVsSAnZWagAAEOoQAKGQ9eDV7CBEVZprco3W\ncJ1QXk+ght9KudzXouhGLOCZiiyQ+ncyhn8GwI7Ry1lrEZvWRq4G3ljosuHW\nFhW8S5Vui3vVdbUPpRoZNimGl8FUUeIFVYxGLVA8yNgAKILgBvCHyiY/2ObO\no46rU20c+1FHnX4xdCYaVU0wAQPjF3bn8Az3iu80tEfVtlqJrnK/EQVGfy8m\nSpPTHYhZkO0HCyUCM6YLLoDk0Z/1EdGda4+CQBZkdDwXX/woRbWWYTy1+r6A\nDWtC3lCdC7op0znGBUZBLD6rfHWMTKMiqZUC2g1UKCK2y/4E70oRzZebn6Yu\nJLVWrgbsF/kg89X1MA8xuvn3SG54NgGrTQ8cyVkE7qQ1ND17J6LJ7BdZLLGV\n61JTHlZkbjjmjp9naSMyF3n73OpVHQZrcyCL5b03zmyKNr3tbAA/5eLiG6HY\nk9qicvBfzJbPFoaR7gq+Ut7f+vO84JpEX3O7vQbdXCiYiqpyV9ng4UIMJBdT\nov/sERBrfg0REa69vhVXwSXmNV5gKnDaroGR1YpVFH7pWCjPOdFKVkYFCSiB\nkfy+FT5Jh2MyaXexatTKad1JNxZBdeG/JtUommSxdDJSAGPAjuGD53KMNOSq\nnBnARuqMaZLhuvXIz+tmfXCk94LMrtqSv2ZP7p8EZC3/t+08SMSgK/stzHK7\ng/Zj\r\n=aV8b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDeTmdjmtbZ29lbBJ+Ugp5joH/5VBpHzxD4sQ78V3TSDAiEAmMVfwnekr5akjrRWU/8SroyazXkWNAA/ZbPXmb5kKnE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-with-repos.20181212132523_1544621302388_0.27746377075582873"},"_hasShrinkwrap":false},"1.1.1-with-repos.20181212172919":{"name":"@atomist/sdm","version":"1.1.1-with-repos.20181212172919","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1d253ecb2e40d5bee8e01b44764d990ccb6c777f","_id":"@atomist/sdm@1.1.1-with-repos.20181212172919","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GYpHFa7Fcx0dw0GJOVKkdqK7JYzgkag8YqLhIT4dvitUZM0n/VE0gMW9cYCogRNq9Q+rdTC4c6DeJSB0bjkiVQ==","shasum":"f4a4f86b3de16dedb1926b2d9f46de3cab1c4001","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.1.1-with-repos.20181212172919.tgz","fileCount":712,"unpackedSize":5459414,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEUYVCRA9TVsSAnZWagAAoJgP/3B15C9/Sld9Xh06Si5E\nFMijSGXCgApqxYbrT8qio/f3NuADpzt3FZA7MEmZwDyJu6RkWoEX5fLrCkGz\nnT/WM8CbbYLeczMWb0X2fif4VCj+oLa8XWppF8m4Q8I3JgbXNVfJCEkw75Ni\nrnLT2LhW+viClc6pL8TQdcIVEWNAG2bclMp+Y+1YE2SFIrH2sqyu7DvYKILd\nbOL+DtK5t1hhOwl00ag0L4QR7SAr33hypOf2ceUf+jcMzuO+uUveF0R8leNT\npeMAw0dStx3+jjAVx4a53Mz4miMpi+SlY6ISIdGEvmqjTMs/9Y2+qHP4X3OB\nCfQQ55KSgVV0h6fQZRteWi6FZOsr+T4zTpz5O4hX3HSXt3dmt5GhVDayjN7z\nn1xOL1OstVmq8EkUUF6VneJKC3rulzGOoLDQNh+PjAwZbe9coThs6pUD9InC\nHJZvC/1Oa4tK4YxGxI3K0InrU9v+HJVqAYcSMcLPtiYTD4QJ75DJM3cvCKIX\n8FhTbm/1tTISD6g6m4sG7fF9rHH7LxLtE/2x4y4voaFRPR8rFn0m61kvHkY3\nn/i+PcYhhUViVVvvtGCuGpO0JlkItsAHsssCBoyvUleNug23rqVEbs+9SZQO\nsgZGBA00lc0h66clGnw4bbFRHDgNIL4MObj9YvOWApcAg14/0z24u9fgky4K\n+xjm\r\n=jqDQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBfUIoLILFcOX+h7leEFM79JqI0wg0pk6iZwMFOjJD1QIhANj9mbj17H3phlwlBVlBwRPPshFLGcCkBHpBWYU8A09h"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.1.1-with-repos.20181212172919_1544635924228_0.7949105186241321"},"_hasShrinkwrap":false},"1.2.0-master.20181212222153":{"name":"@atomist/sdm","version":"1.2.0-master.20181212222153","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b7310846f938376e74b6ce520f7b6d20a5232524","_id":"@atomist/sdm@1.2.0-master.20181212222153","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DfBfTazB4HKhm7d8jc7TAW3RzGHOpB9o0VjOcCUkfYI6gOFR7Mdm1i6papfoHCzV39G4u06tBiKhhOLsGdmfVA==","shasum":"6795240c0798f03c079c7c55a5634e5fdadb055a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181212222153.tgz","fileCount":712,"unpackedSize":5370376,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEYrFCRA9TVsSAnZWagAAdEIP/23QkiSDOXlzdxJIxzNf\nR3mg42kbfYBggwZMreby0TBV5OR6srKPNV5pbvUMN1LLDz8UGCs+nVCdfOaY\nFEjsDGX4Mx5m+n/71Fzz5EvJTK7TrFDp6Fcqb7bKcUdV2V9RxkDCkikkM7Rb\n2dfB0FaJG1P2/H1Y/zG6wZHvNiOnziQHAoZcDD1/uS+DgA0oCEO+nRyefwBy\nG89c+vwkpF5K7mN9LrdvGRSh45EXVmwPC5sQV45scn1EjcO8krfQTiLJLF37\nqmTy7TtMewUqqZs/WVDFC4cUH0uaBSu6fjLu7/bNTXwgnL3UL2ofG7hyM5bt\nBZuK8vrJQCH5bJU888jAnsgXwX4HvIl4pp082q/kPW74P2RNGGwOZVBUlHGp\nOJ8hyE3ML56pz4xcJaX/T2c9YxiB61a1ia7lTVm6+Nd1TVoMZUjN0MB09Pwb\nwvMJdZk+6iamXcL+xPD395iq2x5bbkgKTHtO9BkzqIbaHg2uLyaYRKaPoqkV\ni5LLOrBQ8I9M2efBnaWdVYsgrUPctT7We++RzZp9OT+rDzK/VDDqviObXm4o\neNzQGpcNmLPHxn/rwn4gzvRUVDGQLs2Y6FlIuDcu2p4eZ1KkgzDXOGpHNg1s\ns1ZRygA7V71zTcb+CZz0hGXq7ostS0XV8NeT88W3kwWCub5OzhBI7RL4Et86\n3Shz\r\n=4zOi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4qxegzrlex/Us3klm7xUOMxzifchu0k8jzo6sazFnmAIhANltuoxr1yHXUEvLjKSFzY+dMLrQQYlB9tg9JvYrQX7z"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181212222153_1544653508522_0.6279079554825013"},"_hasShrinkwrap":false},"1.2.0-nortissej.pli.20181213055544":{"name":"@atomist/sdm","version":"1.2.0-nortissej.pli.20181213055544","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d5f1d456eff8cf2d9682608e3218a30a99288557","_id":"@atomist/sdm@1.2.0-nortissej.pli.20181213055544","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-juU0vLXRQahaxLeP6kauWk0lSHDHEeic0XMVprrLfb3ZU8uY5ZXS6FRaJk9Ppc9mU8TtmjPxfsY/8DpshPfOzw==","shasum":"8e3404a68018aae36a984048c87c6bd19bb74b95","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-nortissej.pli.20181213055544.tgz","fileCount":712,"unpackedSize":5369869,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEfUJCRA9TVsSAnZWagAAIdcQAKTnwE3gyIUZ1l8rY3rU\nFqkG9NATtBr3L9HD9qaWBAFKk59FKZCb5a/c/zLHY/yu+wHcwUm2ylcGIrlS\nV7AhK9YYbAFWGIBr8FqQK4QGRE+IehFGzhgWvIr+CCIR9nZ/kGBtXMH7YzlF\nZ4Y/QbZbhHBVHNlEylwO0pjAEd/00RVUcMbkg4nrWH2JreqH0PyD0eZpqCUe\nx5QYbkHKglD1q1CIDjokoj0yJH9SoB7BuqwT0Ho1CkLLiSkRn2gc3k4b9CrG\ntgv5dpU6so81HqPnCx3/GqIydGGBbHMGewaHdqfGp+vJxwKHY29tC6p3XZED\n9uKVwIdlxLNrh0syjNbDgqq2sPCvZ2R6/bARMRoLECaKnKxQRRgLIoBEwGF2\no4SUtqrVB2GH1k0/tdfQB/r/7NgFcDeCQh+n6iadk5n68iIP4wLoskSO0vbA\nQ4VwYvqLo1GbbbyXW4EAtJIKnFkMjPyO8S40om4iEn0y96j5ier6pBN3FGAL\no/84nf502vakXT07VNZHbd1oAmION+fKSvuAiPEWqHDGq83MpihPLaZGkJiz\nKjzdxBeiHVbKPZQ57rE3bVCRT3AiFBhv5F8rZbxFzptfy+6DH14tSK+HoJyX\ndxWrOYygmPR2UJs93Fh0q9XdZI0U6WjMpSWkIcPxcmLEws4TyQva2tyQE88m\nprkn\r\n=FkKZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICoCXH3EIw76Q0Gpl6u9TDajHTYoUxtNIZnUliqvLv+gAiBabkjFClyTYjKpeO+je6xAN/VYDTKL7a428BBYydBN4w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-nortissej.pli.20181213055544_1544680712105_0.6862099169482945"},"_hasShrinkwrap":false},"1.2.0-master.20181213080727":{"name":"@atomist/sdm","version":"1.2.0-master.20181213080727","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"00edfdb2aa7bf204ccb0e9978936fd6b3e0ac18f","_id":"@atomist/sdm@1.2.0-master.20181213080727","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rCgeOAAQQGOvFSSRRDLhkbMJfx1/U+K7Ze/vYgm2GlSMWEFDlSHk8yRva+27xwQwHEWphcgb8ENS0L5joh4jkg==","shasum":"78aff5909d287a1dfa257249c1862a6641538799","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181213080727.tgz","fileCount":712,"unpackedSize":5369862,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEhPjCRA9TVsSAnZWagAAiRwQAJ7d5qK5rq7fTJgcKy3t\nVkaZPrHbimp8r9da/aArfUMgcDDsKvzO4Y+4bV9iHJXAYCxNhsNQg57kBvNP\nVB0KF1ugE2zXDITeEyj65grn+Z7qwv8qrO0uaTcx0+G4pbYLkGeEFAYXAucv\nuj/tzUYUP7EkZh39xiVmNBcyDjluALZ9/JLZrvsbsg1+akQ4WTL127dyvv/P\nTru2Yvx04KyZszPS/wycWsbbpZGlOYGM2xv598GmREp9E3HqSbYFmsIIggZD\nFrSdiPubULh+tL60V8Mnp2xrYFAO23x5mN9we+9HZUQNEVnza7QYabOesf70\nosCLCXJ2seRe+Xy/OGhr6P1LM/prxmcF4dTWZSEakDg89/WbDBtAwkcysRkO\n/N3/0NpihsCBrRL+y9u6Hrja2zzj78iip8YZTkYbMSI1ViVNy0x1SIK7wi7A\nhkg77GpOYULxGt3JvE+Zm3Cc6qWGEeTNkDzOIJDyIQlDnlWbEOn9TLKk6Ugs\nCQ4bLnBetxMAa1DWnrYIqCf12Sn/LZqjCI1zOVqeQI8S37fz+oIZ84UHw+cP\nONOu4zGJxw8WGSJqQ2tERY8jcVHIMGoTK6a+L64iUzwypS5ZrD1qtormplR3\n6wZDnplFwMgj85N4qjliDzX/P57itdWFXqjdCcfun98u3LyYEDbYqzuEFzzo\n+D9A\r\n=sfij\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBs6SIpByjavrEog3XpAh1Z+BVn8CzbiKk3fIKYK6NH+AiAmljdwK/s5ff4x0Z4po4xrTnQmw4nMwRRf7TtMsmCT6A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181213080727_1544688610627_0.5189338569300221"},"_hasShrinkwrap":false},"1.2.0-child-process-on-project.20181213085914":{"name":"@atomist/sdm","version":"1.2.0-child-process-on-project.20181213085914","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2284d131b2b13b85129482ecda64d4b60845d87","_id":"@atomist/sdm@1.2.0-child-process-on-project.20181213085914","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wPchbPHt1v+QnJIl2l+XM0CcJ5u39nN7Y1DGECOdIrHmcweODq1bFu4vwncKI476eQ1suS3CMmK/IsfAX6k7ZA==","shasum":"96beb5be2cfb9d506b4d76aa68e644fa8941e0a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-child-process-on-project.20181213085914.tgz","fileCount":712,"unpackedSize":5373438,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEiAgCRA9TVsSAnZWagAABh0P/RilGR7SAwvUR7qUHZvq\nRBtbhkWmawxx+PXTbARc5RP3bqH38eOWxboHiGxXqXOtfbqyhTq4UwRBA+TL\nEerEjhM1PMW/Q+8fmOBWvxnVCM5KJfDcIlqmM7kWj7zNr2KNd3E+eW33X46p\nz7sXZaxkE47Pag8ODfTFO8GWLoQzrNFz5a3XWyptGnpG448aJ8NT1Z6IFZeG\nOcaeupV0n2FJeZ9UyFkHzk7WJTD14dMEF6BAPxo6DJwOF6bRAln16UAhSxKo\nlBzL4ycvuCmqDPYv3FKQkkTKNhPuJS8rnRkYjxt0dqnwTizsvBco13D7XYNJ\nbGSYqEJGlf/xKBxzHsERKKJexLbKcwZ4/9d5QZilHYJIRJMzAHTvdfXuAf7k\n3qu0old4vETQ3p0FuT0BkYw2RxVLNZuv8MluLVHwgKE/z4qZGJHmz8wvjiUX\nneufEo5JJyhRZw8EnTxPOSns10c1IEQccsV2fSzBy7S1EG2I4H++QKLPz/Wn\n5XA9VIqrjcNFI3lgbdunuXeKP4NrHRwTgzxg6EFZQ8V29jJd+/2lefVVDSnY\n0cWJZLBMR5WxKA5pPv0WFAkefewMPV1xeI4hLL8TxI1i+LLw0GHN980RfloS\ndxxzsR5FP6XU0TzcZKd5BaXzD9bF3r61x//wHjwRZWADY2CGeY6QZv/Oejeb\nW35k\r\n=ux0Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmjvbJZtUEOKBPzJ1kES+1xnVPRTVipXG1NJuaYrSXTQIgGPm7WjkkqJUqAh9aYvNoVPLJH4tsiPENIoNdv/HOkoc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-child-process-on-project.20181213085914_1544691744067_0.0729021005653463"},"_hasShrinkwrap":false},"1.2.0-child-process-on-project.20181213145618":{"name":"@atomist/sdm","version":"1.2.0-child-process-on-project.20181213145618","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2284d131b2b13b85129482ecda64d4b60845d87","_id":"@atomist/sdm@1.2.0-child-process-on-project.20181213145618","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mMAkrqA00T+/3qpnfACAWtmnzgXD2ha47eiUdMkNdrxIt6w35QQrkRdKpcdiKY79ql6FeBUGVjqd6W0RjeWHtg==","shasum":"8726c077b0b16dd7c227f51429b212dc6ad5ea26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-child-process-on-project.20181213145618.tgz","fileCount":712,"unpackedSize":5373438,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEnO3CRA9TVsSAnZWagAAt8cQAKFpKg+wdpO/GrMsZ6pw\nu4IiOx5SFVTdDkFzPRuMI0H66P8Ld6gCIxgRIEBiF6lv/lPwhKwN89NfIHjA\n17cKrvFfbf3UZBPdqI4bA2gUQpf1pJgWRSclB0OeVQ79AnmUDdMSo6k9Eg/7\nfOfH6+m8uCz48N4WQcoscYpbLgl++tV1PxirekkJk+Q/MsYo3UhwZ7Pjivqq\nQpM59+FT/GmdTXc6rj0ZCBk2G2gPa6MVFlCR0iwMrpP86HYhVPB2sQ5sKpsN\nE2RU7ux2fvZJ1enH8sneocLtDVN+HS4pkzIJSI+/13JJ4lS5iop8rgTJXzX5\nZxBFkHvBfVoy5GFBNaXYw9IKHqj6AbPFDPS+hRVf7Rze9LravDlVsxFDN753\nEOVsJOUSKlKAS+3kh5q69l90o1RMx9UFcGAOmhaac9F7nqJvUw8zRLwN0Uy0\nyOqEIkSgswVohvLVLwovUifHdqE/1VC7lyV+Hrx7wAEVpOL0tEupKuq+iPUS\nk8w7Z8AdgtQnVID8leiDf4cLGsKrQPHx3/W52LPC7Qe22kfEawVw4/VgMbrK\nblQCVBjJW/qn9+Ji8QB+Vj+i0VL069zXLHvsAUfRMOaS9s9Y+81piHog0EPf\nyepTuYiYRfWF1kaNeqYH2r6lYnezBCfP1UX8dYLKIMOBqXpQ/3iXPb1rTIni\nf6hK\r\n=M59E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICcvGN6krf2xxxSyG/2XJaLFnzpSWhhTXw4PitmOE5RtAiBMlDb3hUxHW4pKsnLUxTshDE+gL4Qcywjxdzc3bg6WLg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-child-process-on-project.20181213145618_1544713142539_0.03187110832024387"},"_hasShrinkwrap":false},"1.2.0-master.20181213154601":{"name":"@atomist/sdm","version":"1.2.0-master.20181213154601","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f0f5582cfc9d9b74e196b50e27ac2ee1aa86a858","_id":"@atomist/sdm@1.2.0-master.20181213154601","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fXggvDshPJ5k6Ek2JeDEtj0cG2dfup2kGHDHZoXu26ZbUOUXHDyN/fAddvjj2D24zDTU+TxWSXmkXnrVnzBVnw==","shasum":"672b7042a00789a8c03e6d8062febb69c91ad235","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181213154601.tgz","fileCount":712,"unpackedSize":5370212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEn97CRA9TVsSAnZWagAAe2MQAIAXpnpL2AdBsPxGC2Qt\n57EHeLvwBlkwn+4b4gBwY7kjSVv5F+ydCtrSVOBC4/uQDjeqGBygJIHh8Kih\nGB6e7ul23vCKa1NL0Smljb0Qj+NObhdTA6EnbPuobPhrZJ/Uo9oppc54HM/7\n+WiUFQ2rnNt8WXB6ffLYZaDdItBt9o7PngJ1MyjKBskTYdPwBZ5WEbonOOUe\nuFl1jVj6/NDrZaaLQzXtGxmaLDGsPderPbiPgTeygu8WyxEnGbDBNfjXVVZH\nXvzZIPwfjGTNITFkLW1Hrr1jnR7e9l9NvcbIAYVYHWIkzFHLR1Cumks6dBd/\ny2dWnptz6DI2ARjlJazm2czVI9eUSJwq8tPKP7DAVhHL9cxfyhmuJLKjl/FP\nN8TkRp6/PL0udG92byrsSUfSDPLifFHSHgjEag1sIahHlIdQR6NZyLgICbqp\ntWl7yQwfOsLE7go238i6BcK8cZ+9/h+PXxjHpk2i9NgccSwKtyiosMPoQsyd\nFyfJHOhN8pfzXIzD8DNkcUdCosAnTRNdT3qHgCIKYLfuiGZ3DPe4ddsbEjQS\n/1zsrrH7BaMd5X31V+zqcLm8cGF7KmprxBcCxVUXgOfjPdMGpC7drsxjREMh\nrQNkiGR0LL6fmZT30Qe3Dk1hmMYpDZQhIB/scp7j0IQhdtaLARcck5UMvsNp\n7Zz4\r\n=zNVy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnRNgj1h9gcxP0KG5yZ6V/Gw0Hdr84Hpus2+nbeapqkwIhAM0D86pkNmtFE3Bedz4JkUYBCRkwnmwApzPBzlGJBvKo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181213154601_1544716155250_0.7725040385470658"},"_hasShrinkwrap":false},"1.2.0-child-process-on-project-delog.20181213154618":{"name":"@atomist/sdm","version":"1.2.0-child-process-on-project-delog.20181213154618","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6fbdc2e974c33e4822ee4d8feab1dda02409359a","_id":"@atomist/sdm@1.2.0-child-process-on-project-delog.20181213154618","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zs1z1XFU+CJABSycSvMjKx9NSLpi6u+l/qwlFSDuMiPofN+k1lzhLs2srpIXDP/hX9iSDmsjaRUv0UosW9BJjQ==","shasum":"0b8307fd1a9eb935f6f63e5190c9c9d40b58d7e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-child-process-on-project-delog.20181213154618.tgz","fileCount":712,"unpackedSize":5376665,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEn+QCRA9TVsSAnZWagAAEg0QAJQ5BGmce3d7eBP8c15/\n20WyrMzKt+A+MDI+/NQyMzgnmHZs6y3FymCDnSeHjf/ggmpgv+H4kddVmWNj\nXk2XwERHZKJO/lGPKhvmF0PUy9Hsg4oUddKktqZoh2uIkZi1i4UP4f+4WZuD\nP10PgfYLmk39782p3/sdgekHBb4DHeDl004K8qtXtG9Q6xxu/DEdFzOZ8Azg\n6ilZtrVvq2J4qFoBdr2cr0gwFiFk8g/9E3IvFfLNHdeA5MQaxU4czKCosvBb\nUI6cf3MZ8SJFxT6ndodCw55PTCdEeuDD2a9OBzETyWCabv4TGcp0gP12BbPB\nldu5LZkgEdCNtwsKEL/BvF9jaO0AdF3MppDMDISEDoT1M99Gjw/EFwHzTZRk\nNxsJq3EmgvcWrw1rWBxlwOkOh1t0YnYVHD2fp/dUej8oVt20GSwuc0CNDLxt\nltCKtl/D+KpMFVhNg9UMO9vgniS0uyhnnuRrQ5ATnKJ5xBBZswowk1IMaV6p\n5LvYg9l8z2NqeGBXKrSZGX+8I1XRGLHU/oZtmP85TXnGGNYVbgJtHiNX9/xu\nZkXnBM/nxfdERt5OdiRZE1aOi7/8AE6WzOotwyMlurxNJaQWrJueWEi1wd02\nWeBdEQuusgWvLg0IWm44airmyS+ATIZH/nC4chIp9nvMbeOub5GuJwhlaWnR\nAKZa\r\n=ix2V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAjIgDkt2mbAZBcGvka3zpadvKk6KcASJnrxCioX1AnpAiB7xEGHMSBcUX9FC1xaAvdWQmt9IlRiw2eTY+wLVj8yXg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-child-process-on-project-delog.20181213154618_1544716175435_0.4553722826507216"},"_hasShrinkwrap":false},"1.2.0-child-process-on-project-delog.20181213170109":{"name":"@atomist/sdm","version":"1.2.0-child-process-on-project-delog.20181213170109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a66610f6f6e23ec4dc753d6188963b7ecfe36d9d","_id":"@atomist/sdm@1.2.0-child-process-on-project-delog.20181213170109","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LzpewyaCkYQ/3r9smqSlOovPHuZh1wOY05Xa/IMQhDb70re+JfLfSJpmfRSj/ZfyhwJo9gdC1uINGYtrNztzDw==","shasum":"082483fd2d7b93d27d6c3e551943afeec8997cb6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-child-process-on-project-delog.20181213170109.tgz","fileCount":712,"unpackedSize":5376536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEpEACRA9TVsSAnZWagAAYnUP/iooSL/fKlIRGv1kUD/0\n6+fwhRnhjin7SDsAkkNtP59br48qdy0bUl0yZqGkoNkRThxoqOaM4w8o6xy7\nnDWFYIbA534olu0VhR2leil2l2IML0QZ8jMjDfSiyu/QSKU9u7y5DHUH4G2c\nL50Wf/8X/VUTylxRQpJFtASgS/0J1H49M3vc7IO0c10rmHemnvpp+wsRQpYF\nLto3axaxGyZT+gwkGkajJfe0gMUxwJaMgET/df+cPscQzX/i7EpAS4QVZEm1\n+RcaWFp+oLwpwHLskJPSjSTLBSAGR9nvbLlLANLjjwVkXQDxiM4eiog/bTLp\nWFDoGuDNP3Jis1vSKIvu2IGbo1FhBw0mOcnaGjdgNCilgWaXgimHJhv5TeT8\nLCsIoYYowWEPD5IZpYD30GTyE9IqynkxXLvDzAdVFvYhCZogqYwRnaplW5p0\nNtQYtbCRuJjrxRaPZYoOOjT6viYQPWOmlNfoQAApgIx1j48bLDWPiJ6T3WmY\nIx1X1Ponj2WwC0zjxs+VmO6pd7VrFJCJq8HaSVoO0D5odATJ/wcYd1X6HJ5f\nE3w5XvRyiKu3EZBtuIYQCPQs0xaZhI/xKd7Ld8VdMJVpMNbwiBFPNnVnr9ZZ\nVl+kOXmUPXLh9DrhoGv+NuuZyamfdsw+kmyQvcup8uNSy85O8jYJIVhW6s/E\nz4DC\r\n=90WU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGVJXTO/Ubdunr78RPYK7xVOImTkIYp2Ppe9aZ8hFUenAiEA4bPBwLyNQ0rS9VaQsCqfOrSa9cKue3bv+Qikk9YZabg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-child-process-on-project-delog.20181213170109_1544720639639_0.8052317680817618"},"_hasShrinkwrap":false},"1.2.0-child-process-on-project.20181213170552":{"name":"@atomist/sdm","version":"1.2.0-child-process-on-project.20181213170552","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fdff9b3862b292e494bbd61c595dddad2125fcf6","_id":"@atomist/sdm@1.2.0-child-process-on-project.20181213170552","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yycj6GmvAg2IJBwj2xj+KHCd/WJrRmstF40CiFbKg8t0gtQKbO5jP8pGGfksJMl70gDBTL8mY4HJA4WR56+6pg==","shasum":"ad2521af8ead3d7dde211e5bd4056aef3d981b04","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-child-process-on-project.20181213170552.tgz","fileCount":712,"unpackedSize":5376530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEpIZCRA9TVsSAnZWagAAdq8P/Ru0rMMyW8KedICzq1A2\n9BZS/sjlDR8Ck4s8NKyjw71CeiUbygoSIwzjvaG5eJHdR+qnWl03omH+95w9\n5rzpfWau02hsBB03HTOdd47Xm+6iNCqRJd5/gMnTClgp5oN7QPRDPY4Gs3iG\ndlIyRehqxR6CqRTDepshSEe+Jw50GS8H8jovvHgkRXwAmotGDlpCdbWhD6zf\ntE/NQH0NL1lML3ESvm4EEADVeBB2Y/HAu5CyzWQ0nSe7W8MbVhLII8IaCDIl\nicu58FYhqdHZ6y+drfN5Esx0fcd1J5NTmLTBcf43tCik/qD+128MsdGKmfaE\nDzAexBeWV5yzqjC8OD6i88PmT117vsKTu7Ua+M9IwNNx7Poi8NtuqdkRZodW\nthCqmaQUrMh0q9m53NZ04dLDdIA71Xn54DAOFcsttzrW3RgQHo44i4cRLi34\nKLs6ViT0KZ7A8pJ5Zp+0gArU9mFKGRQAICqN7v9Gj+PRvHxS8FBKROpmV2qS\nzT3KivYjV0DqHidJRPLYdyU2SVLU7oEqCXTB35K0QnvLOd3Br1eve+HooYqG\nRB2LTEi3CrPqvpJoDD0xOrRsRmysuzt0qV1Jk8nhQnNLAjh1RJ4rDYSNpLGi\nqfnnICSr71RHe39IZiLhC49mcikyT4AnTypSUm6ARx2hiL91QhCn8tg2ouFn\nkgP5\r\n=maEK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCim3K9a8EpA86eb6fQCVp3DQOXSZnkcOfA1n+gEivJqgIgHLcfU36XS5f/ekK2IGy1rPu4Gz+5SJkIeu7z4PDnXWA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-child-process-on-project.20181213170552_1544720920644_0.26672553408758315"},"_hasShrinkwrap":false},"1.2.0-master.20181213171110":{"name":"@atomist/sdm","version":"1.2.0-master.20181213171110","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ca56046b7bff6270fa935c16dce91efb710abe94","_id":"@atomist/sdm@1.2.0-master.20181213171110","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Jwtnun4byZ9lVaBOKXhyK5S7HnJ5ittDoQP8Q31y/1tcr+a2ll6dbNYY1PS3rVa1FkN1iMurPZEtwrEASDEJvw==","shasum":"cc514a3419f6782bbf3adb5889c99c979b0f88c6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181213171110.tgz","fileCount":712,"unpackedSize":5377066,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEpNiCRA9TVsSAnZWagAAoWQQAIFOmBPMDNNkTNHGKnnA\nNLWS3/+nElgrb3njsZjiyp5mInoV83QzzdZxhGnrCXpbkG6baxfqmHSPB07/\n4A/hSw33yd/Sx7+9wSo/3K7I8/DXEXFltGlhYXQ/WEFrIEhpe9Nf6/5u8S9D\nVP34Uj+zeVbcjTFyL4kqmqlOPzApwqQZFzhaYj0si/gIbziDCzpHYyTMz998\nZqFRHYzS5zhr1aAw+ki+isQD+g9zfw0XiuOvUn0E4GV6p3SkkXZohQ1S0gju\n6vRMB44P0Y/3ZeS4pnaUdUEiYw3uDX2TUpSsKtcZ/d5IgimxOYPHxEH86gob\ngwHQ/VPxv8Ui40xfrieMiutAIhJRkarbriQuYUiZreSJ7BIYPPw4flKVX83c\n7EDijpfkxm25aQn7wO7aB89n8zOew1IkTQH8DVNIfrbto7+UWOE1UrFyOr0W\n1cba5VLWAJmu+UCMlR8AOKOIEjZsNo9zu7Og+I50y1t9yFU5YGQck3iZws0T\nnKfQMDWuWWu+RxujwQ1XGZChDppTIHzqzsGW/YNHdxQwpmFfjIaT88C25We/\nKMO0Yfu/WNQWksQ2L9WFhcn/CVs8BorAPz27IuPOcSTQoO2rF2drvKG+VlzL\nN/vFYqNuNs3XBh250h5rtuhvOV+jeNPgSB38WFBlzSHjgGEhK+jN4KLvYDUg\nVFBO\r\n=ql4g\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBII5EoPsOeA1e6ha1+SV+nYPzWvZTevZQYJ6ve2X7olAiBruJGdOFjQPp2456AjshHS2++ozelIyiDU462uUGXe/g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181213171110_1544721249725_0.5094067288907576"},"_hasShrinkwrap":false},"1.2.0-lazy-project-fix.20181213231834":{"name":"@atomist/sdm","version":"1.2.0-lazy-project-fix.20181213231834","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"47d10d8f6f400fc70adc394d5246bb42e53e8a88","_id":"@atomist/sdm@1.2.0-lazy-project-fix.20181213231834","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5SZdm4EK9slcn9MMv9ZdfelJnBcPqpxY13uXHI7TYrkBDZsMB3P8o2p67OAYLev8xO1px7nCShalvMNAsh0LYA==","shasum":"9f45ea3f0637a033d8cb057c8e52b0add8874d4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-lazy-project-fix.20181213231834.tgz","fileCount":712,"unpackedSize":5378960,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcEul+CRA9TVsSAnZWagAAGywP/iJp5HToKg5b1ayy+bfe\n1S1P3wId5no13mIJCGz5/z+U9RoPnZOq+IPcGDXVen5yBDICFJ4eKG0FP6C0\n7uQvIvaceae6RMIL5GmmVN/ENKRBX+2q9Ae/Ew/elvk7ogbYz0peqc/Ehum9\nGj6iLGaM9msd4rUONQ/k9fnxbHiZcPz7QvKfiB1GFq4nRLdBuDOLf8eodi9a\nuvHlv+XTkhVD3+nMvjOT+lev/qy+nMQeHHstUD9Y1Bk+KaezjR/bMhBsvAt0\nPSBafb4HNZuacY5lNaH5HEJePdpPLJPVpJHPj7Dj5RwXHfamWfwgJGH6iTju\naXORbDtaseXR8TLfYsI+vrNyYSMn/tN0AximEYbVZDiP+/qAJTqdX9PWTYXM\n/OPAtfhwMiTvXVxa3Fso53cw+KBg+LFWgTwHwMuzJX3pyJtdvkrURkQcEFaA\n2ak3IEQxTzyMOW43d+VIG2pLVupl96rrSGVb5Rn8YE33eHAPq3bszHbtdh3n\nFsbMbAQG9Gx6D6AOIB7wYe4V3zvg3/HYZg+BjBPOv33o1jJTDwPo5hsw4SXm\njb6GcLkr/CRS/C/K3B3/izH0FlhQfiO+cRG55014igymNiDIYaQJ4p7bXyRw\n6tUSh8kjAq0gaKuxO6eF7klbZi0fgidBMmFD26hF58zWA5KPJpaizCa2g5HV\nj+Ax\r\n=AJtg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCqCmo78BkbayjgWiKl/5tj8GJDzparFp4uRDmMc1hiwQIhAKzylZbXaK0qyQIZPVJJgW5GAebP6LfY8+A5v6tjSyCF"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-lazy-project-fix.20181213231834_1544743294015_0.18272339382327774"},"_hasShrinkwrap":false},"1.2.0-master.20181214081302":{"name":"@atomist/sdm","version":"1.2.0-master.20181214081302","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9c56b7ff558fce5cc188c802a4e2b6b9947db865","_id":"@atomist/sdm@1.2.0-master.20181214081302","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-llbz2+iEJsw2PVk/cUU+yV6NBcjyjuzbOlUKiw9efPXUsBNiPz03gCme9s2IvqDABZ9c4UtIA6Adt4L5sh3edQ==","shasum":"d3a99d4a4d3e52fd5b5be28b42d9dc848626d0c6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181214081302.tgz","fileCount":712,"unpackedSize":5378292,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE2lbCRA9TVsSAnZWagAAu+gP/AxnO7ke0KjL4oC/Denf\nT3A1t7BPsmE+C4xxeR37oy0/mblsYiibdWqynDYq0BZIzWiybASaZFaE/gAe\nA0AdHnBa+bVn1z39+6tzCuwjr6JfC4WIlJCGyeL3YMGiJS/jroDgnLHs9FtV\nwn1XhQHhhQ82mRRHFp0jp+D4l0GyLiTY1eEpaICOU30OmX0T1TKk6+wxUPTe\n7XiNFGOwyHAbWhMkGIBX2ENUOnM1npBTNMRpcHWhI0fAeULGHYs3BG/qEook\nE4ePB/vwYkT65Q1GiMWWKsh3YlXHXUu1EO1MyFRvJYlvNfgCyPOzanE+Kw6E\nt/ygpjLxehlG77vFiejBNhIp6mvGFdi29CmZc+fzhro0TqsCar8IWZEBmtH8\nUiByXc67BOiIZWJ/dveYgit4KLeSUrDApphoBF4mPkd0etChxb40cP9vwbxc\n67ViV6DfGd82Zv9UXMSyEgHwBNn6CuBdNWA23jmMp1G00X/DthOzspRu5PZb\nZMb4ai+c3sL8ZmKL1Sz3KPPYaxV9XxVClk6g1bSSUqBvsqXoHZmcZu8Mn7HT\ndbGFOxlxGqW8rBp/kEj374QYZknY2zrnbbzBx7zSFsY4w+RmQwwTCNsOVt1a\nremwscqMR7ubpuCF5WWDmoaxxUELJNnd1bVWJfY7ALQu3v7BrSvZSH4AMpel\noWX4\r\n=hKKE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIERNosTibxe6RcQTHj2RxUtdDFxoctZx6A2wDlEcuY4GAiEA7Ner2fv5cSG/J3yysMQ2RSLV7Tn3H63EzCJoB198v0Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181214081302_1544776027112_0.12690924584605612"},"_hasShrinkwrap":false},"1.2.0-master.20181214131108":{"name":"@atomist/sdm","version":"1.2.0-master.20181214131108","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f9e24d129c8b2fe8fabae8ae51a0cec76235d321","_id":"@atomist/sdm@1.2.0-master.20181214131108","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-iJOA9x0m7dQIxU0N7foVYP0GtcVoUXwzmuZNz7w1CEFlc9e2lX0kikH5sRK+95ZKgf6ldnMSlshLi7QP7PnkeA==","shasum":"76da4a7799ba933658f17d7956ae25ce95871d93","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181214131108.tgz","fileCount":712,"unpackedSize":5379915,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE6yVCRA9TVsSAnZWagAA8fIP/0hsq+w0YmGnQXWkDZ0s\nwJnRpRivL9ZiKUN7qvU9WOmVsld2zFYjx9Cg8pYW/1q95SMPDDWQaIeb5b3M\n47LZnHZ5sQl60iqfQkgd6gHYF8z+AOWrZ4sd8UsbnWD1PCJr8BDEZdqdrXlU\n4PT6CWrOUv67JBtR18ppYy85H/JbivVdXjE6GsBJSvK+oGj95SqZpoRLwA36\n/tql4HTtA6vIDsv/fvPfoLqxNBZKJcT11xBnFThUIYxiDBT0TjvmN2TuAbBB\nX2h2kUmTZa7QODiGYCKIVFriF5i3PMDucoOp52lWUW6rkq4Nc+WI87atgDKy\nh52lty9J2KTpFK+wri+KRaWi6nKuZoX03BWIBa+hCFE6l8pEzrrKOoDXGmqk\nOeiwlA7MJr9LjBk7HYJ3OPpOzQrcDm5+4ITYX/RqxITMLHlURlWBLetxj0tY\nA0curf/tV73uyLm6oZNlVAlxrl5ieD6bzyPB/fOvtKANr/UoxomFXn8mL8a7\nNxKB69feDjhiTBos3EVTsy5iJ1yL3jPajnYhcgmhBVTVc+mwJGcV2TK6A5Ad\nslqLwO0oA2omHlSZcD8/TR6en7GHy95FKMdrxiDOjcvi/WfUzF5DTWemI/6N\nq9iEEQGxaj9oVyGilplCMD+kLjRtxY91aRnf5AQ5uFeW90e+5fbNDf1zRM+t\niTcT\r\n=7dix\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAbdEo92BSjKjUw6EZsARrjEhBRh0RRRes2+q0FiupqqAiBMXSYoGtNOEYX71DRBbrGnuPzbEYwzcgUHXcOlUGuoXA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181214131108_1544793236301_0.6391034316650746"},"_hasShrinkwrap":false},"1.2.0-master.20181214135136":{"name":"@atomist/sdm","version":"1.2.0-master.20181214135136","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"73870ec346cf11521edc6db2b7bc150808c29161","_id":"@atomist/sdm@1.2.0-master.20181214135136","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ph4ZugEUTNbQSX7U/F511pnoKEoSK0PXUAeOfwaislGVjxE9OHyEKeybh3hekE9RREz4wzo0D8GhbvFEEwnWWA==","shasum":"3ed669911fb74346390c61406c91d6b0ea8fac6b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181214135136.tgz","fileCount":712,"unpackedSize":5380181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE7YSCRA9TVsSAnZWagAAuDUP/iutIA9vYOrHlCE1KOzw\nfxn3yEVJCGoga4l7YopyNuVuEWiiU6x8ZZEfv51ERSUHz0MzLJoYvpI8HR0w\nW0UHi6xpXpgeur1rDDpLq5kUtqR27z3xHSdcM2iw5cgSkwGlsIudWZqN+WKZ\n3HEn2tQJ5Yq05Ee2UVp4VTKeP43UQEv0BoI3xhKCc8j1yagj60zcWemGp1J7\ngVNniNVmEYCSjOzu7E7D+ug0/KMqpgIMTE/A6Q6l01Pnmyg9JXkf7QBZQyTO\nMlEyRFKi8S+h6ffychA9IglQkHY/rNMATXigB79L+UCiSgWNRWDSbhQMAyh1\nOe20wMvIQe0WNqZjeYzB8j5Ixu/dmDHiDwe/+WccabphuQyyCrenh0/s4PYZ\n/zRbCGXmdRp0TWf7mBoXWwg6xlI5R8nRx2MSkrvv7y7jW2OZFHZVDRMyLacD\nk53AyouhMq4e1VVbOkg1V2+gE8PqiUS/uOXiNkcOdmGZRvl9IDLPZTjek2zm\nxZOz0Gs1pWQH5z4UVGWykdcUGo36u7KEm2lW3+dcvSZQ9iIhS+oTr+ee4utT\nEdZ85Kn21m94uyD8pF4kfdyYu7Lx4Sy/j8YtrCYwu0PKoZcL/N/BMBl4bToo\nq47mt9HSq9yg+EjwoZPnmTTjmDgVH+b8Tf09KBtEai3baVb5d1VVWTq0jTRH\npfkq\r\n=9McB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4Yrkp9Lj+4vvNnPqQMN4MhpV3BXlgUIl0lH5DBS2NlwIhAId0XAK1mTZtec28LGUI9WyRppFB2jwHJjS3qfZm+sKX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181214135136_1544795665455_0.13971534587441625"},"_hasShrinkwrap":false},"1.2.0-lazy-project-fix.20181214165700":{"name":"@atomist/sdm","version":"1.2.0-lazy-project-fix.20181214165700","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c9d52c86bcc6e0d37094c8b8af13ae605c400ebe","_id":"@atomist/sdm@1.2.0-lazy-project-fix.20181214165700","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bQIbBfeV0cx1AVbv9LlbMRN1FKXw0oA3GFixlOKFD1uPjS6MMIl74avUnDtaSPXjgaLUfuOxYXPzna4BYH80Cg==","shasum":"79c15b94e748252be8ca8d1f2842a3506116c84c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-lazy-project-fix.20181214165700.tgz","fileCount":715,"unpackedSize":5386493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE+HECRA9TVsSAnZWagAAtUIP+QDDfaFWcE6t2yKVbUyH\noqEACeQu4S/GhxK46T9R0sjjEfZ6k1EeRAPZ9RLNQVXf90btz6lol6HyFbYe\nEHfdc4GFfkOOswJQMMKzPYIFkSphrTpju/Ixon0Nghp/S3Bu79zBF1jTXFzW\nsaMfCUAT3UAZ+CJaApXw40Dhh+U5Yvui73TexX4ccrI6gK6MBKlJyBXxwjl3\n0EWGqTSUaJVkiF6zv8TaO/8xv6/CFIzPn5Eq19fspkUxftIMvCWP+yie9NVD\nuIN+QSvv/Rrv92wHrvgbmQ1AiMNAhNPXXRA/TovJaP49888vjSJ7411yIFr6\niV5ROjNQieaSYNuHlrHY6TtY1gh+65M9EN1Q7anPIIlWRvxam7Qxsko/IGV2\nm60/gdGIHRGy9Br5ScMc3/GVN9K99D+FfDK30CQVA0v3OOseuB26s/dZC6o3\n128MkJP920hW+LDWqCPbz9OCxJ6fH2uGrQrGQdwbJ5I3Lws3wDLwYJpQgig/\nltbAfbxBIkK+BD1hV+dmWJ0cycwOdajdCHxP3VtzDEaeeJTDW731xLk2SFFx\nSZgc6M+You1AojBW3yPol6krDNCufPAlaLvhbMDg+wQVb8i8ibXaQcVYT8x1\nL/TGAUA88m92z6j18rzwWg0YjChEfvii3tC7gPSaqs2lyVikmvSJtJUzYVJM\n3Xgr\r\n=eKVO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH2dXVBjluyxDCCN+IurfeDq2ah2cTpXVfMiGsPY08yLAiEA+6Jvm23p6olyBP4GoNo1sA9LDoy8puRjXdqCcvPIyHc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-lazy-project-fix.20181214165700_1544806851812_0.18671572826047544"},"_hasShrinkwrap":false},"1.2.0-lazy-project-fix.20181214170715":{"name":"@atomist/sdm","version":"1.2.0-lazy-project-fix.20181214170715","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"95f9e4f83abcf22de64f09c83fd49fa9d04ba351","_id":"@atomist/sdm@1.2.0-lazy-project-fix.20181214170715","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-voQJHB+blK/1Fv7LkLtfnP/vEd8g/4JEgycbAMr+l9p4qpLah4T00HzLc1X5Q/gVEiLe1LU3QDI4I/glfunUqQ==","shasum":"883eaf37d66f83478466779bcc7c5dca44955955","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-lazy-project-fix.20181214170715.tgz","fileCount":715,"unpackedSize":5386493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE+PwCRA9TVsSAnZWagAAavIP/ArdveTVoUGN+BBUFaOK\nESoxrdnZlwnX7RV6o7S3BlnvsneT4+yRzC0JCymJprteuz6Ihe3YOL+2EHha\njnc02T5/6XV6LZ7b/cKzlGXJ+EoPZ7baqP/a/uVIUO+kQpPmNGBU8In+TeYY\noxlnIarfZLB1XV5eWqSly0ytaJ7yOimMjRggCMNyZ+Xp1oEE+97gKpQWM7tJ\nXa+u6AR4t73bTSlbkZZiNs8Rdvgj4ua1hPeDHtJGFH+UH8EWrJXvCBBaVKNe\naTFovWW+5jrEA5nfrgRITyJb5VPjajEzQxlKf6B7BfcDKmeZpYfgDj2NvtDE\nE1/SJ5+bJCavg9C27jsNTwimgLjiRB/4x/si112uSY4jr+F0ZRb8y/DNTVLc\n6fKCUp6Zflkcppem2IzokNUcOy3BCL/5d8KdKVoggscEgtNVw0EzlQGVKBpj\nfI8KEGNSnFQtQ/A3A6L2UDYhGBYwAUHNqUAQkQe8aRfbPWmYbEs/xJxb7XlV\nKpzVfvolzRSWyHCH7w2nZuedCFjiUeEUyKrMsmrFShmGf0UthpsA+7ZzUMHG\noRIHohUYrMY2ye1+6D/QVw9PRUBBDd4c7ilXOftXaF+xd/VxxQ87FtweN/lN\nf2hTVvdXV0nXeIwbVAcoj47kODL/jtZk4MRr0N4EOaEfDqijJXiMCIw69TN3\nLCMG\r\n=Z3o4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGzHUJ6ax1pXIuHQGmjSMx7vXTgzHxMEEKt2alNeAov2AiEA1RQdZH0dDSOif7VzJ8BXVZqwATL1shPt7AnVaBSkMsA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-lazy-project-fix.20181214170715_1544807407443_0.23105100943731105"},"_hasShrinkwrap":false},"1.2.0-master.20181214184627":{"name":"@atomist/sdm","version":"1.2.0-master.20181214184627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e709d5dbbd11d889e76a01750bd6f050a000ec86","_id":"@atomist/sdm@1.2.0-master.20181214184627","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ae2IEybj6x/D1CHWitTnfh4Gnz7CVJvLLhb7F81lUDUmv9hutSi+QFg8H2WQ0YFr2gSgfCsAVTa5v+vImNgfPQ==","shasum":"eb70b2b21e38a2aad10529769481e5b7ac7b35fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181214184627.tgz","fileCount":715,"unpackedSize":5478679,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE/tRCRA9TVsSAnZWagAACfoP/2bq02X/po1UZBU9bH3s\nFzhJKnJ/UnccHmRqkCMO90Df1CzM2qT46m0t3BcqFZJwSddXmbFA0peoqgLJ\nnMweCywRSd4ToWHY20NoYdNaq26vG+pebLx6U0Ql0/jbyeA4ErUpqmguyb7r\nlNeve7eQRAyipiSsZWyvRCLMlM2lljAkahe5CMSxCpesBdD+0Orq3EFxY9Qj\nhKo/dY0RegLk+03Tp/k3ufEQPLtqK7Gv6p+VCl7OUeC0BX0nfxcdbweA6cxc\nv4Lbc1slfHafGKlFttUh3JUCcRNDrbW95MClbt9+Tl5dIR5nMnGCbCFEoyEG\nyMw7nxCbfyghpzsK+I7e7ZDBowTPvA3d+hHWDGCjbDk1kXD3H4mGjSpVtRCp\nX2XwhH6UZER+3219BHJIetpFJtQwaz0OlZ1pyycFlIGWhHpw7ULd92dwJpnL\n5hTIOd1CBDHpDFDGfX/3YI9s7uGvrOdUm1ytj0s+rFhkXBhatudG8c9+zy2X\nOxwUOQgrS6EhrFaZDA3UZ09imncyzzPTucSLZhEtZfvhWOHaSne1dVC7Zcot\nIek6CwE0D7KarZumjV7G4yfVZRhQ/Gl+al2ivnauhXLRs3mxNEN+14J7KDsB\n4L31AdsUtUb1Ft6M6yKDCyH3JPJ5HiSm/j7XQX/u+3zG5gM7jPKFXRwmEhtT\nj4fR\r\n=RByv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBP70l0ngVU3m1ag5hbrgPNL3q3lRHlRMTTIl/TLXwaxAiA6jLXzsiC2r/y0n++FB7STJypinXTnibmk0RvZlXSXxg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181214184627_1544813392478_0.28269729047142955"},"_hasShrinkwrap":false},"1.2.0-master.20181214184807":{"name":"@atomist/sdm","version":"1.2.0-master.20181214184807","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e709d5dbbd11d889e76a01750bd6f050a000ec86","_id":"@atomist/sdm@1.2.0-master.20181214184807","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+xShVmkwm63/lWVgbAWWhICvee2QG5PeMsqb5a5/puRP2xPjNjtXsUxK6nJeLtimuNk0vRTvzlnAfwybEhaTfw==","shasum":"fd09c9c714dac9475e53af9ef956b4e3835abe34","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181214184807.tgz","fileCount":715,"unpackedSize":5478679,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcE/uhCRA9TVsSAnZWagAAyXUP/3j28wH6rpSwybIxPnbN\nfvWmNsi5t8kqS/ZsLBUhvvIEgOdGSEipWzJBEBU2/370ANHOfCnni0qBTFGU\nzy/fF3vV+666LT13TWWKtZEoddaSdrE7oEf5jXXMEFlgtZMYSGc+/XqMcvQv\nz2GTvlIwzAFznKvjYtuiqX/OdQEFI9+XUk3oQy9WOUkXMYazsdWJkDgji+Td\n4puqmYy1sUoznBuJszVD40wJ+a8ouY+9m/xLdXKUyxSWf19+a1xznlYvBGZ1\nZc9+lPxp1q4mimRY9xvuSUmOvFi+Hha5D+Ra8q04eKHtFlL1hGa5iPqkcjEK\nQpa+1vGJ/LUUJdo2n6kxNWKCYcQnQcobfeKGLLKLZ54V2S+4YkoE3MWR08jY\nz38b6LWhey5vzpjxdG2TBLvMSPhmxjC3e6bcZDq0HZeu0YiooA+iRNgobDKI\nKoqGoB0tvmIRnpTaz5SEdabWj0BSJwxuCdGqBp3bc1NwM5v6zvGYnbCB6q6B\nQe0jyYLcIhB4D6ekoISUMYtHyBZWB93IBOyQyKErPzzre2Oqx8Idpz+qAs48\nadjR9MU52qzxWawORQUvaCKBxz1g9rs7i9/78YHVpTEb4D5vO325ICuWMM2M\ngEDVmYiej6rrv/wkizzzqWfQtU4JOv1q9PikYEaus/KqG/jHY7Q8ffInc41G\n1qoS\r\n=5tS6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBoUYIToASkN6YNld+6XIqfSfhzxuMmaJwzPuphNZ+UfAiBtRKAofYooKeCu7mF4hiuSne+1vW0nCKKCAfmgj3dT7Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181214184807_1544813472578_0.3943552003586841"},"_hasShrinkwrap":false},"1.2.0-master.20181215074737":{"name":"@atomist/sdm","version":"1.2.0-master.20181215074737","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"917ac072d29ff93b33d253a82e1beae4731be7a9","_id":"@atomist/sdm@1.2.0-master.20181215074737","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LJ2Z8kqVSYRMVB/3aLFCJf9t+sMA31A5HYWZUXaMKUjV5kAqBMZ+Kyh/xSWiFKHkea/VQAGLdsnkzbPiDjDX3w==","shasum":"86a2410ec30871ebdb8e4d918d3d86d98c87625f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181215074737.tgz","fileCount":715,"unpackedSize":5478853,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFLJGCRA9TVsSAnZWagAANAoP/jBtOcqBBEPYLFnopTxZ\nbreKR2Bze9vRaswCMGzuFbjzE+k94A3zbigw9WlfPCY53ikWOSVRx0+4BrW9\nIoRg8dHsR871SW30eYZK0SIRYWUNkFZPRTAyADOT4k7/+rKdYmAbUcXPeKzb\niakgJrvYx5X/cLE9JJy2s0c+ZU+fZG2bVGAA01nGxp1NYi0CA0Tq0NmQDetk\n5NtFK6OjPwcCAcq8g8neMZ8gCretscPgLzhsRWBFhAHoC/5ViOtWXOEu3F2j\ndFwKUJYWLsFGysDRxipn08x4wO/nrSYyhzTlq9q0EUW8k+Jb3Ew//voWegNT\nDDTipWGNyqkNw0Cs25nzDj2Thiq5FgvUciAp9sqpsmDVHkv38LUm7iP55JO5\nUFsNZDGmhuTPPpyqQAa0/h1S7cXy/0fIJdFGujoYcG9WEH7xMNuD58xze3cY\njdX+BMzCGWj9yApMF5lTa+jMGWdVHh1Dp+cbRb0ZECzTX+z2vJtagshn/Djm\nl/7RwKp28eZfdzBnZIkDwHgyYYSEssEqvbobtk+7asLJhQ5yeE20kGer/f0g\nD8JG3z2IWljBxTLcQRVXEvH3AM2BgtVLmLEuMxMkLF9uXKhZcQVAN6ryuCOM\nD4FRoB4aVWFDtbivgcCXvT4gqqXh4NuwhngdMYh2EHfvB7kH9ybcOxZ1H4Re\np31P\r\n=OnTf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsdjf6kQKXny5mYpXm0LhenH2Yt/GD7ZPOYs1/7f4D7QIhAM5pEzbggiGURuNBbiUzluCDzcSc2+LacirIEn3T1Zz9"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181215074737_1544860229151_0.561384750225242"},"_hasShrinkwrap":false},"1.2.0-master.20181216144121":{"name":"@atomist/sdm","version":"1.2.0-master.20181216144121","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cfb8e06a888a3eebca77dcca8299d415f4a3db9e","_id":"@atomist/sdm@1.2.0-master.20181216144121","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XS5OTV4/MQFO1oaf2TSl4E9BphUkkUY+0FcsIww+G3mQ1hbjnBwQFl9dVKNRWCWoFnXBF0/LVahGmAjJrfepqw==","shasum":"d90c5c51143d4479e339308f6064d4aa1036742a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181216144121.tgz","fileCount":712,"unpackedSize":5477125,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFmTdCRA9TVsSAnZWagAAF7sP/iL+PkkrtWp17e7HzqXn\nu4V75c8qP/zEBUMVwYdmwopFitA+mTbJtxjrWou2mVR1oLAWEiFkWDkAWj7o\n03HRxBMMLC4boZX/MTtbs4O1Ok7N5esI9S7zEY2YXCzeT87ifoh6jFaA4B5q\n1Md0/akWXA0KymNvUhaN4tSTA228sSnWJlQ1CmSlx4HKX+kQQonwXXYPFz+Y\nGKFuF4/TJZX2sZ+WA+d+gWAT6Y6z1BNSyEDTbQDz8pXuouMZ+U7RrrkG1/18\nGGZ8c6CmCuhbt/oIk6vlzLXW7p/bWG6QpQ4LDvuXpDU08y/dfuuun4HLbbNE\nuKg/CZLNA8QkYdTqZDBIYJTO82tiblFKhlJwzdJnuzxukv2jLw9iNjttbJPp\nbp5Qsfe/Qddg6Gz88nDgidkMPm48dE5fTWp6Xiec6DRFZLMLPnlv5JX8jmIx\nOIEmAYrOZeyELYurf3KoyzLLu5hmFUVB0Rj8N7xucYmqc853qoQW5bmD/m0t\neVUMsqprsq62L/UmO9opMHjPxPkNpP+3p8TS8gbTrBYKXdb3eGa7fYsNA0w+\ngMAQzv4IDmuoJ6gJnhDenSkrY0nwnLW+pLprhLnz/IqAgyniwbOpdPTMbGHm\n9WngAXF1GyCagArngCP7JtLURWikVGg9KSlyjaZNepxx2HSArvQ9NGfTZMcB\neZcy\r\n=RVVK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxAFhUOokYkTCQQ8As8hPl3Ip0kPY+DH5tW0VV+781lgIgBI+yEs/wphrN5GaAkuWpB3RUF9NngUXg1iHpDvNhI/E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181216144121_1544971485281_0.1422909294988921"},"_hasShrinkwrap":false},"1.2.0-compute-extr.20181217010313":{"name":"@atomist/sdm","version":"1.2.0-compute-extr.20181217010313","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1a6c150acc5797b45b2b09879acae4b9a7754ee9","_id":"@atomist/sdm@1.2.0-compute-extr.20181217010313","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4hDz/LTjJgigsKXMJMDk3ecWJIu6vlkQx5QTBZ9CqkIGRBUm0AMwTjwNAHceiEFf3cSuwpHBdFNLywqJqZSp+A==","shasum":"90de9eea2a81bd895b820acc38e461d26f0cf209","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-compute-extr.20181217010313.tgz","fileCount":712,"unpackedSize":5478610,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFvaQCRA9TVsSAnZWagAANUEP/0E3LIkmAZqD19lHJp1h\nfQqlyQ4q24VNh7r06wp+IM5JKgZ20CG8CkaQxVtn5y9xk+mGYShwTkblO/MN\nCW30fSQMomzQ/3ea4GmFTeZFZweiK2R9R757IqqOrOt7QM54zMO7d2nOPUlr\n11MBPBGqfV41IzPV1nsldgMktNSYiiUQ+e7vwfeuctVjFDq+qcpPi9ukcvz0\ne4gGrFo+5Keb8SSwi90OMY597Ie1PMZQFis22ryntOTszWcrgQBERzYKDAml\nwdgEfOfqy0TW/nBsonWhvEJCGf1L0EZeuHniRMNej1iv6lV9fv66e+6MM+gG\nATNei81Qd3YHAZn7hP2YvyAkIKo5pSwhfiGqbDyIYZ4dFPqEVOA9cAvzlwPx\nxgh4OtKhcJ5JQT6r39HTuckOcX7wX6pP8rIg1oLneaLjF3aMg48NiStH+QdE\nSYzAzt73xtYfGAYbEKTllcslLUtcPazVXB0bp0H9/w467JwCj+03Z8nR2LAQ\nuR33fiWrjG2Y2Lw7K1G2uQZUatv7T59eiIiR5xCUw2+Z2tr0lG/x4apruHmU\nA1Q7R3xgFeQv6RizzsGztMQGr9S332+iTQWsU8BKjf5HFkF7Grrxh2mQXwsG\nt0B5x7YAUo9NIRu+AG17usarf8gMraFuQT1HGCfqAXvYza0F5DUKQFTZNs5Z\nO8j2\r\n=3Gea\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCugAUzYc0o4qhbdmZQA5b3trFjOMOSd6Cii8N8pv60owIgDVU9GsfGtdnWGAc189r6G6pPH828Ogte9hPyVSc8a+Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-compute-extr.20181217010313_1545008783545_0.7359775683114005"},"_hasShrinkwrap":false},"1.2.0-compute-extr-jess.20181217012006":{"name":"@atomist/sdm","version":"1.2.0-compute-extr-jess.20181217012006","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3985e673660c5af3bfe3faf0707469c52f1a6937","_id":"@atomist/sdm@1.2.0-compute-extr-jess.20181217012006","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-m50k3pnGIpD2U6qcAqLpLyoq5A17bbabRHcGuirQ1AVqH5lC8EFhmyXrx52so872b9oJ/f/DiURbQFI49C/fPg==","shasum":"68f90dd9d7d430188d17288fe9249c391de3719e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-compute-extr-jess.20181217012006.tgz","fileCount":712,"unpackedSize":5478883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFvqnCRA9TVsSAnZWagAAUEYP+wdnUbGrX/omjF/GzomN\nAOARNsoq/Q70MjCvetUbe10I8NXPRAELPsWlwB3wV8IkXq2wcAfdvUk5yQvT\neNWZAPeKbnZA1R3Md3gF84eRAUv0DfW3ZahgouZldfXXCdF/mI7JLu3TnAvW\n2gUnJ7srJpp4O8zyY3gQFr8YJq5UTwHfryA09lFeyRc1k1C6hvzZu7tOmxBI\ng7gCGdcV0Lyc3uuBLFs0t8ZS0aR8HTz7aOSg7/WGozGNGIskkDTHeqPO5in6\nCxsvt9uaokJw/1OGOJnze53drB3TJl+FrnkJ3U7Jhus8Q2F3fmgtteCQByP4\n/Qld384T0xKR0D085e2VdGnHmxvwWtHCrN3VKZQcikHSrlHdXbM6anuMtzVx\nSiKw9TsYpFcbeS4D5Hwf9zj/V8skQ/p4FXgtEaMZngrP4gKKyXh+TCUP2F28\nzUb34H3w6uUVXmF84Hk26RmfWMGftMGRsCvW4MJglUcQNQmSRvD1b72lrZgY\nwRaXsJoESR01Cw3ght4ZB08LdJr6PgbELu//Y3IP+NiGER8/5EvyRViVR6y8\niT+nbmYT6ASHXppVlZ2MkBomDnVy+T0Mon8EJrOxrZtbJyPhkpk6Ye4ed9B/\n2MLUoLESEx8Qr5NpcLQuc+jhE3KGLFyEN2xbOyV5TXXbhvcJabLgjvYZyGSw\nSsey\r\n=PdEY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBOuI7N3FLk6jZnT01BFg/4S1Lba/6iKMJXtbNHDKgr2AiBqTOe1DuwehXH/z7Szv1GfB7YfJ5774r8F7cyYPozypA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-compute-extr-jess.20181217012006_1545009830512_0.20868685266321974"},"_hasShrinkwrap":false},"1.2.0-compute-extr-jess.20181217012345":{"name":"@atomist/sdm","version":"1.2.0-compute-extr-jess.20181217012345","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3985e673660c5af3bfe3faf0707469c52f1a6937","_id":"@atomist/sdm@1.2.0-compute-extr-jess.20181217012345","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-p5rCQ2kpK9LOnI5qB6S3EXgwb1LFEHDhSwg3Iawx9SA5hwjveUCeEptOfBUN8J1QNE/7j8tBNtFhKNV2xzTAqw==","shasum":"3cd3fded6c93aab325e75d9a75222d69c46da7dd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-compute-extr-jess.20181217012345.tgz","fileCount":712,"unpackedSize":5478883,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFvtnCRA9TVsSAnZWagAAVdAQAI7lxLO9tEQjyHP9qBRN\nWVL7597kbJIjwvuYqbHNTDn8km88oL5X+XA+VFBxSMIHssrF/cj4hVsC1z1e\nnC/zmEj//qc6yIz1RXyHjXDFNfDmGawZpT8DOSELKSwDQPG/0a+R2UPxvkbG\ncop1ATW6p4VUu/kWFJ7Q0BsHqftMV+sS47jmRfYWfFBge1t/KNnj9ELc1+wr\na2vH8odLDC2EHbRHNU9foqLVwaihAeWFRvtAw1s2/+DeKn1X4uZiGx8zw7VV\n1G3qwdpKPC9Wc+b3c6FcnJQNL3Bb7IyHfLjqortmivO9qgee78ckv3tu0kse\nWurQ+MnMDQjFB+tOkWuYtJYtGrwcos2sXVxDPV29RbDTo+s/gMiWKvOuANJN\n8HHJfF+VhzK3OZfzXn5FXvsMh5TbW61XyNPlkgZGJH/zVjLhaMJ7E+ORA0pP\nc21kcAafI8/uBd1pKe+1JEZGtaurSAr0I4jSRajDR12GXGYIQa+H17rhssFF\nTl4f3LYA4MB61M0VVI6a5Mn4miCWMrOhRylouutvldenMJ3aCqrvl9nPgdQC\nVE5JGL0s4jreEjF1sedpOtNGZ3zX1XSp8f79OsFQ39bvbAy8K5e6mLmhsF4J\ngkBqhQy0uIEIBZU1zkzvSIQdcesLQthCTFmg41CvSW7VZQVTB3sf1WUNs9Wj\nvu88\r\n=5xdj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDrkWIfRl9OpqDbLvpylTAVRuK5NaP58m0KULX+kXgfsgIhAOfTsHiEbLVFaMhUGLKGm12o9EUOBfqY5OZXdopcrtcx"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-compute-extr-jess.20181217012345_1545010022419_0.2616873770475505"},"_hasShrinkwrap":false},"1.2.0-compute-extr-jess.20181217015741":{"name":"@atomist/sdm","version":"1.2.0-compute-extr-jess.20181217015741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5dd51e063678ba5096cd10c6116c15862c374f9b","_id":"@atomist/sdm@1.2.0-compute-extr-jess.20181217015741","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-E1GMU8OwESHT72Fy1ywbf10jur3vWwVBXt9nQ17Da3JHXaZdqpD9g6N0W8d9WUwLH6xT+tvbupvobNdYvomvhw==","shasum":"93f229185c6f641c8db1de7aefda0b38ed15f0c8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-compute-extr-jess.20181217015741.tgz","fileCount":712,"unpackedSize":5479014,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcFwNrCRA9TVsSAnZWagAAG8AP/jgi9p8d9zHc7R2FvuIr\nbFUz9fZDC2mOIaxz5NV5eL6VqlyixIS+jhbKm+Eb2kWZdEr9cQ2QlI4pmgo9\nMhXQGTqZmu7HFr+Zwsr5TkRPv21mpj+f1bJ/FN3RT9tvbJyxVuhjaf80+RmK\ngacVCg5PxFLlhmj+5P0eU3vKpW4jdclkHiHamMig5cmkhe+4IwJMrBuEOH6N\nyRNPc4yuOM45oW4T+Zk8qPGZKfGUFQUk3pdZA30a/Bc6mK+m5iLZI3F9/kgZ\n/FnEN4mqBfJRwiZvldBYCvwQpPAQWbOsjiYp9XVPqJDpn/uhfeRPockHuXW+\n4dqgfyJ1CZyjYaNnR8X+VInO9MSHfKNRi1IfGV6ma1/eh7nyQtKyRZG1/+Iw\nVNUmkn8q/f0GyDsD/pLA2MN0FPqN0IlAQCjF74Ou6MuP+Dk7p7AmXPIFqcjN\n46xiOeegn+wf766SyVo/tjJRSCZdMKSH9BZV16vQLpIxVBP5DJJf8F2bpA2A\nBYwvTJexMSTcLS7Yiu8sb8JR/fAanY0ALpU7F5u/06TRKRMLDpeggIGEfnO/\n2OOIKj/lRVtn9ipPZHEHk3XJLFVw4NK3SGox03K87+NbeV9mXiTkcGqf/p5h\nZ84325HDlZ6I+zqSGIK2ujQINhRHnZv1XXIUFlwOJMnYVgXzgNgGVlsmPjUt\nc3wK\r\n=KL2H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCDxndkqasNyNIfbxkd/mYtP4b7r1MGyK0hnDHx2MSgjgIgWlXjRd5VKzNPwB7u1rL+D+z5g/nPBB8SmOsFnawh+9g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-compute-extr-jess.20181217015741_1545012074314_0.6777614095322773"},"_hasShrinkwrap":false},"1.2.0-master.20181217135326":{"name":"@atomist/sdm","version":"1.2.0-master.20181217135326","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cf6637843c570eda7a71e6b0820e6bff92ad5717","_id":"@atomist/sdm@1.2.0-master.20181217135326","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4QPJjyDhqS6Ywk94Cbc+ecR/a5EHYE4Yu2WJA2L+x+J6jaWW6WCjiY2mSvpGqgfAFZgM/KKrsRIhOrDi2xy8Zw==","shasum":"2fccacbb5e26da52047dc9aaa778bdcc80ced330","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181217135326.tgz","fileCount":712,"unpackedSize":5479106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcF6r5CRA9TVsSAnZWagAAf+QP/jZTWlZuk3cV8x/NOn12\nMbcffPiVRXEXqWuOkM26km7NdlYMtuUO6/IgTcAl618Iknn3eMfp3T245nmP\nGyMq0q5fQD0FQzWPVk4AapTcunckUhTOXqB1DTlhsgGPqSMnYAFmVZApd99T\nAawsNRDWzplgbUMRBE1xQ6fALutjc7JBmS9x922JMesbPIRT57pQyz2NLsht\nmkGO8arxIZHFfY6vDR1JO9VUoAVjpSbJXDGNWLGF0Z88zKZE07q/mmxDMzcP\nPRPvRcNpiolu0kWLotH7QDKVEVvGlPm6PgXUYUE+kRLl5AZ9TETJP3hfPrEo\nA/DJ7Yi/6//mlfWyPRspTtwjLXyPLQnSgE75p9MCNW8X2v5Oji5sgxgalSlL\nwwtbc15i1EKeNxs18sq3/D15qTeG5yhHAB9OdDnk+fxrbJ/9L/vbihkK9o+w\njMAsEdixo1nJh02r2jnypRWIP7P/7tNq7U6VmVSrtjqM54t8cH/0ODdZlQwV\nvk69U3bspkU1ykMrYq881Os/ptvA18aBi9PBNNKDgUaWbiRPxzoLn5BjeSm6\neFzwNJSnDrSpRci/RzvWm6Yhh695TEKi513d9urzK5EHwpl6OMfzC0wU7F0s\nwIf4fvOLyoPJI3+2ViGtgv/v+DcjfYG1/fyZSRvxTenrPxBjML7SgeZPrS8V\nWwGX\r\n=jd9e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIQCSgr7l1FpfhExbUtIkJ8bZ6B22/uvR9nfpZsGU2CLZwAIfcyKa1ySx4bUf58VEAMkYeBKAeIv6pQXY/Zh0Ym3D+g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181217135326_1545054968343_0.07893652947787944"},"_hasShrinkwrap":false},"1.2.0-master.20181217165228":{"name":"@atomist/sdm","version":"1.2.0-master.20181217165228","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0a8d8f488b83bfb6793f64c08870c33ea4c51699","_id":"@atomist/sdm@1.2.0-master.20181217165228","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-35n3BhugpAZgBJNljbU48cZHE5hzPCcV5LxslJzjH1KQgueoIUL4B2CPDQ5/QQYl3vGyJMQDr0IX+zV/n5UURQ==","shasum":"816c603604ad5e446d7ba4b718bab91f344efa33","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181217165228.tgz","fileCount":712,"unpackedSize":5480189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcF9UkCRA9TVsSAnZWagAATWQQAJ44or49zN2nNjSvr4o8\nihgdoo1qwxEVRN44V3NiGKZeBkCFYSAvFaFJLgpLGthRRUIxATiFs0LzKk0r\nYmolqbEVZFZpBTimkPFf8LijtFbAs63oqyatayxv91a6TO3p/gpJEszHB3ws\n6SfOG1LCfAOoQv1lh9trnuaplxLSAqIxBho/w8+kreNanmJIikzz8PP425VK\nFvw6giUB3Ai4E+NJVOeZW7KKZi7DKrAwXjMNsbqwiEil/fHrvWDPcAMnKU2G\nn0paDwXz4DBwCNrKhbD8gfqgnBbQi7bTo7GehdYldd9W8PxKdZpMjvWsWjaB\nZiqONNCDklnNc1+UU+MwuPlh3C1K3AogvKNpOZ9HT83M2vcIQdwbUfb70WeN\nr7LzcKyUK1r6k2aysP3fJ94Osq93rc0p4B97csWuuZFWgtrZ0SxU70PImrIu\n9T2U3dDgPnozld2dkQlm23PK3VMAIcVTWBUXN5i5RJ/EMb3oS1KYKo63h0xw\nErcbF8zmF4zbennI1m8YvtiFHH7gtMFcrKhhjizqJ9/97fcgUejuu5qlxuRD\ncr9oRmIa67HWqPjmvRzU4yuFEg5t97uPW2s8rZPNBjLpUus6z51j+a24CpEP\nsn0b51OyK639EJ09KA5bYA89eCUFCNyhpLeId/yxRqfgpXbW/sL7lOXC99Vl\nd6Gp\r\n=eLrH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9u7AiSVQ8opuDWLqK6DKm/Ry9HNp1RugQUyGCIqx8/AiEA2q6D63NOWRUrpsEWxi6kJF02xNGWRJeusgYmdC5k8e8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181217165228_1545065763945_0.5753362821222681"},"_hasShrinkwrap":false},"1.2.0-master.20181218005143":{"name":"@atomist/sdm","version":"1.2.0-master.20181218005143","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e7ded0e1d73a6de4035fc762c0ef963ef7f4d536","_id":"@atomist/sdm@1.2.0-master.20181218005143","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FarenQvR84BzHdE6v3mQeiOJXqr8HO2ST3gtFcccI4QxzLZ20Ju/xhj03OXO9wi/I45Axh8bY1f0e8sGfI6s1g==","shasum":"15ab9d64b8fc0091922e73d7739e4523a6eec716","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181218005143.tgz","fileCount":712,"unpackedSize":5480209,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGEVWCRA9TVsSAnZWagAAc7AP/RgVovIvafkHREk0mAzu\nmhJ1n1d6TGuy6iqSGyFglwvvOKGzXf+gvbkqI3RVgbCrW10y88ZBoWWnHM6e\n6VPpAmW4cXcGZn9KFKiGECaRkn0Vy1xtbT3he8cspHoLLT9o+mz6YrMbpd/Z\nRq+P3IMt6B5BSTEYXmMZMZa9Fsr0EvHIRPpNJiqcCOG0d9IgQ1dFwhlqfAqU\nlT0GQYtEISkArfPDakOqJ6m43sW+/4exQxAVIM+VdKOAZdUd87XZZ29ptypw\n6bX8WyKUZJYso4288yNNv2RkEsEisBwMrsK2tqQAEWL7MMxn7uLMCmoe6w5d\nrsIxTfog9doh8Qz86rXyalJ1zpbJ4nuz4ZGHkRTo017APiSNDzXVRTAcVTQc\nVtOQ0wdlvlnXUJUG7y1i8NuTTpF2TwmqBiYfb0H1KBFwWCHQxsvwVLX9xJEL\nkNbyLOZeDcle10Fv4llCJLXbUm6EsH895Brhxjoate/toP6YEi2ilFL/Tc1/\nsuwVRmV/X5Cd7we6z+tdTZM/0wYaw0CoE4D9ou9iv4gv0+v7VdbzIC4lT6Hj\n5Y84IGS+2+toCkSsnXi9C4qrKLljvBvQg7wGzFLuxrnN9MFGSyfFbqa0WoFs\nlHCU/N+oq/gr9ERjtO/J0YMy5+Tbb8gNYtklIP8SSBeW+dvb+cPHBx1VyWgt\nOtDR\r\n=6twc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFpfFQs37XVbmALN5rRD9wU9AdcUR1uqYmJr9mX4PJvtAiAZfm1qxfrsYb3eslenRxenFKtVIzvoXQcEOynaUokysg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181218005143_1545094485884_0.8878078624280825"},"_hasShrinkwrap":false},"1.2.0-fact.20181218214318":{"name":"@atomist/sdm","version":"1.2.0-fact.20181218214318","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cef8292c9ae7d362efbbe2839b5669cd630bb91d","_id":"@atomist/sdm@1.2.0-fact.20181218214318","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nRm2p0K+DsFJfRn1t/AQiCZ+9IAy7LrLjCHFFLY+GSn5RKsMAzCh8ANsbQN6pD8t7IHnp6rNWnLDAZe1BGfZyw==","shasum":"d1a113cede934489b4ce1e6882cf901cf3681e59","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-fact.20181218214318.tgz","fileCount":712,"unpackedSize":5480138,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGWq1CRA9TVsSAnZWagAASQ0P/1kJFItIFpRgbWjPfsAR\nMVXqxN/dOF5+be6ih/Nm8GF2y1li4fZSMD9Do0KYSXsMdbJ4NQdxSiSHh6Fx\nQFyz2Cc7s66pbzSKNuTojTSKE2ezBJv8hniocYKyEM7WJ/rbQ2weEDWWayYi\nMDG8WVneyeRJPlrTw/SZDeZCMzdvr/Nux+soosSUgiRUOFVWSYe2oI8yebD+\nfFDYp9cIxhP828RTBwiVCKvf9xs5NHhGSmbgmqzZULY0C2rE6ytAzZ1ElcHu\n+q+Z4oZDm+4GfNyzQS8cL1jEhut+JKWaSAYKIV0Np1uPdP0oqAw03CqAiZCV\nqrIyM5Rf/g11R6VO4dO8JYrKmJrB4dhGMtpxziG+M1IJigH+fn3vHY1iJJcX\nSFoOSz3DfqByxt2OA42qBifynU1loF01fXL+GfOhnNbJde1CHTmcoivQR2L9\nEPVZCt7Vt+JX9MtPF5ZYJn5W/tlAlbSMuiLgfxC9eIGExZ9fHmeqz9/YlJ8z\nqp+8n85uLXBHjWE3kljQHah9jx2sQ4vDPctJ6dtI7qAt2b3ZT92M7zX11MOo\nJufWPSwkMT6YD6UTkr8+kN6oomXxqLkZI5486IhllW0mD1SK02HEQGcCMZzG\nu/jV6Ddbz06aAxM5a2tpyJnd7UEaGoH2CLrd5tdanaEmLmLp5M+M2hZVcNZd\nxMCE\r\n=HT5B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCI4NDSE+JReUot/plHrsb80ocwoTQkWo3jhwmzb6pgoQIgRWW8QeMkMNqTPy81g9igecUlZfVqQ4aSzcKxQWf8YP0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-fact.20181218214318_1545169588875_0.13329603509309673"},"_hasShrinkwrap":false},"1.2.0-fact.20181218220425":{"name":"@atomist/sdm","version":"1.2.0-fact.20181218220425","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"77bb81c2be5d81e0bc4f33cfb4ecd5a2fa3ab045","_id":"@atomist/sdm@1.2.0-fact.20181218220425","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7Bgp5e7/0E7T6Ce9U+iIbR2eWBczspat3HvAH5hizh0ph4Cflks626rMIyHlfprXBW6DoG2ITmBTgj7AeIsPDQ==","shasum":"556d29ee58caf95f2c32df9e54159782cccf978c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-fact.20181218220425.tgz","fileCount":712,"unpackedSize":5480291,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGW+iCRA9TVsSAnZWagAAs5UP/jOv4jqc5N2J6CdxbWgt\nFHw9uH32EO9Jtbyqjt0pJPHI9HsK7y/8jYFpGTowLt3CjNx5yZQs8vlrL0jQ\nRw55aIg9anP+d7zF7yov++ZfRLXNPjrWdOV79RJC6+ldRWwerEkXXWZr2KN7\n9C+qctPeAroC8Z+HyMlI6rGP8AiydgbXWfVq9UQrsHy9a8KZqKJiz/TW7FEe\ntZUedcQ4tiIYZr0RWuVhlsKbp2W+maM3UKqa27rz5H8Md65shptOqJJw03ey\naJ124o0b3DrZ39dOiMwiGeFUg7+pZX/uxbou+nyN3aGnUjQ/HWZYZHzQJqYW\nommBdtMywbE7GnSqzzUPXvlOOpMttdTZ7t/xvftnpqr6nQz2SYUBBjXx/F69\n0c3H6Fs55oYxPhvz5VohCvZaglu2ghe8HgFssyP5HZSEdYOfaXw7VDuRoifd\ns0HH0MAL7cDFGdaZ6hVYT/mInoY9MHK7eolL1ypvyMwYkp/sF7ZE+TOpt7Jl\nArrme+Fp5vajbFfm9LwZqC76LB+qlkU1flznkApfXSLBT0ZRkb3gxUiKQu4q\nF/ydOkJTa/WzndX78H0Jh3a+69av/9mD7DRhX0Mu90VIZY+OiG1o8qqb4rBR\nGwZd1BRnE1J9cyxtoEcXojSu0tuY4S+fvqiGB0egPCMgnPHYzoES5MQvvCNA\nSLqy\r\n=d3R3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGjjHQCbNGiP61RklcfTNsUWswruoD92EFHzuMLcTPJfAiEAgKijcVkOuwwoFPVHn9i1QifBwffkZiRj20POUfw3Ok0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-fact.20181218220425_1545170849603_0.2604103414085255"},"_hasShrinkwrap":false},"1.2.0-fact.20181218233453":{"name":"@atomist/sdm","version":"1.2.0-fact.20181218233453","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1f0860ac1ce9593ab6a13a9f67c709fddc69f914","_id":"@atomist/sdm@1.2.0-fact.20181218233453","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ILsFTns/0kugX0DfFOw63rLPgn/Wf66XDrt5CULHBkhuCdAPEKK3H/gWEQnRAR1xahNPk9rZK36YI/ugZBMF5Q==","shasum":"62405903e749d60c7c8a9848186e027f56b8ae84","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-fact.20181218233453.tgz","fileCount":712,"unpackedSize":5480580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGYTbCRA9TVsSAnZWagAAbj8P/0t0g/V7CGJtq+/Ina2g\n53E+Y8o3cVcC6I8pT8GEssmNy65sx8TABUSS/hU3tj+8Pi0ImE6R5tCtbyCX\nFt7OMSMsaUQFkq/6gkUH2Q8Mqtg3vrTPgEs1MaJPndeNhKqzJ5j5E/8uO7k7\nKtZK4CwNXK7cpxFDJZrhz7XDeI4FIuAHzum47m47cIhxvljcEObaEIGxuQVd\nyWSvsa0p/we4NCZ4Qxu24Qx2d9nKqx7DzlMQ6osnGUeMKeLYdE+f3et4lHsw\ni3DtWhFSmRz4jkPMao6uPhZUGx7CKHMs8uLMdriU1mxuaoQApAwlt8+pAuI3\nj/X0yzYb7kSZtEPuLc3IxARiFdShVc0UBuzLUQMvIBrn+PSoRqd1XKzpH93L\nhttCR12lz9DS5R8BD+QhQuamXY9Qcn+c8lYWkVjnDZfbz1HGdRIcK9F41Jg7\n1nZUeju/HAzDOQUkAWm8891PVz82bJBXki33DffCBdpKoXBhfPtWPjQL4KNI\nVIA5QS43ElKx46UPRvwURfn4CYpKi/5hYKUo3G9mr43uwB0QWFs1eFGSRGEi\nCkSmrBcNVynvMFO+fIJeM+cauqe2eZn9z3hPhV7GiEkt2hpUBkNiCo9dmCOD\nZsyil5y5tc97bIlCUlOmwTHj4obHMjMbf/Nfrdu0nHHVoBeNr0CbkwRCJDID\n0YUj\r\n=Ue64\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDP4q4aranEyxSA9A2vy4ICfShc9dTa6eZtwl47KHGXywIhAPZyiJlRpnODtDgRq0RlSiprS5LbW8waC2cO0uY1N5/P"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-fact.20181218233453_1545176282760_0.07986958534589639"},"_hasShrinkwrap":false},"1.2.0-master.20181219005648":{"name":"@atomist/sdm","version":"1.2.0-master.20181219005648","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa218014a21422d6daf2e16ae0a2b56405be9587","_id":"@atomist/sdm@1.2.0-master.20181219005648","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9zUnO8a0Wh9Ej6ktQEncf3VMai4j/djjEiIpgBTGvQBNvhx+mgDsIGo/LuFTdEELONHrMowjtbLqo64QizIFHg==","shasum":"113183905e19e2b3ec140bedd0b1319f4d884375","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181219005648.tgz","fileCount":712,"unpackedSize":5388616,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGZgVCRA9TVsSAnZWagAAPegP/RtDqKdOrccZyzoYo6gF\nzIQPOo0GEjxBMyYQ+cY7TttvltKm+4t/ZclM+tm81qsuOQBBUXvdk9i6k+Z0\nldyf79LM+bUwiu/p6lUNI+P/mWfK/ATcGRjVbaMsmm2ccvQ4WswPzUGnCUZb\n0PB12YdDD54DQLTfxDnJxw10Cl3iEJGifKhfdhVysFx0dWZjvgCoh/jP1R+a\ntWl06CpbckvEt6Bu/LW4GRiapOM9sllgjOlVLjvCVqqYLI/tfwJ89vNV1m9J\nPgfPRL8PjpFv2yT71TSeFPkyujILWt+JBWIvRwf79VewhRgyf1XNQ/dOUw0A\na3xTJWFFBsCKf+SNKUPCr0+w6fUqj028jtLdnfWcZVuihpEfvvaIgbV1YjPi\nzaZHOiEAuiGJqALRqX8eoqHbW7vJzv/7LmVv0Y66tJG6AFRhQN/7OT/aQRxz\nNKo+jn7bwdl7BY+GgxG320M+RtUJJ3saqxeQa/MXx4pabiLNGqdeOSF/9XgR\n2yPhobsvLbdHwcxqL2VB6oC2R+LMej6zYEgoggiDu1DwLServ+f3HSlWt6+d\nWcoTbmTsikU09AaNvPEL+/YB5hXmIuhxMlPGMTrFQVcivx8IDShgUwQZpRmu\nF5Mwv3j59auWhxjSD7J1ykqHLoNl3gZK0xvphfcyLTpKg+nalzHeavfMWn81\nrg5D\r\n=unEZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFHWIfu0XLJuuMU5XLW13gTT5p0IgakaZPltHsTZ88K1AiEAwT3E55hfDpTDOVoFFPPM4NPXg0yB8wrw02buIV0D8as="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181219005648_1545181204606_0.8159162615931839"},"_hasShrinkwrap":false},"1.2.0-master.20181219005843":{"name":"@atomist/sdm","version":"1.2.0-master.20181219005843","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa218014a21422d6daf2e16ae0a2b56405be9587","_id":"@atomist/sdm@1.2.0-master.20181219005843","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HNoZLz/PMNyUnocuR87HCmM9xlromD4i4GuMe4Y3B+ySR3OI3nhAKrKymsLQxEGXAj1WPs2jRCt4fyRxrNlPaw==","shasum":"b731bb02b50ebb3d3bde01977365aa8e48b92cbf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181219005843.tgz","fileCount":712,"unpackedSize":5388616,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGZhxCRA9TVsSAnZWagAAAkUP/2Mva9EY6OGszZy8tJFP\nuniibBTke0ov2syRZmvITSJmQ2VrFJ10NYDOpoyzg/i+7or4j8dAgRmMWbgk\noyhLM7c68YxFc1i+zH6hX7+1ip3J2yQXL3kLjNeJ4cgufgolGpbcMLlL31df\n0gP1ZYi7Dp34o2AXrRjLLdKmalYTdnykOWyWdcQ652/Vaaw8e+LBES5G8XQH\nnTrNM6ZZYnpeiDMvjy9UhUjtl7f/O17/R1bFWk1wU+PQOD6sMI6pocLw+z2Q\n7Vm8uBpmgYMQAXxQ39SRihATdO9qd4OMBGOtpWCmt/SYGtfr9Qzt31rnd3+D\nRj6hcvokQT6LakfPVECKVdo7sBwXwnaPYDi5hHxsqjbY9mqQKDk1PjLcwtXi\ndIIwjyRZ4RzoanfBU8eTRxDjGfzQ2wfhFh0f5mFU02oEx49cCoexIlKrOaKg\n42Ka7NH+lIqM5lfwjG7JuNWqSNUkSlH2jx7P2qMb5VyIwhH9OYcI5XPe/O/8\naf5/ITMYGW0uH3yKM9ma3YIU5bfm8j0/6MX3b2sz1dlGJa8Ha159sbTiDE/M\njz15BtCaAAzkM3R4NBJ59mQgQFlM3koTsbsLhluRG8GNpucZ2KI4Z8dAIHPM\nb3cUVBGNFLpzCg5v2W7xSONE0xyKb1ytr11La69py2R7smQSZeCEsT3Mq5Sn\n8aF2\r\n=kCjz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHf5N9+Dv7fENryA9SDofc9zV3joWb9Ohqad0FlKFQxuAiBCAKOp5d2rscDvYeBa6cghiyAICvTLBzH5Exy6tycHEA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181219005843_1545181297084_0.20057155945127603"},"_hasShrinkwrap":false},"1.2.0-master.20181219023620":{"name":"@atomist/sdm","version":"1.2.0-master.20181219023620","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c2747f25052ceab921fa2e48331001eebd108948","_id":"@atomist/sdm@1.2.0-master.20181219023620","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7PEiM0hdexZBjFHPRAjLYf8DKXeFf8znr8el7skVCHIcc9ZoYfLrUKFtTXNK9ONu6N11P7jN2FueRIyj0j3w9w==","shasum":"55c03f36e0a138ebf7d4aebc953b5806a2278c21","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181219023620.tgz","fileCount":712,"unpackedSize":5388643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGa9aCRA9TVsSAnZWagAA2ccP/0Bed4SW47u+UU4fV/N+\nAQkM/by0l97YJWPD44yNeYrbaXvJ3zcMhG8n4F+xjdEYXy8CUZSUg6zEs1A1\nPFFqyiQbQ+5+yzcr4dbGiHo9MNvXNdJ+YmK/D6fJvoIZaVR1NnVh+QWFWTpv\nxrPS34fjZlC3dqI0KjfM8fadxyLf4Q2EPwJcMo8ZLkc+gYqg/1WyhOc+VWS/\ne4di0Q+wBjgc/aqe5hJKZ8TvdiCH1I1o0jgyLZMEXnW4vm+0CgxZlp7KTDcA\nynmTEw3L/h7e+XzjjWFv8kvN96c3pev57Dr0Cxib53haGQXVPjSWXoUPozVs\nt4WCxbhe3gyghFiErLBtndUR3sYO/4IcafNK5zjd/Pq2f57NiGcr0Ae24808\nEhSz7oyuVZ8a1AM9J9SIDzYLyrX+YlZNcTZleVU+aLu3DwGnmn/Q4eDkF6pc\nyLy7zxOR/QNNDiJ37Wf3jpb1n1JxkY+gicjNAkFVardsgfrYiEPm4Qk1qiDF\nJyHXuy+69z0mRJo9dKkCGe1WX2L2uJhogmj9pSDn6Oom+4/tESFTHjIIeq7u\nHEbcyi5T1UkI75VsQfPDr6n4RnjAAkLX7blGW8cm5RHZv68gkvEMjR34qgWm\nw/BkvCJUz0WmfgLeQKXtQbyXLR5xZy0zRKizR/SXB2y/IFTY09oeQ/CjZrqT\n5+fS\r\n=/k/7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDN2+4JSbH+9+2V1j+lH7RUh+u307yzSqb0F1rmd0DtxgIhANej2KE1mQhNs7dgW7wCDlC2BHspnXVGdTZRVrAcuuza"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181219023620_1545187161689_0.29802786956245875"},"_hasShrinkwrap":false},"1.2.0-master.20181219083048":{"name":"@atomist/sdm","version":"1.2.0-master.20181219083048","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bead65fd38eeeb2ebfba523297c792c7c1260ff6","_id":"@atomist/sdm@1.2.0-master.20181219083048","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nnIX1W7Wm+fP06ruIy1yj/HfTuMk941qfPAhzviOsoyW3mSRaWjXrZ+4FCRTIKp46hnKVx1LhaF9togQf20alw==","shasum":"e06c93169b3dd0f040eecbb8b716aa99d8380292","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181219083048.tgz","fileCount":712,"unpackedSize":5389712,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGgJyCRA9TVsSAnZWagAARRsQAJPUtxxrjz7K4NL1pRBH\ndErtPcbgAQRuAjpRhYlsS8OHWdRQ/OrcZ11Xx53DO2FYvMFihWKC2gqptrqK\nGUSrQyX3yi3OloF64nffbWhNxEfgJR0O5vU0I5nyz7XZ1sHIc1PMgA81oojw\niGpaK5bT+bS9z9oBBhm6Yut0U8cv8ZyDUA3QOLMEUSLP7JQmGc64Jk7CCTdM\n/7jZuJjKix3qsdTrD+6xCOX5mFpsRH34atw/M2NltCFzuDiDGks/Mt2T2u7u\nauJrG/bb4NH2B0GU9QIjw3ZFXTO3lVnVaKHzQVCgEFSq65zZPSMLxn/tfWtb\nKBsmN4ZhywLRxOxvVuL8gmjt+Na1FPPDolR7M9mDboftKguYyko/5Zrm4Wqn\nyi9I6lds+Aj/ST7UXP4smkh4A5M/+xcnuKshUvy0jqEFHZgHD0pFssBEwkMO\naCOfqaN9ZyIAP3wbzyN6K5IIDBpbYAgwgMW15NG4fTrVI02MaY4jP57kdwSg\nlScCsu4EdBiOGZMFP/aPK+GhS0zQujGffHfe/KbYV8C65tlIdYGhGqDZgip8\nLTVvHIHUkG8MzuFs3rCudapVhZA843oQLcbCaNsVqAjQ3HDikLfS++WWnPsC\nZZtscDinVlwR2Bw6ULEG2k7xB9gRr3uoUFGjavVj6EwB8fEHwlxNTaH9s3x8\nJAfj\r\n=ygaU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCtQaEmM4tU2rxLZIkR/EEnJQODHcTK4Dr9Wp3GVO/8owIgKwscO/Up+bEFedjQlTmu6Gia3T2AIp1ofBvXWG3s+5k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181219083048_1545208433460_0.8411273703199726"},"_hasShrinkwrap":false},"1.2.0-master.20181219091010":{"name":"@atomist/sdm","version":"1.2.0-master.20181219091010","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.1.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2c9adf49c9e405785a49f604a73e4ab1d12d6225","_id":"@atomist/sdm@1.2.0-master.20181219091010","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-AhcgAt2/AEy+QuI/6HLtKzC021FlO9Vfrb8d/tx5fqcrqSfCUw6i3EniLSsYaxC13A0h5umWX/bO/eIB6S/B8A==","shasum":"60f3d6970e9a90354acf6686dfb555cbb6ff7716","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181219091010.tgz","fileCount":712,"unpackedSize":5391569,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcGgulCRA9TVsSAnZWagAA6QIQAJWm2StL/9dclN0ym1NO\nQlgFOO/7W8HCVczb3qNif0EjhijDWkf1X5aqemwzIXVi1+RYSRAvrY1beC+Q\nEKUkWQwSPtYkKFSMm8DOuY9czY2jCXKOmBlPWN6RMjSK3Qykofv+bdZ4745Q\n2vs4dj+9I/KZL7RylLgGiQVAu8SoQqkAaulQFlv0bgXLgeeYDO2wCEvmKWlV\nWo9R/zN9STbnTUKz5c6Mgk4vz2lSEGNRVn0NFqwgFDp7MF/sWw8cb099mATC\n2l8eQD0WyH1XQMdAhHQVfRGxgFdj7Dgs/BtbTeIDpeoKmxis5UAFmj8SmZ94\nLSEbloUEKPIAlW0ffvShi3Lt85ipgHG6DCrUIhvslgo8pyIkujxM3d87NgZ/\np8L+T95XYtQ4AzNYebyMbVrC4snw3tFfpHlchsvup1ZTGJHPCmTriDFdMw8x\nw8BqDxHJqOMtTfJZYon9SkqxfOZyDliGDrPa4aidEkJeDyCrfmOKMZBCifpM\nMq9Vq5lPs/RklC3wE7rMOV9AE71DN1FCielpMbKUm4oe0XCq1h2t/mglOV78\neJeZ/DJ62HJQogOkuy9RiIWsq2M5Ja9fXpY8RDv0rB1acPpCy1Ydy94KDNSN\nkkdFF6YahTHIaPUeM8MbjHMffRAV1sl/MObBk79Nn59SvetLzwmBgCYzVLo6\nqrEf\r\n=UonP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCG3SNPuweMKsrEeez3J1MV40hMxyDBIp4GCzhoZUrS1AIhAL0hk0NwnsEPGxfV04G4nOarj/m8vB0geEelbBKZ7y5n"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181219091010_1545210788394_0.9602278165000169"},"_hasShrinkwrap":false},"1.2.0-targets-branch.20181220113940":{"name":"@atomist/sdm","version":"1.2.0-targets-branch.20181220113940","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.1.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-generate-repo-id.20181220113107","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7a18f63a84afb2d11e372962e63b5de6502ca026","_id":"@atomist/sdm@1.2.0-targets-branch.20181220113940","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fEalnQo+bURtsarIlSnO0opmnSmJW6s7vuKTSPUnyK8ZFpPOcebOcrCNkd3LpLM0kwCdqopl0FbF8bC0F7lnbw==","shasum":"85602713c179abac26bdf0ba667ebb70a118ca77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-targets-branch.20181220113940.tgz","fileCount":712,"unpackedSize":5392279,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcG4A9CRA9TVsSAnZWagAAGpEP/044M9s5JjeVpE7QVXUV\n5V+W1SdvQx4MHR7CFZh/vmBcQjpE40q5m6yzpIH++MoiPPBRoBpn12zR1aPQ\nLFs5Yd0UBZJfHfDa0MpZWrd8Z4IVo0CJwkKwf/WhYAmiLQX4ahAMb7N8rmRk\n5+FdovBRRY+/7+Y3CWufbkkDWQlOvharM1Ycgt1FfR9Fu2cYHqB1GawBrSUb\nyF31je33aoMNTo2BM1GCi+fyCJ6P9/UlkF5Tg9PTL10WLf9byx1fma3bchS/\nyuZQEl3/XEioxHLInNGHXXRkQir86CnZwfZtNzn0yXOaZK3vux+ENHWXmCm9\nsf93+ClXCO6Uyh6Sf+X81q2HJNSqlcbuLwK+z8Hqlp0h+4BnfFjppZvQhqKp\nfG+URmpKi6a17p9ma3RzRjqa8xYOQZvn7yTaH0behme78+1FFhKIhYGI62aH\nB2CNEz6MIyiesVJ+1TlO4zaOZemR1mVMsWLaYjk+w/C6xIdEkHpePaF8EwLT\ncfeOuo2ytqCmly01u2D77yml/tLh1Nv4e08uqUmpDN/MbYUVwCnHfMOq6bZt\nyIrfYu0uuc3eOkdMzb6mDm41IOXe9jQvDdgI341CdzDdWsfVQm8XBqn+vJd2\n5yXfzeI+YUeQRUieQ7wMWn5iPVOjUzXVKGy2zEcDRPzbXzzFvUGOdnkQPtcm\nREeB\r\n=XipI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFgNkE0LVS6XXj6da6aYQliUXSFEHc5ID6VrRkR8xHCnAiB3ZfcES7WaM2y2YosgSWL9dEFBYatAkr259zPPMUqvyA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-targets-branch.20181220113940_1545306173318_0.678313099597345"},"_hasShrinkwrap":false},"1.2.0-targets-branch.20181220172547":{"name":"@atomist/sdm","version":"1.2.0-targets-branch.20181220172547","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181220162531","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e0aea80c432c369bcaf6cff4b23d13316b4b9bba","_id":"@atomist/sdm@1.2.0-targets-branch.20181220172547","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-aloY08Yu5Au2QckoZh0VbTPUqvU9hg5c+QLrWht9FqnhdknR690aqkKmh6pf6FOc15x6zsgE1AAfdyhSpo15qg==","shasum":"ee49aa05355388343fec90f13eae442b481c2b61","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-targets-branch.20181220172547.tgz","fileCount":712,"unpackedSize":5392269,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcG9FKCRA9TVsSAnZWagAAbpwP/3TvuaWrI3Q6jaI3QcZZ\nPnkxgeHZ8Ubq8fxAMhajXgFtthtnNxSCZ8cnkM20ZnP2KyqcmCzmhI54VlKE\ntrQzBVBJVNojON4fNZGzcrLj4OmByf3FlqfAnntxL6vK+bB70VU5jScooh2v\nNkprnfFdvPvt3K9OvgEczI68ggkuVQc4aBKBpEgoZQCXE04vPmLM+nAbUm5c\nVuimW3r75i+XRc5N2btzSMyvmatTQwCB6dNs606YoBDxGOkAeRBjxfPenAe5\nMvY9BAj4KRlBV/xsXUx/wcvcY60safjj9eV8r7+zWCkV2GwjS2oTO6yXKaWh\nfXKSDh0IJ1CDwtfAynC/dcL1a/v7AHF54cbKOkjUuCxiW/+S7kEc7aFaKx20\ndF9RsrkKZ+H2aEdEM6BHrIKjrsPsbYklKITcPiNwxn+oycTbXY1I/3RlJxqh\nO/08uLdpFO4bupW2bctwd+j5TsTtnhOWVlykoVZ4d/drhbkMoaEm8cpDRnYk\nVtbrIwyqvr7FRnKw4RSUdvGGnDpK7y48y+NmgrBg1FlcnF9oqSTL2+HCg9n4\nGSAk6tDAdlyC16Q/xBwb409Ptz0cTU0gIL1B7s4kLYEhcXcmcRUIdylDP5Dx\na/vtpQ+1ngNehvRwwIBGR1Z2OkMNJWKUG/H4JRtZ3OamOYEosQPpgvw5GMyI\nijNI\r\n=YT3H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGs1PpfmN2HN9y6/PiHwSLrsSIBsRPgQBhkQJREednr8AiBwviz4lfHyhecJYnghVjfexGCqMBmoV53IqV5cbMEWAw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-targets-branch.20181220172547_1545326921827_0.5649286046268376"},"_hasShrinkwrap":false},"1.2.0-master.20181220184732":{"name":"@atomist/sdm","version":"1.2.0-master.20181220184732","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181220162531","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3762a20ffe86130f45acc0b95513bfcc3f2c6a6f","_id":"@atomist/sdm@1.2.0-master.20181220184732","_npmVersion":"6.4.1","_nodeVersion":"11.3.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tv6A3HweTEiZZ9AnsRUjLuL1hgf/cXPBy70Lq0B8ur29Opx9G+qZ4Md9CIlBH+MKBa3c6ejKvPnRy0WiHeg0uw==","shasum":"25db5f2ea170e0044986cfdfa1fec80f2b62c003","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181220184732.tgz","fileCount":712,"unpackedSize":5392481,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcG+SSCRA9TVsSAnZWagAAq+kQAI0E0VxikO90ILKUrgxM\nxQwi1jUUAdj837Ov0zLK5OAjKMGS9K70cSVbkKiOnUTjwgIljPyiceLTbrT3\n0hosQmM4+kbEqbXKB84LEgFUsO2NdNksn3tjDcrHQ/cMCIwyWGyk1He+PtLT\nOpgn3/FzXKhU5xmZc6gZoOxQ6SDha/gPk8qn6pInGawGItJ6nFe8RjKzsDee\nqugJ9taBKWkuy0Mor8Cxhh+tCVFR7IiRIASoq6e6UWe/SDXZS2geYSkR/pu/\n+1h7lsvZOkGOL8J2wNWXCHceceWNLL2vGdtO2Bi/zQMlmcSqcGHtDy53l8eJ\n1UnD9OwOCw/R9qNhtJlrJ01up3HfIs+KyOotQLcb9ZBGOoFE+m1UraBPAMt/\ntO2y3CKBGEWGoWDwMgfDgQWu4PkN91wXNTMhDkQ02lM1+5EIDUzVRyKSN6vz\nit5+RyYgxhb8lONwS6VWXfq9DCUfy8Xp6qItMn+WovIEA7An/QW6FFRoJ1J3\nL8jnjkAzDTDVlYrGsnSrqm4AoJXxKyu4bxgDmSlFRk3HW+ewIwRFOXdmT5U+\nCGFjKVDeq1682sHzAG5GzGoZDPUWVUQ/gy1/jNQOLgKdMaxsGp2Bm5gFJxD+\nXKbCVuLYNyrtim+u/RE0kZUSOn03PkWsh74dNdv6R1EJ2aGMj14pabdGpXDc\nFnNm\r\n=9gXI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzc6HaxfOtE461X2WfjuXL4dQxZnMYEmfGL3sHPNH0YAIhAL5/ckNQ4c8y4tvWMavPONBSFNOoIMos1tdJIXfNUVY/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181220184732_1545331857882_0.9818430591652942"},"_hasShrinkwrap":false},"1.2.0-code-transform-branch.20181221193253":{"name":"@atomist/sdm","version":"1.2.0-code-transform-branch.20181221193253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181220162531","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"47946815c363358df8087fde38506e656e259df5","_id":"@atomist/sdm@1.2.0-code-transform-branch.20181221193253","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XuVwI2RT52+su8Biq5lnEq7uH5+2x8ScvFHYW1ahaq9HldSfJQfJHEZWhsWSxd3m6IgcNwO5XQrkSfYQP1+A5w==","shasum":"da2ecd7c55201be334907abb3dbf48950eecb38c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-code-transform-branch.20181221193253.tgz","fileCount":716,"unpackedSize":5401745,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcHUCQCRA9TVsSAnZWagAAHMUP/AviCjg0o3i0r8x256xe\nXeP/EK6/Z7kJhupOZ+VfL8Cc9Y4+2zpwXaHbnaPsIYsNH5QZKaVfB912bXWY\nAT9Ak43vnGXxls3PNkIXYEx+Kbmhr7F/cX6UZieRXd8499K/NgiMFurl26zD\nxR4d/npnNDJInbdVXMTLcm9aFjv8RgjiSfFfLumgx9x6+6wG6Kzq4ahrF6EY\nRRkjeMvHQ3uV+kl4MerGxXrfMLgLX08gKH5tvbSnVrE+G5FGmpgJ8cuvLKm0\nAlp9LrgE7UWC9qESrocsTUkEOM8OBTSUtuVOpbe6UroAJ9fKxi5TNE5p4nfS\nUWzEuc4xfCUZtPGaHlpwAVzmkcJyCLbyozOxbEL8HaK1dQY/rANNfB3qSR6M\naZ8dxSYrRfFFSRBcfxgnqtGIOyWZkQ9hwElrT5ySu/Qd5vThT/lXx/xNBkGo\nVejOMcdQ4AKDPH4oaWgXZ44lmlNmJbWRCBpZmzQQ7ucC27/zkC0/wIwciHT/\ne5g1Q8lsP9ruIbopZmmoKNmwdXWOyi2fxvotc+hTNgou4Sf3vMqHlKZFhvOp\nec7lB1g3Fk1U2nvG7K/HYzWImEx5fvHW1w5BpZUdBefCsLEQHVfiDq1INp0P\n4TPgW2rgFtOEn1SMClZF7PVU4lWwZgQbLBzlAL9APXs1T3HoCMz5B0B5cSb9\nQdLX\r\n=elCD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICY3f/oD6Zory1KxprurhLxo66Uh8CmpUjZpS6jiWnOUAiEAi+HCUAgZtfJqZ+Pt7Ce2dguKQcECN0pvcoOv7kl3oZs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-code-transform-branch.20181221193253_1545420944097_0.6856887781517735"},"_hasShrinkwrap":false},"1.2.0-master.20181222155954":{"name":"@atomist/sdm","version":"1.2.0-master.20181222155954","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"90a3dc26276419b12179140d87d6abeeb3c8d1ae","_id":"@atomist/sdm@1.2.0-master.20181222155954","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7ZETFaz20vMz9AgQN7RQgYeUIctdGUhimMg/A75x2bQJHLWsmE3yNgIrqKqdIuFatualRLTLxeReIGDFtVYNow==","shasum":"f6bef169644e3f526b864b2098ff238123693299","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181222155954.tgz","fileCount":715,"unpackedSize":5486714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcHmAzCRA9TVsSAnZWagAAtlYP/3kQwwS1vKqAJmnDIJRk\nKRJgPyiULAnDnCoUvTAEUlwGEd8zGc7pGwgiD9bfKje7P/0tlRHc81/ConIA\n/60UFu/lJqhxt+mRIZP7B7gmro9wKNbg2S2JP2xneC0DaotYO8/Zbhf/Nf2p\nSufzcXJvXfnuKtSEQ83PHsx9d5DVZEvYmMD0hfmqiNpm1+WEQ5ZHabmKoXbn\nbmHXOgTX6DtxSvf/7Erye+N1L+/+IQzBDk6PnLH6TnFq1uKDqnpK+6XnQ1zY\nVUIWX6Nk9q+Vibd+uQx23fj5uDzjKIue32fGeIxY+yU7aNiqniWwfTWCa0Oe\n1xHs7hpujn6sT6xyEtSIeHv9cbQL7Tloct9/skXsCJ7GoTIg+T4sfJial37r\nCbad2QSb7AcfiCMOoIq3mEwgDwJAf5I4+W1bizKIb2i8rGHuQx76jvjBhuTv\nBnWf4WmiFWNnug4X5KF1qv/hoHFPhjPVFjWKjljYq8zhMAni3pgCBEP9K9Fj\nwLkdt2PGl4rYycHEublBZESLXYK4+KHKJ/FzyGfEMVoapt7ifLwV/fapec8x\nOltDowr5Lx/DUYew7Q2EvqW7/x8U/JaS2M3K3IgunUzMnzvDluwO1FaNYzCo\n7ogpOfKYAfnnKSpNs7SoHX0l71UUQ1W0UPk1tjS2LaEjRgDp0MouIPwPe6V2\nsBqW\r\n=GKMK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDyB4HEmJ5i5HOKA9TrgLo1EL1+tFxzsozJnkAwnL1THAIhAP1+WGrbJGmq/br1rlcH+CmeCRjXJe+p3AwvoQsImQId"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181222155954_1545494578785_0.8550253161201122"},"_hasShrinkwrap":false},"1.2.0-master.20181222160121":{"name":"@atomist/sdm","version":"1.2.0-master.20181222160121","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"90a3dc26276419b12179140d87d6abeeb3c8d1ae","_id":"@atomist/sdm@1.2.0-master.20181222160121","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s8cXAuyheVVn/ERr5LQBezou6q6mdI+xqsMcW/zVYNJeZF+n23uggZfL3B1tYtdojqDa8ScxQLqGY9Sw45ySsg==","shasum":"6a2e4cf1fd6e9b9a95d4085eae71ee584dc0978c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181222160121.tgz","fileCount":715,"unpackedSize":5486714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcHmB9CRA9TVsSAnZWagAAqVIP/0ZRsx3Iroz2u+9upVhq\n/6HAlc1gRgnXHVTqNDqnuvdeOHLB9qynD85TrNpD5w8aImT6ARYggBlj0h6A\nmLjy2kgSdGzsu39GlBl/QWBAB3/ilF6Rbp8mdCoTSiRPbj1zqMWyiKpCmsWn\n06JPhA4dnqFyqy8ZnGVy9ytDjBwrVIveTEGu/3RE0C3RmbUIez+CAOBggxZ0\n9kqkmiudvUsOpyk/uXElx1H05cnvFRvPOTrr7k8ZxDo8BheQO8wO+dnrYNe8\nMQWmE+w4TWh48WEGTmuhyI5CbQhgFhsxRJdzYS5YFj2HOM4XkU5oc+Gfplpw\neRb6Kqv0tcRwYVwbNusTwrOnG/BNXLKKUtvMVDTMK2j5lIX2nV1wS1iVwjTj\n4jfer2zy/yU72HKJk8WaX+dLyil2XPXTg4QSFd3N/oW7Fc1aMMFsRlq9t9M4\nOV6mowjgeysUL4z74KWIoRg80b0i4LOIZtp/Kb6WiORPETkBULZPggXUokBR\n4HH/VX8SDp6jaIrszZ4G+hFE65OvoDcwA4Laz6LRDxGN0V+Z2/kEyW0g/m3I\ne//ksgAERfzCUSF7JEwHELu3xUCvdzvJxOEYRec6cIs67MyW0KWsSXjyyDu9\nie6DwdpvRDqXSC+Jtd+H6BiTws6j2h4TUgyX36sb5Mc/M+UmXpsWbUi2ob6g\nCyWQ\r\n=jSUE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBc799XoP30o0cImJOrkcZb5a3jqbgoMA9FvajvHbz2bAiEArG20PD9GKbxzqG5b8X1FTtoYNEY0FE83VJBDcnFWO8I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181222160121_1545494652641_0.1490066715353222"},"_hasShrinkwrap":false},"1.2.0-nortissej.doc.20181222191227":{"name":"@atomist/sdm","version":"1.2.0-nortissej.doc.20181222191227","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"66ccb02cfc04b4c9a3ad71b4a63b04f5a184e659","_id":"@atomist/sdm@1.2.0-nortissej.doc.20181222191227","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-j51ue5s9tmU+hSv/m2ojPQ+kf7zxU7OmJIuzLlTe7JCkE0IrsKNj5LIgDp7YIsNmJGTNbdLACQ/+/O6B0Wmtlw==","shasum":"fc490a5368646c3aa8e2b3c8e99cf4a265360153","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-nortissej.doc.20181222191227.tgz","fileCount":715,"unpackedSize":5486757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcHo1NCRA9TVsSAnZWagAAXbgP/RhMMBsB4Y2aA+RPnZ/X\ng1PtQJFY2J1/VHViPxHX9eQ6HXTyWyy9wkb36Vn3bewsdfezsJUuhuDFpvMm\nSeb46zHV19OjMFlQvkXv/fvr2NwG2aYAT7KMqEoRGXK470gtXnDL8cLs5yTx\nI/hSX6jvIP0UP+X7DB0DW7gpBkvtSv1Oj9nJBDGpHr3ch080UJWJy+OUlXN7\nyVosRTTLV1MMrhcZeHw2F7ZbQPQ4mif25zLFkfvKrN4Q2/Y4YPz1JMtsqtPw\nqPHcN+aDUOj+kmd6l7MIDWKU3yNdNZukGM5722iT/MSSOnkTZ+iGloNlb3x0\nAsD9lN6MV0T9JeT+FhPm73YcN4RJC3wL1rm5gklY8NJMRU7Pi8GOgInJ5l2L\nX8lbdFyfuMxCRKrbIUiCb/7tMfvZ5txnbM0L8YPo0T2VDvtN50TEvIZ6Kr1Y\n1BGC34eNy5+krlMj+WBL8ZP3b2bYLH/pM6Sg51zGTWNkeOr+QHElMFpzBiAd\nMvK3nhogHAdg5gQQgE7EpV1VbkBrjaRdAjIUYKviHNINSaeFNdECDI/smFOr\nRtf5An+VfXogWplX7IOmQGJ0UJ5XcBtiMb6U7zfBPGvD9SeErpY29KNFH/oA\nOa8o7rKSVUzSLU3jUC2ejOamivpwOB+dL02twLvQnRkruXQZd1UYC4CPoq5E\nZmzZ\r\n=T/h7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbER+LJTI9r2F+o3pNWbGPQ1BGFutxwN8KfzNoBR8/wgIhAP0oB/bUvEco47Yx5Le/ZfOWaAKr1cPGoIvFbGyxVSY7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-nortissej.doc.20181222191227_1545506124836_0.7439480863151999"},"_hasShrinkwrap":false},"1.2.0-master.20181223112642":{"name":"@atomist/sdm","version":"1.2.0-master.20181223112642","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"756578ad64e8bdf1544dd95a5aa045a2e06a6405","_id":"@atomist/sdm@1.2.0-master.20181223112642","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wif8ACs9WWx+bNz5MHSi9OyDHmTvtkYo5cEYTlleMdSmbLUQ+XfTN0KmTn3CnUlpLjqdwYlI0U6IJgOAl9nDFA==","shasum":"6e6653b71e72abe4e788892e279ad951dee8422e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181223112642.tgz","fileCount":715,"unpackedSize":5487250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcH3G2CRA9TVsSAnZWagAA38EQAJWRISOSUJkY57WRPqtF\n1kBFXRquVaYdb5JDoIAIOEDfNIZE1SHYzBwRC/Ifz3LddxNWMoYKrJmvKnXp\ncxrTp2rLh7g9CB3xrMeifwtxClj1vZaDgqM35PWPa0mStvlS4r+Vo3KJRpuq\njWJjZ0KFpZ61s0Y0a48xT92ZeqxGAtDjdiwwc70yylcXqvqsdTSkp+zcafLy\n1n/b156Uiwl6EMEDxLJVba2L81DgIeUomWTdpeSW8BSmjumkp9sBT6ORQI1R\nYtV+dVU/SCWUbIDBD/5MalKiwT5cYPUIeRaXzVo2jd5e/buCXVNvDNUuisYb\nHvKvxSwgD7MQzi/E47dLex7oWBLe4hbP0Vkvfn2+iZLi+QZsRteSRuV7+QIa\nZ8zR4K4WCPUzNaJxDlXiyWJq1goELypGl59fPKjJ/1dkQvP46kPa5hGeDoso\nHaef8q7wBs+gxDTlrICmLPJkDmjvQuyJOjm0B3b1VpC5VLhzSf4d5zYKpMIg\n4+0jttgbCdG8HTS8JdF/VUuy7GCV/VWfTwc2sv3Zfkhe3Q+9LddjWyFKYRNy\nKeN8QZWqucxBXR66uewAcFUc3ayV7ENoGltPqRjDMhUz7RMKknHo22+5o1ez\nLtOyS+jDOevcTJ9nS6K6VUxXRLQyI1zsJHdeIEA1/FXgDMmCf8wP0VLcn7Ap\nhtbm\r\n=oHHC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCWCXFwQ9qy6Ud4qBO5uScVNfupgyk0c+d+QAYGwcvm7wIgO7hpW9UjUGqnnJ4RHMMInWje4siRoRHtEZ+UG9kalDc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181223112642_1545564597929_0.7635311674800671"},"_hasShrinkwrap":false},"1.2.0-setGoalsWhen.20181224014656":{"name":"@atomist/sdm","version":"1.2.0-setGoalsWhen.20181224014656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"84770263c4e27632180a736266add35782d646f3","_id":"@atomist/sdm@1.2.0-setGoalsWhen.20181224014656","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oCcoG4PeJk0fKMDoXr3qdHeeDE4xhiUKe9Mv6FoTDe7seNZ/tDgoYAPI2sdabjtTO9zml1N8L1tHF78MbKks7A==","shasum":"9419f92caf81d52bf9f281f09d2cc33172e3e86b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-setGoalsWhen.20181224014656.tgz","fileCount":715,"unpackedSize":5489152,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcIDs+CRA9TVsSAnZWagAAvwoQAJ/teZCpqpKCbUGc5uXc\nZ4zHIaBI08IeoMsEipKbBRnvCkfhDa6GqdizBPAv6MQJFhz70Osz1m3e5luy\naQcIv8Y6gO4v2T1M4z6WeIYJE6IxZDSvDapnV42djPT/VyO1GNxJIl5zuF75\nrhvMC1Z1ICOFMTLSsuUXuPzHKUUBauckY/OMN5HFlcxbT6EXOyBedYtabxoG\nYk6SnPdTKroUlNlVkjcFX5nfio1Be0f3iRyNGnoH/xPqwYgbWGn8np8B0nIr\nY3LwU+pymyctkOK1QcBbDRSe0MwlAux2ii3xf+tSrYgPUNWNWAWj0zcO1883\nOqA+2VBGEcjVNxLVvqFV6hX66uFMB8SUZMvqh0O3QOGG0en7O23ZXp+NdE32\ncH6igkH8UmXMYTDsqMTgWx8JCDdCf45l1DzvllLK4DUKW604YiWT8vx5wxw6\nigveunldVLToqTWKJ4mBS2xAwNN7EoqoG1IqCVqBc1ED53tVgjjUfkx1RzUb\nLK3JxWBgFw+P0L2L4xbiD7/5nCLxyUvntzwOWN+yKWqtfhWbQVHzko7qIs8U\nGPLHwlPxLlz/DUBViJENsOVpGXmxY0WV6zFexO6YrM1ix61xxEvfMKAL9/rs\nPxdHmCWuTwZqBQ7Bow6Oc9YSvTgvr8b4LgWUnvNaXZWm4ANoA1N1wwpFX599\n672p\r\n=+BnH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICeALfmshMLZxzhdYZ/yVNi2LfRikiZqpml9ars2r5pXAiEA3zup5ZfcxcHyFTbkdNoJPZhGx3vnNSh/3R283v4xJ/s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-setGoalsWhen.20181224014656_1545616189205_0.9162312886808146"},"_hasShrinkwrap":false},"1.2.0-setGoalsWhen.20181224050605":{"name":"@atomist/sdm","version":"1.2.0-setGoalsWhen.20181224050605","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"adbb410dbc23aa5ae750b30d9875d48983d8b077","_id":"@atomist/sdm@1.2.0-setGoalsWhen.20181224050605","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-d+lBApSlWLkvF346pe24tlIuWge+CU6DuWPykTExKgLEM6VUUeyEIGXp42o1VD4owhuursPgB4jdAsofiyTvhA==","shasum":"abc2ed1c0b8d37a2a53d948ffd62384271a471e2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-setGoalsWhen.20181224050605.tgz","fileCount":715,"unpackedSize":5489211,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcIGnrCRA9TVsSAnZWagAAEvkP/3EwuPaMPkFIuEYfP/i2\nlNX5+TR6YHauc/OPx/6ufcVPloqUaX8kXw3y9EHFVgcAyhX5FdNqz86SlfHV\nDHExiTsXPFv/Ilx4unGyxx1x6xD1I151x69nrTAOhS617uf8g7SF9R0S2P4k\n63wMA5YrpYfLsV3BZ1Qn3vgcRVw8wiMwhKf0/uFH4IE9El0ESfea3GvRMSG3\nPfaVCfxcUaQmxP/bh0w5szg+jiqZ/1fZE/Jn0SgAxUkrRJTZxwYQOb7vgFOv\nU3CLzm2MZkBga/Nti0KfHWyGN2wNzkPp/Ipy48IPBeyH65sYh+q1Khyhu1uh\n4cg3DG3YGJa7i0S+eihlebKdnMhuux/v2cbr4uSEdVm6NdSnYq2rTOtjw6te\noOxMBB7T+YlakZ0gDgT/L2eQqvdJBrIERs7UmKBIm0gDSodxqLi7uuLZJNHs\nh4pH7JFL09M1Nma+LYkGlL+7GWsbD2nxnrja1mDApZiU1clLo7TD7lmQnJ6D\nH7heCLTx4ccftTs/JpigwZAnQukhw6NH8RK3CXGJQv2Fa6sVWLP3Mwrppw4+\nsD/vsVaHGiwgV65DQYhzxUKp+3z0DBqUwL0W2cwVCV9/lQYVbDgYopyPb0QB\nIjOWXGSp4+2vQGQzgDFXCSD9NrvZ3HlKS8LWpxUunnfBNRRQOSZyKe2qQIYg\nwBya\r\n=69f8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDcX6Yuy7szy0tJvDBght+g9IiG+bIJjPI+gtdMAjThHgIhAPBZ29l+zovMIHFeN+vdASgUGh7jBKWFpuBcXfO/o/LB"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-setGoalsWhen.20181224050605_1545628138778_0.22848132023088397"},"_hasShrinkwrap":false},"1.2.0-setGoalsWhen.20181224052930":{"name":"@atomist/sdm","version":"1.2.0-setGoalsWhen.20181224052930","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"81bf435050b81c90dab8290a89dc93e116c38afe","_id":"@atomist/sdm@1.2.0-setGoalsWhen.20181224052930","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dA9NK3TKPgMJSbsbC8LeQrhX5/RuSz29l7Tlxqg/hxKkHdvBdsI8VCXB6klDT2OAZyFhr6/pH6JZ2qoktHAMFQ==","shasum":"96f4402cb452631a5a6cce1bcc10a49cd39ce171","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-setGoalsWhen.20181224052930.tgz","fileCount":715,"unpackedSize":5489214,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcIG9oCRA9TVsSAnZWagAAsRcP/At659xWRcXHpxIv8gAd\njGF6W65/Hx+eL8B7TWuml3B/cvLw4xHZLsab5rlhHePbI+zIjhT3bj1K1Avj\nSUwNfB7TE0Z6qa/y5XBmS+Zgs1XKn809ITYUZAIIigi839X1d+rg2HtwP6h8\ndVAdQhXeQUZuklC772BjC6mi/xPxy4hdZi3WbqJYff8myEARH9r8PnPut6Q4\n7nvw3ldBji5QUSEswFK/q9XgYqIKOzjdaLATg9QaCNThDtSq0ebiA/23cEPz\nH0foQU43fqK1cNSpqTLyY3SmCMvL+q97hH/PwLiYiKV8my8GcjZmafW4+gyN\nSE6QIWCSrNK1/qKde/99lyJ12axyyJnQAAQ5Iu/h/KDmld82MyoVhLFQG/JF\nPlQBZPNHIrdDPF9+/IcQEohbinsUV7vSnWX+/ay3XFDSwVmg9bOvyfwT5BSZ\n91ahSgCSjt0rk782CdibDt+CmA0PLgV6kaXm+kinlpmVWEgvqIvkYV88NMGw\ne/Wyyof3vR1EVfsbX21DJrZ9ySKhO+jI6iIIpdig/8P4VOMIxjxsqU4V2hnX\n+6i/1pLkKlTJiUWflTCBWpkm0x3xCRjdMvQ12+1pIDsm6nCRonXBKNs8L4LS\n0kxxX1nTjAS0Sv5F7/o0/yaDBSPZnCMKT4ql3IfPdR6hi5X7g/8nv2eeLCsb\nyijK\r\n=Tv6r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDcM0GNPS4Z5KZOFeYjoQsXNgKPwrclbOFfQ+ADj3wtIAiEA31K/CMwwwJR8Fql34NDM7gLKgxuAW3mkx7F7JTvMqz4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-setGoalsWhen.20181224052930_1545629543947_0.8993723570303918"},"_hasShrinkwrap":false},"1.2.0-master.20181224082627":{"name":"@atomist/sdm","version":"1.2.0-master.20181224082627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1bb597e19d2f95d14b375eb85b2b8d33685fbe5f","_id":"@atomist/sdm@1.2.0-master.20181224082627","_npmVersion":"6.4.1","_nodeVersion":"11.5.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a0dmDi8AY0KMG2v/jxK6bv3Pi7qOhk8Pbp9sav/grCqVnN2i74uQyY76Igpettx7goR1dtO6LAMkP3X3HJ88xg==","shasum":"30e9d8a754939c7de775392ce68b6d4112a7caff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181224082627.tgz","fileCount":715,"unpackedSize":5489311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcIJjmCRA9TVsSAnZWagAAQJEP/1RvwEQCZ/NB0RjveRKM\nnsZ6w2jfsR3OkmFqNI8slSF/IvpUSRZWVRy70ZIom6ZHLDZTem0qlSG1RmOc\nCiiWb/S8HOPWwELrZLhCkaouEgctJwGO8DGj6MjLpq+HcsDFLrfTo8VoSTF2\n2Z3aODIJGU5su1kWYCZNtfHHN1H2Kc+zimXzMrzEQpI0DYK2ImjxTWP6gm3a\nkwy9KO6kAMflFEBLSW8UAJ6s5PdId73/VEwHgTp7rohOMai4JAM5wFFZMo9Y\n6ImrW7jUQoleBR2O3M5YzCNIIkuZ55bhCzDAP7WMhZMKr5XJsSWc3ZvzKaxM\nHQtAQNmwpT7ggOOXttTjkxbTV0jMIioNzDG0g48NgbI/5MwD2QAxPoax/dh9\nj++/AEGCV7Sp17gInrGG4p2U9HBVEOnKxxuiK1AwUspMBGju23mkXszlQk5t\ndU9pkSLLKOVTVyr2Vw/RFZ5YmjAbAFXsX2WV8xGEk93SBr+EjFko+ADKVgnl\nLQJTsfvPvWTZabNN4Hn+6atDhdJULBr/yiCt11jFWnANmAM/bIPW5PbG+81I\nH16L7bb/U8Fy63wG0S4C4mLOxSeM/IE4c+KuHKP8AX8n4SjhXHSW49RHb5N0\nH9iL2zpFK8Ukvmgp13OLutwcDezJrhrsxYBsE36nAI5P2UTD1U+fZ1rXu+Po\nTKgh\r\n=7k67\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD1eNYXh06atKXb/39Uz3nOlxX6nRSlnZUxyNZalHch6AIhAO3MRlcAcVZhGAq2UOPFP580bz6rmZKe0OhWCBkhHjHC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181224082627_1545640165443_0.40748897076459034"},"_hasShrinkwrap":false},"1.2.0-master.20181226045517":{"name":"@atomist/sdm","version":"1.2.0-master.20181226045517","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ee490db9948ee70a2207ccd5b1486411dc3008a9","_id":"@atomist/sdm@1.2.0-master.20181226045517","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kA4iyFDMesg0VPuQyffSzuwlOjZux4xvfd21DfW4pe/oxCb8C9n0twL0nPjFITVFnKFZk+CdikokXf/XzWPrLQ==","shasum":"b0ff09661f59f1afb0a04d751b2aedcc0b08e24d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181226045517.tgz","fileCount":715,"unpackedSize":5489311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcIwpXCRA9TVsSAnZWagAA5LEP/37/fDSVBOcWllom2ZQ0\nxF87PcaAXwL0s2skuY/g64/0mvx2jEM6s/uwiIkaRpNAGacNJNW48TDbTQKb\n1NimnW3h5we3W0NgLDJVVlNXZIkQglbcH/TI6zx50e8m6B0ITluc0MrG8UZp\nwIle+p2DPKk9Gm8Sx3nYzSjhCg9BLQHKpTNT+tyUJe1DffLPCHbHOzVtrrhW\njj/+NDaSD9cd3bbJ/d0/35o4BLNYFyCu3oQCNanr/4u9jPoJH3kdAf7fM2HO\nbqn9b8KZtkWFqvShenJsYX/z8UkqN97AaXs09oyUPJTzQzH4UwhcfIRq/HD1\n2BXXSmyBXrUTHBgOFBXiLVMSFuaUQRXnrxos+7cv/FYXnUHSwFBTi4qeCWb0\n+3EktizLriXfElYt5ZtZRlq/q7NwBdas5vKtvbUNz8RSR1yV8dMcMsYNqE4s\nUiRFI2/I+P24b1q1DARBsHsYcITqiA4uDOI9Kw/K4a51qQm2s88oXHrGA72R\nSULmq2xW1/pnfFnTJXRrjkCdPyyf+p0zM49beDO7N1qrOgFJmlc2Vc1AZyxx\njWjoKdE9F9TpqiWXsORaXDYQnYA4qnrOttFW0Nm4gjLqJYJCKbSIXDcIDVGg\nmpMDd9WgEOofOFtDxtPfyigWqVCup010VRWeNjZ1sArlnNKJyRzF430IS8SJ\novfW\r\n=pfj3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHIDd8IuZvVGZ3gv+Ti5jTAwSKWNRAMPC9BL0UdlUTCVAiAUTGuQc2MbavzJ+6nw3Nl26AG7BAwgNF3SPgC4HZN/Ig=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181226045517_1545800277970_0.36132175497009356"},"_hasShrinkwrap":false},"1.2.0-master.20181227211347":{"name":"@atomist/sdm","version":"1.2.0-master.20181227211347","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^1.0.1","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"0.0.29","@types/lodash":"^4.14.118","@types/minimatch":"^3.0.3","@types/node":"^10.12.3","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.5.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.1","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181222154357","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.11.0","typedoc":"^0.13.0","typescript":"^3.1.6"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4f143a7fd317c3b3d671d9fcc1d1725a188a4d01","_id":"@atomist/sdm@1.2.0-master.20181227211347","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XW+Pjk2wzLfb4Ixo4kC8BgcNZ/tRflA+aOl7Hoe0P7LZ4ufszuyfwkI2epDQxsZkZPLjHg1zXssugCJh068b1A==","shasum":"d3d8c5d0aa8a9c294d041474ee75719eead79b02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181227211347.tgz","fileCount":715,"unpackedSize":5489440,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcJUFHCRA9TVsSAnZWagAAykAP/1QBqC6t6xWNsWTvm5ca\nCaTMFllhHGgRSuMbXK9LgJYk9k/f6Ayxi2iaDZecju0wMH5e1uzR4IDFmmhu\nKxSUFazdS/oDoeNtSbWNj4gq6/yyBOdCETmxLqsizGrr1zRRZ82be8LnvfAc\nDletkLaWpZqdCb4rnTEOHf9QBHDlzTIxZ/DZ+NfJk8brJxoBTlTPwtreRewX\n1sokmeY5bSrjXqStYgFHQ81TGr7ZM11uUPBiS9plxOADr5jcv1lboKNt8/g3\nij49zAVxCB35HX5y3o4AVkdf0VLfTaDehBKU1yFOTD+kYsC2dhVlIKnquGyX\nh6zeOTOvj84ZPAnnj/KOZqsM/25zwQD5GJPAGlNYSx7Lr7Q048hisMoop+1v\nM6nKuMQEKNevb5d3Vmk5NfvC++pIzvK7XZrRfaYNpO8DF4XU/zpWyjCBa9Zm\nJhySUnfEmV0klbS3PfOJKnK0sP/i+vsxV1IG0ZNQgx8b2cqUaXDI4xBS1COV\nNpkenadhwQfoeZ6AMFRIkrBfGOt1K8t/2ud+0sIHdcJk52MyRWtnS46s75jr\nY66cSMDxx/TBYZpGMWhgUwKjfV7WgSKE8tCXoC/KEofoj/uosI8vPe80XIVJ\ngf2YTN/m9LpEb84MNqRAGqnQmxrULGzi/Aa7FwukE/p+4G8PA3k27qwpjSlC\nPePn\r\n=8/xS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC71hMa1k/D+c9GTBWpmTu/U6qkI4nng5jpSKU7Xy/bNwIgE9/N5qNIsbbZ7hoqoJr1qrbWOZQsSv6rBHqvt8LwCaE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181227211347_1545945414499_0.8312904239964736"},"_hasShrinkwrap":false},"1.2.0-master.20181227213905":{"name":"@atomist/sdm","version":"1.2.0-master.20181227213905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.2.0-master.20181227212650","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3711a4e8299a28ec7ee8272cfb68a939da04baaa","_id":"@atomist/sdm@1.2.0-master.20181227213905","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pKuEY8R2FgL659/85Js99bBy6+nNIAqNm3Mbogyn1Oh3re+Bv0QiKRPVkYj1pryfiHY3c/xT20AlOQHHA9kvjQ==","shasum":"b9a3a07132a33a661929602a463397ea8a3a8d75","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181227213905.tgz","fileCount":715,"unpackedSize":5490390,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcJUcsCRA9TVsSAnZWagAAIYkP/3I8LEAM1z0OhG07CTId\nkA0PGLJPbtRYRaRJAXRa/Sq476UJb2vaTGj0MPt3bvoYmFvx89UuZPj3Xr+x\nSf7fEkehgnK03FXilsuEhUJCyHJ3wqmUpJBEwxfWkqNLg96ZTBiWsUe4lzr2\nA7cvO+stSAVLbOiYoo//LnrrShSX0JBCzbVXj6ZG83xfUCfmL3uhjG62khdl\nRLVPfTSOkaSPPmvEsChJyuWZgjwfJxZ9ljk97SSRXQeH8O1H5BBwJbT9Hlvo\n21tuH50J10O9oFqD6vP5MN6Jq6zq5r+ZJF8whDirMplK/JrxfCxI/tegZqao\nY+YshFz2oGuyiIHWZZVzIdutQtVXT+16tLHNZEsmMxzNabYAfELJX2VQf8bz\ni/uK9odKv2BoFW+ZasK4zeDvoYXvADqaumGijV+KEq/0dUWD9U1hMu+wk97v\nPLIz+sOh4R/6dMNlggowG+WxgvBEdApghBMGmxtbbaWjb1dJo2lM0p4RCud3\nhnkXi7flDDQVP/E6pA+1C7RUPaYffMKOcNxnTSCIiRPJ/s0yBoGI4MGxFHjx\nqyZDtTuWwZ1ozYUItxbTIGGGooMKHZq8LzkqkLqxMd6tz+6rkTXxEC+H0uNt\nybrOb5YCzygzSFALIF8Q0mf2kGg4F4e5uKEL8PkLgNKvHwd/Pu/skxZOrDqv\naVt1\r\n=xyNe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDw5eywauMGN/e0VYRw/zSqp1t/IiOuj/8Rnz6bO6FAegIhAOiD+rxOX9EoZv2L5iMmwJiQJJ7k3Q9KY7tOlwrjTF8C"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181227213905_1545946923707_0.518805822335048"},"_hasShrinkwrap":false},"1.2.0-master.20181227220209":{"name":"@atomist/sdm","version":"1.2.0-master.20181227220209","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"458e92319fd6c6b643dcff76adf410da717a768d","_id":"@atomist/sdm@1.2.0-master.20181227220209","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fOReUFsh4ytCEn4LqEgCxDvzhtjTw0+4FmmqyNV2VIruLjpxJo1WpOHpK713UyqxWoEawEXJuE7qLFYkw/Yetw==","shasum":"880a3d3552dd29c78b8ac172247abbe68a2f5099","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0-master.20181227220209.tgz","fileCount":715,"unpackedSize":5490347,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcJUybCRA9TVsSAnZWagAAZwIP/0SB5Jhu2khITFAErj6T\n180s6l2XGa1NisUwK64c4U0wbKd0RLKu+3A1w6UOQXf3s0Yyp38ueEPBqphY\nm0zBwC6ALAa9qDP7xCZAbCymU6y6GCK1nppO+U0bof6T4/lD3mw29XQWRrzt\nVGU2uje/jzVm5I02JfP8/4pA06jPSyVLQf3nTeuvumRT9IdM8A8wlgAe9/7H\nXANre3/hTrf6PeJXA6yeNJVuCjB4kdYc5kBmqoQNsum5/yjXobWXPlYlvNsz\nhHGAUbJ/LWdX5EVjir87fnQTG77wQBT2aYik1mCkTsLENlXXNeoFg6WHZAQl\n6uCVQxBU6rh7xhaxol9zgcb15gTygPWjYHHWd0tGgdsYaB5l4UtZc2Vd3ZgO\nyHoSL+ZCl6oof9jyBgqfJefYQrIazYeezNMujdu3Pu7WHuVHQCM6Yv+guNFU\n8z+H0Lypiu/mzb869iRfgK0LdYZXORSqfj6k7yXDTZqx4mPjf+7Jea5b7nu6\n74EQoIw0ifHxmCeuvVK4W6pTv0nE800gkNFABYaAsWyKQXzjlU2/zMKwYELs\nYhXoKonp/mbphMo1qedTvN/K5Zjj4+Vxn6E2PsuG4/Tm7TpdKXkcXNNll6pO\nDKVVq3sud0pHbTvP81Jyojl8YQIo+GUjH3FI+vZuxus3WM0DzPAHCutOrEFa\nSBN6\r\n=0prf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFAl3FYQVDyv+RYt1tptA3FDMIak0P5mDX74VeBE8BQjAiEA+hoS1L0/9VPbHkzhfdQdbavKNGl/BWrzMxEaKcG4pp8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0-master.20181227220209_1545948314631_0.12029634798380417"},"_hasShrinkwrap":false},"1.2.0":{"name":"@atomist/sdm","version":"1.2.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"4ca29089b1035a3987583bd45bdc03be6729ad7c","_id":"@atomist/sdm@1.2.0","_npmVersion":"6.4.1","_nodeVersion":"11.4.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-GXUDT1rOoCm3J2/bhf4eH6BUlGcgWL67qTPcGmUrRmlsWL8mb2hh9l3Xg5FjsmHmDrCs+3H1a/r1MeazxMID3A==","shasum":"63f19f324981f998f49223e064ca33eed0c80368","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.0.tgz","fileCount":715,"unpackedSize":5490325,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcJU3MCRA9TVsSAnZWagAAgygP/jeeSDzPmFuGiNsJ0d8S\nPf+BgUJkDcQ+2qQ543/cxWdj9pujosSSrqKENo3g7+jK+mFIvgKarMa/avms\ndM50aT1mqhw8QkCGxYi3u/SEXwFd+lPeJ2VkWq9TZG2AspmMN7j0tyPrXwTd\nP8v2D1K8BEn7RmF1ggUVg1gQftxLulmNDbSDULuBTGd+oBjR+tUfzDPKEmQj\nvwPzaPnttnLu8/2oxcd8UCWIlscz2Sta64XtNKsmsWUQPls5GGskWVe7U/Ub\ntDL6wwGLT/AqfdmiQTEFJp2fiawxIjw/SdBwkMI1eosJT4tIXOW+WC6Sto/X\nwlZukiTh5bT3goGIPdAG82VpLiN5FIz827/w3skE2cywjsSq81GcBUZLgA+M\ndnGyp6MFwe/BHtkLVPulSl5vwZW9dM74ZKh6Mmu87NOdne7b22ukNiBzX/V0\nTHXrk3sSHIk1QA4UXtgFb2o97ksoImZmZjt1GMGFqiJAn7z6a9FPK5Pgf5Xt\nhwCwrvBt/MRxTPuF26O2WmwGaC+k0W4oqhiwNsDxoJv7a866L5heA/yLTHMr\naR4Q0QQtwfpbaWSX0pTHYq+NeQxkVE78AdwRFQla1F9yjK9962T3+wcVDX7k\ntA1sm2ui/E7WNT5G+81lTrBR1BejJfIgi/bE/sDlUY8z66YfhDjV0GAgeebV\n2kEn\r\n=S1Fk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDt4ZgiFh7o2w38c4J115nNqT+djrsNhrPJJz1or9MZlgIgU11ZIhjvQYSWttzw7RMinKi/MXSpiOk7+XwK1HZieNg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.0_1545948620072_0.5657615885995619"},"_hasShrinkwrap":false},"1.2.1-master.20181229020155":{"name":"@atomist/sdm","version":"1.2.1-master.20181229020155","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5e45bb13126687c1d5c331e4c2b6068a55bcc332","_id":"@atomist/sdm@1.2.1-master.20181229020155","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2LWYaAM8V176JOnv2r9o2krMUDVHPUQB4p9qmjDNUjThp3eODABauU89bm3FUa6PIlLxqil9M5xhm/MTSCGkhg==","shasum":"2b4554b01150f87d3d6b9d9b612592ff74b4598f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20181229020155.tgz","fileCount":715,"unpackedSize":5491049,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcJtZFCRA9TVsSAnZWagAAewEP/3y8IFgoBsTEzdvaoCCu\nTGWU2FGG9E5G/u3LnM54vj+GAq5okaGmwYHRkGDRNeSKFv7Gpt/9ZusYMUL2\nDIPv7vUmLUKRO5j/pZvdRiIrIv8JfY9JocNo40ttbrH1I56revIzCCIaNr8E\nDjWRvewo6btFosCvewlAAMOv9ov0o8c7Zwk3HH+Ycb0JZWjxTEcYXG3TOvLO\nxBeK2/HCyY+3qszKLllC+JJgz+fGq5sfz2UpS8Dh7xRt4CZLj2JxeAVt4kVi\ngELq5M/D7FFW1AsOOElQBFxAGizQ5ztuGkEJUwb4ayP1mtZIDsyKcgD4/Xlb\n+94l7Klp3xvaxJpyHzm1NG5cYmg2OX8c60VMLCzkBCbShzTTbdGdXJmHr2xe\n0irMkd0ZB3hTFbuxk7bp34Jj11fqeZKfGKaJGQgpyLYrejyZ//fQ/f4VOGSt\nx4mStb2Z0yZE8q5XP8AjCA8LLugq3OwIfwvH7nUS4ZKFc88OWzw99dln/gR0\nSGAQnEQAXhAszRj2TcG8OTTfBTJZv+vIxezaVsNoQyMtWNsZVeDLERy9ixZR\nYM18OVFV8wzfReyhaF2vlf7sN8jxukSy052dTYqTh0y7lfx+xxRGQD4dYd3A\n6KoK1r038FRY2uKuDi24+UlvlTydlT+uLnR0HraSe2XXjwJd+2znBB/ODxFx\nCuCl\r\n=HHHW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDzBydIC1C99WuZvfCf7QqPrYBpRUZuRvOP0OkImT4KYAIgWqv3KLQ7aYFn4H+0APby79DS2HTZ1/wSF+sguOlZY1w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20181229020155_1546049092944_0.2903962000649847"},"_hasShrinkwrap":false},"1.2.1-param-matching.20190102182225":{"name":"@atomist/sdm","version":"1.2.1-param-matching.20190102182225","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"603ee9df11d39643035af7a9e367e34d2e1687b0","_id":"@atomist/sdm@1.2.1-param-matching.20190102182225","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-boec9C6ISVet+FaPJUT8OMPIILYtlTLJZ+jj3CO+JPdIU/DFtdZe7B1iI9rjlJ0mbwNbbvYm7tzvzKNx3a0geA==","shasum":"20707632ef3906e379e93ec4b7bec634be0c5e6a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-param-matching.20190102182225.tgz","fileCount":715,"unpackedSize":5491193,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcLQIOCRA9TVsSAnZWagAA56YP+wVmcQr3rA3rhSSULr38\nuX13aDPlNcZqxu0UxU1uCqIadPvPQ/PQAwZbPPll2pPngIWYDqg5L4AGqmKV\nQTtNuaLEexLbCbw/xP4WfxYont8awZfL6Vu+QtLdNxhXfhDyMKl+e0vnFiKR\nTiytVu2EQzIMG8jGCh5gCiHtrOixsJSxlWzs4yOYeb8WiX7X/8R9xhmhqKl3\nhJs66R/EO/2QicPhMFU5zScgEe/QSwaSep+aQkOIezSEdUwKaTGINM7V31mw\nUlno3GyBXAqScTjVZPrv/n73pHJ4p2MeaJOWQkhHqYchac/XbgZHa9VFbujL\ndMjC+qPaEa78hYxwkn1ej8G36WvyPpcspqvRw78mgqVUuuoUXMXs9r78+Ado\n+5U3Dek2jys7eK4tdGbBJdUl4MoqjYk7Yjjq6QYKfzGhlIrm0PZ+u4VghRjJ\nUb3U6LF71O8SHDpeXtzEZYdSgiHUkiuOIzO2XvskugwwJvk/3Qx9a+Alhj4e\nO9LKSYllseZNVpYji1tpHYlrCjxrmApXIFI5Lk/GU27q77Qo6yArjV3DbgCx\nWSTWIzIhHDx5y4DzRp19cCZEbZAB7Z67RkBuEBxEAhNpRfaZscu9H81pi/eI\nMZYVAOi4tkYFkxmauLBC6l3cZrHWL/9a4lcRq12pcgQuCaOeAssF3//5lG7c\nVWLl\r\n=u95p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDIlO+NOqkuDJL+Pm6vE/qv381ycfBfD0/DL5rvdeCQZAIgBk5cPMcoFviAC6Dzf8XkfyAgNCyZEpPo3ewV1gFRHA8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-param-matching.20190102182225_1546453517586_0.5947264258368967"},"_hasShrinkwrap":false},"1.2.1-nortissej.print-stderr.20190106022600":{"name":"@atomist/sdm","version":"1.2.1-nortissej.print-stderr.20190106022600","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"83b8c81d33b2785d5d46b0969dfae709b9fcaaae","_id":"@atomist/sdm@1.2.1-nortissej.print-stderr.20190106022600","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dt8PqrAerrcejYdSKpnAnkBPVDfX4HZFrISjljYJ6nBWaM3R9v28xlpOtixmS/9KyPq5HhSVdbPIaLa9aFGEAw==","shasum":"5d14a44e6e871972a286b579ee9fc03487c6ac9c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-nortissej.print-stderr.20190106022600.tgz","fileCount":715,"unpackedSize":5491421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMWfpCRA9TVsSAnZWagAAOREP/jvYeRKqDNqsmSFSvplz\nN2Arwsmo7VEy7SYmyyeyYwHoPLZ2b6fAQMd8rWbL6DoiaUmFAFAJsdcdWWNC\no0kaLNd7p/inYwcbTX7vOpaRAk3R3Kg88h2E923L8PPNUPfS3UEwj62611CM\nHTG8LkOn5eGKoYaABl+GFK7P/xciSbi9tkhALFtIUqHnF2CwTmS4X6Nw7R/d\nvHiD15jhNpUbEenTVFiImIBwW+r0g/Uax3TPyf73YyxzexXxQWEo+HfH24WV\nORUsGlsx0atJZSrqRf7wjH38EGTTs1cjE8Mi26FcE7SfV6udaUiW7PAKOV2g\nRR5TbWRL+RlZLfzoIqnUgKeuzkal17+WsoQqZx2E+lk3pyTGwn380k1xoMEX\n4Ta42Thpz/k+hAecHpQRar9sILtCVHj7NNRRcJVrvpn0DiZZd5dXNizw6YRf\n38fe4nbJS7UYhfuP0U6zpx2k+wuXsAAQuZS7xbwyMrNJoC2fx8L3Q7RWtQVD\nQIR1y2wdj+dYGqgw4djd+S3aow4Rmdeum/qxq2G0HJtAI5DyENlCEGb9+tKE\nhmrf8ClA8nrUiMcerVrdKtjaaV8rQdVq5hSCwx/qtTtCWbCvs1ZkpnyC3e/R\n0fIEyMb188iE/qteTvKmPx8s1/MJArCG1vJyl9XkIJXAAsxEwGBmEdOhHLqx\nToyB\r\n=s84e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCE/OeeA1MTEVsO1O44oPmqkgPVt3WKy/UKrRbJP2fdBQIgYbvIEVis0IEz2gV3K4eQDxe3TLDbHlSQdAX0a49OBHs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-nortissej.print-stderr.20190106022600_1546741736465_0.9443345450459892"},"_hasShrinkwrap":false},"1.2.1-master.20190106171203":{"name":"@atomist/sdm","version":"1.2.1-master.20190106171203","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5e45bb13126687c1d5c331e4c2b6068a55bcc332","_id":"@atomist/sdm@1.2.1-master.20190106171203","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-B86rar96iiCK83Jpk4HaKExZ6MDUh3Drdjd49JamZjmQqiooaFiaAwc9fLFjBHOTOIzj4ppd1mQrblXcfK8Mew==","shasum":"80650a2fd7682efcd9b0cd9632aca610075f6816","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190106171203.tgz","fileCount":715,"unpackedSize":5491049,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMjfUCRA9TVsSAnZWagAAYWsQAIuDev3gVDqyTxyKlfva\nzm3s0XxOmEFB2K5mfsnxL+Q/xJmkbArxnk7Di9SZnFWn4fVayq0Qt0u4bO+Y\nAPHiJ+b+SOjABWlU8u1Y0kNKjVmKMZOcS2nOfUw0+H7N5a0hXmoy9og4CqUd\nFXjNpENQK0nAIYHjsyDpsOtJEXNetNjU0hDXTnjwpL+WEeynG79AKzFRD+35\nhya8sEiSyPW7czb+/4hNrgkDuGbefhcd0Hvac7Wx3WT/2z4yI2qkDOpmUnqk\nRvFtViA8zjdLSaQBSAYPjr55bo62WlPW6sQGxqfsgTW24NaqloODPGx73n4J\n+sP4z/hvwBA4WEMHJPXNiI5caPi7wFZRFn6Lng3S4o+1enBHIrYFMhXapnsM\n+gvwpBbNP7hABeuC1wAUn+BuX/18tWYmISGZCnsBQIPDro/cnS/gLJC5Aa9A\nJev2jowIJKC+G4KQKLfz5bArgB3+sHGCXGyZCY8DYpG9fzYfh3ORm0XFKDdE\nwKagKlRvWH5wsSnLnbBLkw0UOOaq3tCjvl5XxuYp17DkKHDbJcuLyTpr+8qm\ntdh+ZHdNmLDLP6/UE+MwIgWMo9hTO4LiLHpPYPOBNhfjIaHtj+fY6VsAz8TP\nPwi0B5962UVff2DAiLBJClfkO+2fFXoyT3ufmfoCLAqbhS1t48JNUXvNbY8y\nnq/y\r\n=tBX9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjKlancjwg1YhF0nSadYeMJrz+czMwR2Y96Y/U4nVIRQIhANfYNIUwgVQvw8E9FUI5Pp4+bBr7hXArNpUcEk1Cetmm"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190106171203_1546794963646_0.28356766385873655"},"_hasShrinkwrap":false},"1.2.1-nortissej.populate-sha.20190106193518":{"name":"@atomist/sdm","version":"1.2.1-nortissej.populate-sha.20190106193518","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"80a3875532a0e3ccfe0ca57b45e95a098b5f2f51","_id":"@atomist/sdm@1.2.1-nortissej.populate-sha.20190106193518","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EfubCHggGp7eR6Ib/GfufFjmZy0mWMcmGcdy4yDaCBYOIVmhnzEp/0ySAJaWRDUVLhhCSuOGyeQeeuQ19x3fHg==","shasum":"be5c20272c04c94060314f1f2b6492f575b65355","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-nortissej.populate-sha.20190106193518.tgz","fileCount":715,"unpackedSize":5491366,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMlksCRA9TVsSAnZWagAA5HMQAIpyJtEArTpdOmifw8cP\nVv8ekDIse922KVueN8hntVCELbiL2l3dDaUFface5l698ilP1W8mxpdLQHX2\nlnawE9IRM/xwK2DG8aPKpzgMIUEEfhChAtv4rI6UTVlFBnYREHg/zbyAS9cf\n4pEahJHhHP1kRuVsRlagTmJcCPbqsFPb62ZZYj8Yq3k3kBg4cYH8e4mm9KDG\niwtZyZ9LnyPjzFQ30CbdLzdE06XR0JIyv0dQsYQNDIC/xJ9qP236wrQAaoFC\nZ2GprPCySlxVnZSRJ15LUlRdziTC/SsSwqlVa9fntx3ZPg+ptZGq+P5+mVQf\ncB6n77V9H1K2bNDFFL5NG9gmaVDRv/ljMI0acqi70jSB2lLH1VmTr1mS23iJ\nHW5YBpFxfnaQxPzGaZxAQ1OQVVtK8CoJ0CiqNmCmYFw5kSME0jzHKG40DiPH\nN8nIP2sN5YQ+AphBBVW9joSa5iyKbJmeCPopojfxXhkizlyAgiqH0hG/LcFR\nD6iv1Um4EZkviBH4Is9S3k0VQ3zP5BAsN7w4ZnhqIvjmTMhW8pa7zR7myfFs\nH8vumEGVLHHbeR/EXRUa8eTCZSOv5uwJHAkNf8WKVfZEriAvk5ezpE97LOo1\nkLfNqw2VOsbisYis62auL/ts22u6AJI267B+RQhcgnEeUuOTSat+0U9CxRmv\n7PYF\r\n=Qpu0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEzgsPuOBQLoERFjNkV4ERnejONoabC6p4SSogfwIsv8AiBVmPx91e1M8vss6owiCbupq2b1mvGmJ+vPItkhj3bs4w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-nortissej.populate-sha.20190106193518_1546803499589_0.5135012867771909"},"_hasShrinkwrap":false},"1.2.1-master.20190106203935":{"name":"@atomist/sdm","version":"1.2.1-master.20190106203935","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a86052364131a2827b4339d70e3c247f1e273b85","_id":"@atomist/sdm@1.2.1-master.20190106203935","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yKPEzCa7KlZo1pJTzxSF6zo09j5XoUnKvXIhVnZgrERzfWFyaj+1k7+TobERvKRGi1QRZkrYwwSmjPPB3RtGAQ==","shasum":"0780e32b73d44bf60b34d62d27df60d6cf807392","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190106203935.tgz","fileCount":715,"unpackedSize":5491466,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMmgfCRA9TVsSAnZWagAAEm8P/iM9BogcVpKrX9HuaoeM\nzCGClFWpES5nLykqvvVIjC24t/ZvCFkp1x9VegDYKBzt0wnxskU55Pt/BPKf\nFmWtvdhfRysrMiPcYfoL4maEd/P6237fmRlp09xVHaIv4PsKYMN/Q2HWyCmh\nJXW4PShha903yYdDvZSsKhPVxX2uC1y93HnTo6YShXlgz0f3g/u/8Csnhc8f\ncwuBKoYEtX4sjkNZgeXmndJSWFitADyuE4vE/hfM0QMjQq46GjaALWtG/kmO\njW0OubEECRv5ERPuFiarvEUkd2wRdq6sD8Z6j6a0KFB3c7Trzhr16L0p/7zC\nd8YGjjWIXCFYG3tIVt0DHoEibvU78DxvM7yoscTSa8q5l0O6ogwqiFFRp1tC\nKl7M2PnA0mGEO6xv4d2WrhjWoUQcm/dQTl7EJ5vup0wUaBr6P8+kk9Ewfvii\n2uqdQZj0EG8Iw5sdMFDK9feL0Wmmp5fn73IYITtzgsvMRoETjNSuF0I5S1oi\nH23CIeoCxOpm44N5oQ7ysQ6es+699eZxcVX5EROEsk9tPhOi4gI6bkiOB4A5\n/yZNgiFYsf+pkTh4Kxz8cpqDCWsQ6OddSAHmhf5Svuf3aLcWgIkqJbwMYBGM\nJgXRqlq8xnsw0a92GrtcF2o7nUM9TnraUWkiGW/YJEopAIIGa+kfTlVU68Xu\nbFrr\r\n=eY3l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEAbX3yZFiy7xRjU0bgYEQ4McqvsZx+JSprjmAmAhiGDAiAsokB4OndxD1l+FBhZXRKip+RGlPNUIjeSrVNRKolrVQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190106203935_1546807327019_0.04937797777393271"},"_hasShrinkwrap":false},"1.2.1-master.20190107094905":{"name":"@atomist/sdm","version":"1.2.1-master.20190107094905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ff8aed31f2b90fbdc311bcc28d6e42243abd1d94","_id":"@atomist/sdm@1.2.1-master.20190107094905","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-s+CfftFUVkqzoMx+XaVghw8VMgZiLN0+hWIWGrKyAXFxvZii7jgBoc6WofL+iDC1FNLAlYE88BHLjObP6Exyag==","shasum":"5d36ee755a46b9bd1b232baf82ebcfd51b4e2503","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190107094905.tgz","fileCount":715,"unpackedSize":5492759,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMyE8CRA9TVsSAnZWagAANIQP/jSg9rNwcPUtjGtYGNvo\ntkdD5pQCMNIRmJmfQm3C5mAV16Swn5RopN6vx/BXcVhrX5cjivyXP9SF2YHO\ngLPUPbMeYcFuW33Kdaxxr9XQ8fASY5Wq4Fh6k/5eAex8+N3o+PdPQh901xmp\n1LVWexD2wt5TuKTRrlBApmgpCt/+VJCMSsEEkrJaogCvYoFEzgAyKub8vQ9q\nbuQdsIC6xGfe9UmOSKimBnGRB42MfKZu7JkxQzR4oqLjm/Ml045sATPwzbAO\npclhmdu47iZHB2L+WLCrWYar3xexBObiM/pGXJEy7t5sigh0zEUIjlgjRbNU\nLiJAoQKhpl6RLlbggIhaEgctCJaqjtDbjRyxH0lfW8qIin0Xt1EmtWxNznOG\ngfzQ7///Yl/x8e4qPiNDRBPwrzn6vHIdX/8F9yRp1b20oIyQZr0LxMXA68QS\nUO4uOdNZ/tKeJ5Udpp509fVnZtdQlY7M1QTONoOtm8FyebIaQ9L+vgi2G5sV\nggNKjFGMn+6SMnbHd0hJ5sfC+kMRI6g6eD6CY5XQ9x5BFudCsojMWA3Gz+9z\nLjpsoyJ72wXchQHmmECh910cjgpy5380h2F3m/TATqlAV4rqNp7m6593wIxi\noFR3GB2SBk6tMNFGHobHLBznVp86f8xYgS2pIYrLhRAoJNpB4uAHBqGU/Yew\nLbZu\r\n=7/zl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBfLTlWJxYjJNyOECDsh7s7DltiYS9qcC71u5qDl8BpgIhAPPVGWHVkDk3Udc3fhj3BEWvSBKoSrGk8JCnvMdSGumo"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190107094905_1546854715528_0.8385262447469504"},"_hasShrinkwrap":false},"1.2.1-master.20190107115548":{"name":"@atomist/sdm","version":"1.2.1-master.20190107115548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bb5d11b2967fc0cfe6d76859bca6f0098f4bfc3e","_id":"@atomist/sdm@1.2.1-master.20190107115548","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-u2F5sVzyVYG+DaagwuhMbuYSOiPnvEtBWU6ZFb6kRCQ9PBdmd2EOUK7U8ndHkYGxH2kBGKlPOwZdRsYccY9yRg==","shasum":"6e95288b283261b60906ea270d612d63423b1963","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190107115548.tgz","fileCount":715,"unpackedSize":5493115,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcMz7qCRA9TVsSAnZWagAAkVgP/AtHw0MFqqbPtGwTN6ir\nazr1wVsqK5eS0ao9UEX2YbeiN3b9ql3OGyN1gxMnumR++l3OyjbzOthVgF9N\nNYimdZ06N6GeKwUegtsikxlQtmCNku4DU/w9LVSgI21Hu0udzfp4GU7LKSUE\nPi2m2gD82WVszy+vbo+xQxCYhp2VxP1kPJ4nT2LQKBSlwg0XWdj3HwZMws65\nax5lyYyP8YEJoTQ9CpFX4afQp5sXCMT3OSiIeFfzs4+ChKthAIB5Oz13SvBS\npOLBmEMtxMgypjr5EwqWPTrvO9l55SQspspyFBSJmULzXtVALlz3nlkwvAQf\nRO7VMXSb7gfJQa8g0IOdNKOoTLA+yzSUGSDXU5D21W6rdjbVHZ09j0fufQZP\ngeI+49GXcVQKmR0x/HvZEw1rtaz9srjwnkaR+7mFdSCgPPrPf7WsWDegS30c\nTMKJa+dtYvFZhLpLbiqvCKyQd7gQavsucXI6qW2ej5SNlpAHaI/BXFBEY/BS\nfMEoB9evIgyW+SI/rWjt08gHInlFiNMj5CXkSC7epVhpjRnzmN6ef2TJI7cG\nrYg18NgKnvBsvKQLrGc4izbD4bQ3cCWFYQBzi6KyBliLHByHQEpc8tFzQNd7\n9uHqtSfTThzG2eBGP38EE1xm5a5IW0x3Wdjzl9p3oNQNhhb2Zuqm6uSkmIvg\n5nE3\r\n=OZ4j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC1dWt3awE5NckPJlh4o082qZwz7g+Yv+BI/EeWn539/gIhAM+WvHg+c4ECdcQY7rRjRORXw7SXGoNF/QyHAkzBlj6+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190107115548_1546862313852_0.6667478037672265"},"_hasShrinkwrap":false},"1.2.1-param-matching.20190107120429":{"name":"@atomist/sdm","version":"1.2.1-param-matching.20190107120429","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"51e97acd02df9c702f225d2caa4f9e62ede3f1b3","_id":"@atomist/sdm@1.2.1-param-matching.20190107120429","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XyMPDgMesr9Xzu9+3jjNANCCMjUh/RCZd7NB7GxGKxseloHaHjM5elfaggQpHaJObdGmhBjHJVZm8GJQKOwDNg==","shasum":"e472453a6ab43759e5dca14dd03766fdb359b8da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-param-matching.20190107120429.tgz","fileCount":715,"unpackedSize":5491199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcM0DsCRA9TVsSAnZWagAAibgP/jYwFaYxTCA5t11Fe0K8\nxdjcwW69gxeiddKpqN10gvB5rvGLJ6NkRRGHp94HfytAFJ5LCrVqNxuvBtoO\nsI4xRX8sBOkyB+NCmtjAzo8FcfkrN+wmo9lB5FY8jIGa91e48+dvQ1GFkgtp\nLPl5Zfbwk6QqdNOhchYrXO7ts1A8+PfpK9nXuyZ6fRHaPA6FNgSlKXTsatsc\nac1vmwc3fkC5g//aOubLoLZ6aTzVQxK1FgySQFugf+70x7raXPhquTQA93n/\njMtJsutWilNmx/JOYf6N5thPv4vVsw6nYJ2LR2LKz79x0yl6lUnypDJMHcf3\n001ZkhkoEHDJWaPUGRswPWtvgDn1MKePmDW9et+YeVQA627CgmrTGBkH9ne5\nDszU+k4vLnH2NEPq1ea/rRp5TIn/raR0JiHgB/r18K+Y5pmLVKOh+QBuFl6J\nkXxcAFmVlv9DUVprQKkM5GgDI3CpB8jmDy8EeLurrCOXOcIt/FHVaRKYo+Ay\nehJjm2rB1JQsHM7X72+oUao8Lnd9/pxtOPKgu4GoIOlFK60IKR0Tr15WYq5v\n1pgE7G3XpbM6Rl9zmnjIYLrUQzrWwuIIRknDe+VMgmqouESXajx5+QeXpvG+\nMuwYcojI7Br6Cw5fKN4E1WBG97yywjPAYuQePl6KixsVBOW2Qe9Oymcvpp7u\nK+aM\r\n=UD+P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC6qU1vY6GwS2ZuWpzhYV2on+MwStK6N6W5U/5TvEXTRQIgdoF21qA2iu2bssglZKLBzX8T2yPaZmQiElmYIBzyvBU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-param-matching.20190107120429_1546862827319_0.3132204043398934"},"_hasShrinkwrap":false},"1.2.1-master.20190107130520":{"name":"@atomist/sdm","version":"1.2.1-master.20190107130520","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7f8243b3612e113697440f995b4d83be03873ec0","_id":"@atomist/sdm@1.2.1-master.20190107130520","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-FsOP8CAVtNInMbCXWugNHtbD59AqijaNS0MTsGSqtYBK5LC/vK2ref2eAQNGsx0vrcMEARVPaKYnp+tp811v6w==","shasum":"e9bfb1cf164e4c8bb225ecf8e2ef5c4e52790c1e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190107130520.tgz","fileCount":715,"unpackedSize":5493257,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcM09oCRA9TVsSAnZWagAAseAP/1AYwntW7KZGPRvmJLdE\nGbG1YqYm/ruG40j2femMdvuEn3QiAkIYggJ2KVlCfTWKc25VrpuJXQhYJI58\n8BkRqiidtoouOVt9fg4X+IdKOgd5ngHfayHMmT5wEydSo3dvxVpNrBXWQe0R\nun3MXMbKHcGIkiI0ynRlgviCkBFDuf34ib9dbYDKis1DWa0TGgLIS0e+QQqo\nEA9pDuD/TlQu7kYp3sGwQ8AJMbyQY//iiYqrvfBhZiAO9x9u6a4sO6xCQ/qq\n1D65fDHwPqcoJT0y5xPikFoR9aibqh6Ot+Ab3pMXCalrc9r1ulbFjCo4pn8G\nFgGOgXIb1AaRFVgALNSduzp7wbb/UE0VRbipvoLFDwh2DCVFv59dB9KTNdBS\nwhMsty4T2RTrbE5Y2fykfcwTR3pjdWWWUy8whIb0xBkHic228YXjNxXHoM+r\nGMRDBPKtEVzVRin+S6hiRfzdrN53hi0HmUSDCAS9P033qvh6mpvoj/5L+9HJ\nQcowmzz3OH5HSmY5cx5Sl8IjhaCqu7Hh/zpDuNmaREzI43g+pxkR85di/PTt\nS1ecHh+XyHdD2pHtR8//klvtynaUHVZCBLY6+SNOQLFrxrTrNkLQnk901N51\noqNTHirkB+1jCIDANMWMYE+MMhaLPdTzSDg9UBPwlpmC7AD5acqZYPnybrKk\nI0D1\r\n=ypEO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDYobfjLfiPyz2aXdGX5FkFDAPaRs19/EScP4oQPwOL6AiBOp9LYOIvIsXPRwKBXkmU0AMH4pz8IsFkXcVS5t6dLcA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190107130520_1546866535930_0.18130463339642056"},"_hasShrinkwrap":false},"1.2.1-master.20190107152354":{"name":"@atomist/sdm","version":"1.2.1-master.20190107152354","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b28c4e1e554e6dac86bf668cc0178ae23af29a03","_id":"@atomist/sdm@1.2.1-master.20190107152354","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-x2jjWAwSOW3QaGTuAMYp/n7YlDfvmFQP+ho5EVos1LHO3NjVKyeile/+cssOD+IFp/qcLxpLkOEuVTLEhD3JIA==","shasum":"676eea7bd2329a955bf832c0ce2bcfc116d87701","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190107152354.tgz","fileCount":715,"unpackedSize":5494029,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcM3AUCRA9TVsSAnZWagAAFtUP/ii1RC1YF74YUc9/3WD4\nAWhUALeVs28jSlVocWgMIuIKScy+RC34PZ6sGJwN379Z0L1RKlKhqa8SfkgG\nuCxAIBVuAZLWotIc87q6lZcOpgKjWdt962hZtndGlNZ69aZK0wIhqx2C9lRs\nUVIWdQrmiBb9nl/9JgYgdfWnKI534OG64e1677smmhaSFXwd2C5dwjnXBqDU\nmwHsb+rOL0cFPsqslK1GFkcYWKZk9r+HYHwXcZwJAGUAsv7jeEeaaoNe1GyE\nOJmyCfSTrAP3PDG4d0MCFtuq+TnuOj7Qxw/FaPePRYWgTmKiVve3BMkWqQ7L\nyeAxfbSnsAMJkxT1EbPiiyN2iQ8AtGVAGMryDjBZPp5iEoaHJ6UrqOP0biCK\nu/jCtflx3AB3c7lM6RQXySbaNHG3ZLm+7O0oRF10judmMbf4/YAwor5WpdNa\nCxEWLMVbjHhV+r+k/4RLo7KQ65eZyqPMXYn/bQiq5348KRdTplnDTd/hJ+pZ\nWDajwFq+Cz/x4lGjS54kHFND9UY322rAmQrd/2qUvFLpyzhAT+xdBZ3BwI2o\nZgoZvjh3rF3zQRqURambG7+du49Q0CtgX7NaXVYpyG1S4IfB4YrmgaJMDfUE\n6+QagwzHQEQ7tTFk02kALArn4Uk3W7F65HjUR2pvRarhje1+zDfzf8BzEr4l\ne6Uf\r\n=1kWY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDFNt7xWpakKaq1o7QOU1l+Yh337DzOUCO7n9S3cicIDQIgZNRIErrxfTd3bPBOITvSuLwHYcNh21kiuMaurYsd4e4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190107152354_1546874899995_0.08007174634085046"},"_hasShrinkwrap":false},"1.2.1-master.20190108095009":{"name":"@atomist/sdm","version":"1.2.1-master.20190108095009","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"615b38edfb67b2ce7f201130ebcde8bb886810f9","_id":"@atomist/sdm@1.2.1-master.20190108095009","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5Nd6cSnbHq3ky5rIu/BEO9K/Sd4XUc/l7YCsr5nIQhRcmQ+TcDSrSlBrYhhXosCR77rDxHnbBqQk6JOUi8YrIg==","shasum":"489e6b79ba0b48c6c20ae30e5d895d423af0f129","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190108095009.tgz","fileCount":715,"unpackedSize":5495317,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcNHMkCRA9TVsSAnZWagAA5MMQAJqJcP5XTpOMhIJqrDvk\nCqOiM3MPn5yZnYyrcuLEkxMZQiKZ3c3u4th7/8NxKVrK7PEuvNvEM2TOXeMQ\n4K5po5+EbRjpE8MSFeLhfrgIGDZNL08NM9wFDDM0CSL2XMI6gF5QMclHerD5\n4Q71SRsD5L9/RvI/mfGfselXvXE9RXHx4fcu1XXlDwvAjUFYGTFysCGV3rOH\nf9b8VNbAeNt9qGUDEdF+zYtnBee0YzqlztErP75MV9522Rso+KDU6JnUSDMJ\n/+qRCsxRphcQ32Lf6cKAxrwhkMP5fVpIIxeOdwVGinJ30bDTzLfJVyNmOAE/\n0AKWcjg1dRFY3hFCebTmN4PO6NG3J6hOzUxEBGybDtY6LXnzY0r9+JvV4QIo\nMscawCiiBbyPh1gpMYQDQ8NU2TFH6G6bn27kmKgDJffHVJiga5HrejVMdtyW\n7ks9FIfddG5mhw3qfgOoQAoeBEm4TtL/4IGsBAZrQzNhlZBgrMPnwcBlkGc8\nHhoryRHJg/2Vr6MogAO/NDPnHbms6Mb8eopWJKpxrAAhJ+Fa+Q4yOZJLVX8O\nhnLZULefKUsTJSOi4xsCp0Kban1iNCg3eap88TMV35U8ifRZYvH2wmqHXc6m\nR3CuBEyAwmiI8j5+dof5Qs4H4zKplaLo2O8c5DOFa1vqM6D6S6d8NjEeOt94\nww3g\r\n=20KG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoW0dAjj5B2BfunE+AE2pgY4vlQUhEMGSXK43K5z+E6wIgCbuzqNsVaBWTBEJQKsqUXuDSZH9la9RZHPfmp9NqTiA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190108095009_1546941219964_0.8486936220337908"},"_hasShrinkwrap":false},"1.2.1-master.20190108111305":{"name":"@atomist/sdm","version":"1.2.1-master.20190108111305","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"615b38edfb67b2ce7f201130ebcde8bb886810f9","_id":"@atomist/sdm@1.2.1-master.20190108111305","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-v7dFkM6ZxBCGAMXE4DzD8bf8BDCKqvWvsFKfigAT0h+Fs5979UwMiGqS1u+7RbIIq2H+xY6H4q+s+PmNdlp+Yg==","shasum":"cedc77b7502340e38f66ef0e6d8ff62efbcd755e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190108111305.tgz","fileCount":715,"unpackedSize":5495317,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcNIaYCRA9TVsSAnZWagAA0wIP/jYgv1SVpERI4A48IRNL\nXZHsgAEjwMMwwPoqeMoyqsi8zyTN79ad3YhmvZw4wJmwmPmbv0mL3FN0ROJ7\nrSgG5MQCSkdj046uCsIlPRoljtloyEs3tKqF3N5CsqL7ZztU2t1Au3Zjbh6/\n+kRNFnrxFetemUDgcH+dOz9KeGxJhA4GjbOc/76Z8/aWs3kKFDwbnnAoKxEB\nm0wkUy99tR4rS8nbCOaG/Mjoa2/YEG/p7Ghi0l9RMYHQizSVb+yfkFNviBU3\nYoDMCxtEHCWbvA0M+lXUaLmEIUBgh+Rwg7eJf3o6DTve57kkJGf3bYnApO2k\nm2vpxMY9nJ4TpjdvcZtnZlZbOs1Pw+9zGsxMHEVdIeHge+voFb/xbiT1c7iL\nCspQJ44P5yQPpfCw5iYzwoW2ahSgqC4dnQwCZQmsL0XiZpgJDkgrYTwuujZc\n4yTfs8oIMMZAlumfFZGAPEL6QwmyNx6OOLTrLWO3a2vBUVSeiM5+Ysfrq9oA\nf0/BAtGmF3spE2cwwSRMd9aTA3FfRDgqgKBoIpK1fuZ4v/BtV4H91rHY9JGg\n6F84TBNYRbm3PmnyClrAbKUNzzpE4sbZcAaordHdySiMNactzogTh/TbRV+1\ndKobJAbhDsJ3w65bJ9Boo/CvqUNg4VsMmLpb4WXkP976tSXAAlKWlH6NoSlJ\nSaJ5\r\n=x/53\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFqHQXQqHV6JRwKpglZ8KpTceM9QzIPKie8r7QN3sNjbAiBwulbP3vPBQZNozi/y/bZtorvdd5rZeDEN1RNS+XYTaw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190108111305_1546946200220_0.5108867884657338"},"_hasShrinkwrap":false},"1.2.1-master.20190111162309":{"name":"@atomist/sdm","version":"1.2.1-master.20190111162309","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f93ec30abb9d79874c80a3e5336cb92e14da4879","_id":"@atomist/sdm@1.2.1-master.20190111162309","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-i33moXR41aLrKdNNj/UXNlFSVrXHVn9CpvvNEwr/vwYbyV7dUovzeHEEGFDt3wk9cM109NCfudIL0ETTeyVM1w==","shasum":"d37e14057643c9879fb45ffee5243ef2611079f2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190111162309.tgz","fileCount":715,"unpackedSize":5404634,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcOMRGCRA9TVsSAnZWagAAqfYP/2w905INVw99O9TZpf4x\nTOd9fjDHuOpBB44WOi95OaEmn08tFCHLE13zubMJs2Kq73C8GuT/gwanNsYX\nxm7ZPL9TOOiZVm3i/V5hI/PrK3iq6rs9hwOaajIl48HgctCxUfTVPbsw0ds5\naEIR6b/TAq4Ay6cvIPqDP95aoKvWWBKZ+2A0Zh/ezTk8rjizwUVpcg0RWhHo\nTMEH804U4hSzFp0QJ8UvphugQ9pJkLUl827Jw7N2MiqojVsKCmoXDyb7EURe\njJNjgtPAv5m5tzPq4XjVfu8tOus2rhRShvzXw+sfrmVrBfXUyLO0kfkpau+7\nQufCyzLBfKHP6Gg7WLC5XTRj7R8hkTuKZHjRJquK5TsFTEqjnV6Ijwv3ilfG\nCJTxwJ3+h4gQnQXN2FNXtEd8JHjNJxg2lpIdU0H/QHl2pxqEkehXCtuBc+1D\nBabK/NMQThOmyrkmMv5owqHwnPKdq7F8Bjjbtcetk9RXjAUoV546Tv6g7CzD\nGFbH63WhgK155gCh9kV92xUazV8Zeogs1/RnGZxMmyggRrjjE7gVJ0nRidar\nNa4flbXtxQyef7UMC287ILy6+4g40MUGKUPEbSSno+FY8r7cRb4c+Vc0BbHp\npvFuki7IfSOSTzA+jJ14KC4gvHl38L4fc1B1HoTDSvk4kcJjxJ2lOmWwxVxr\n1ehg\r\n=f8e5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDo8bXDycKzvmo4EoH1r65xkAdiz+c3g+sj+1xwNLFOpAIgJLYJXiJIvl5jfHxutAR5ML4uR/2ah1d30sbVifsKLBQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190111162309_1547224133205_0.5803296782171254"},"_hasShrinkwrap":false},"1.2.1-master.20190111162937":{"name":"@atomist/sdm","version":"1.2.1-master.20190111162937","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f93ec30abb9d79874c80a3e5336cb92e14da4879","_id":"@atomist/sdm@1.2.1-master.20190111162937","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-5BF4MISwy0yvlQ/vW7/3DdGekihcI0/NrNAeVQrq1NtoF3l4XvLXW9tGqjgskExwflbKY3qwD0o4F47zKTEi/w==","shasum":"aa80f743209e73534f97fb3729e0d57436e8dad9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.2.1-master.20190111162937.tgz","fileCount":715,"unpackedSize":5404634,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcOMXVCRA9TVsSAnZWagAAhOYP/R/ORpa4IhVPpFVZ8UoY\noNNxjwCCC+9oeGqCsvdE9CkS7kV95+TBiqVxS2RRpPhtnB5gR/s2pFYr7t1n\npncY9IiDd+HAxyc/I8JKDO0rHhUnCbp1NKFOsaTAJYMiLpHeolvgjrfmprtK\nSSLo0xkfABil5SGQa/56E0LANFw0UICCSyVZ5RfUdcsSmU+9NOnlB+jRWnA7\nZXGFQeUwZvF8eAwKGUetoZmCXkrkIc6lZg51/BFwU3iCzym8+7EMyCK0cdod\nxxQD4q0ZCQPae1Qvx/iC/wVo2iGxFy6dGiRZb9NGdvoinpkz9yCPCDx0xL0i\nhD9CZQ41u0dHXv4AJIDEukFo6CTzzXcw78VOikpQQO7O2WwWOvo6JpNM41tp\nQA9rSeIoc+ZHyUhDf0A+72BIi6kP2sUEReLI8pGm/uSabz6LbPT6BE+kVduu\nXqv9mGWFtl8oFlFitEZorOFAd0IclFIsYsxc4wf3e/z0NOvG7XLK/KLec4eZ\nAphTTfs6VnqZdLvbZn0b5XQjUejsl6eHWaE7Cb46UiqKef8Uhiypevz/w3Ir\n7YmwCTKqtqEiV1cfq3pU7VKkfi8nK9ul5hA0GYifvJ7ym1KlndHS5hkGsvVm\nHEPCZBRwXOW7Cerr5rySac0juDTiJ8rsynn3F4QhbcK4Iwgkmr1h0TS2T78h\nPYL4\r\n=1pvR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsqHRZiiFN77uQCo9xug0+c+wal3krTWaJhe1UnWkdxAIhAPVRNrbWNv9h88BjW7uDuju5S0VX3IavtbxqEcTepi4q"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.2.1-master.20190111162937_1547224533106_0.8947442585507208"},"_hasShrinkwrap":false},"1.3.0-goal-launching.20190112115551":{"name":"@atomist/sdm","version":"1.3.0-goal-launching.20190112115551","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a11703b550db232d8587f63877527e6680825c21","_id":"@atomist/sdm@1.3.0-goal-launching.20190112115551","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NqKi0KT7Y+kN1Go+EcwOvSNFVDVSiwFRPuiiUe8BG1ODs9gFmE9rgRDxAYPgNM2Z4l6Fgs9YmP6vTwPXVXcRWg==","shasum":"a1efa51e1c4947206ba5f750c1af3e478d99c955","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-launching.20190112115551.tgz","fileCount":715,"unpackedSize":5497333,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcOdZ3CRA9TVsSAnZWagAAxf4P/1eCRoVN5CfS3JKwfP63\nFbcLUcZQi+SzCh/hHSZuawrBeZkE/IV7nOCCLdUpO/wn3TD1os8jt/5siijG\nbydeeXvZOLlQdp5Z34yQ4ftsCImlMAYrvL1rzw6q8q5ywimlpEOVXaySR+mj\n/5/7XJoPDqEpqesNGwSSgBK9cybHLqgpI09pxYsn3NvShdymPJ47HCwne1CI\n8+uXr03Wbn1kJoeJFrxpMdpClLE0ZkaJTwlDEbUkd+wmKF39YyMA5yMROom7\nFsgkMeyZet05FebSP51hUmX1Xh59Uc6R+UQEqTJnTQq/I1zIU7eHI9fAakwr\nz47crSSHbv3x9+HQCKHLSPDinY4fU937yHSoCaoOQbRHdV4hJB1zz+m/SDFj\nCPNMk82qC0TTN+vWRrrYfkwhXIPzsbXH2KhaWSu3vNpjT1QCR4ZaN2kVFVUP\nNVOGC5taoa8534o3BmsnkTtiCg7AirMibUvn+ZRqiE80ifkGabZzO2LlfaL8\n7VyT9GE/3NAI55CrFJFfwFi199w2o5NHeYjSwNJlTqO39O+IgzOyMjhmHHYN\nH0KwStCVzsTd/IEhjSUOrLrJIyNoiJMwBxnoYJJ6KywdODaYOT4RNRj1N8wC\npUdbS6QW6kwFUVQ9rSxlt+UEAU0us4Jvm6HZveblqAWHTL3BX/P1ZmfcV/0E\n4lnQ\r\n=lCUB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCWio3p0nJQvtORACRhRiXwJ2fK/FZMzIqUZvcIwCQlRQIgaXkKX2a0bxyMVWtG2Twi7vJ1DE33T/AytJ453CavR+0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-launching.20190112115551_1547294326828_0.6977162717620096"},"_hasShrinkwrap":false},"1.3.0-goal-launching.20190114154445":{"name":"@atomist/sdm","version":"1.3.0-goal-launching.20190114154445","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a9ee06e3f0a72438f0f9c259c7e76ede36b192f6","_id":"@atomist/sdm@1.3.0-goal-launching.20190114154445","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7LxbF1KC/kaLRAO2XEae8UtSe0dU0ipZigCRBxGEf7RqzCXfzBl9DlB5GLE9PPF3NOCObamTS+Z+t2cJnhWCIw==","shasum":"df0adecb6391b9890b995d2d278bfbf6427768fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-launching.20190114154445.tgz","fileCount":715,"unpackedSize":5404618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPK9HCRA9TVsSAnZWagAAwXEP/jfX6x5P5GydhsDnxZag\nO/+bSJmdE7hl46j21GNEMSxi75a3b+IdLvzcuwLD4eCgauKpSXStwrEYpLQh\nX5BO5T2G3FgfLbNsP+whRiQSbfBpSj/UbgeuzVDb0IaixB26TzeGYyX/qpC7\nwjHiGRUOmBV7rcrf+ByVrcBSK998sPFQwvn5db3cyA59PfNujQJCxEMTZUFn\n29Z/0xyTgJ0t8KV0CVhvKLaAkDiK/HxR3T52FkEq/f26sQRz+RxKgVMWOqvf\ncmxcDGgBtIKIcEGID4qDcvQpa8hdx/yyXnV8iywvk0sN0vg3i0OhYFFBlPtM\nfNSW4eJpTvvfxKaoChDzC1lXlqTirKIY1fxnIKZJ1A2eNxJ79TcMlvPuvFcx\nbPNsaPyJ21qS1gOBe3DAQlynLxcHrJ0qwyjVKaSt2AI0+4z0PLBKH5QR0MU2\nXijzp6GXPCEBzJRxrzHfJAlr6/WlpZomalSWWQJ6nCB8ZI/9gs77m7mr9fTo\nZs/CkwK1FW2PcHI78rakYgUyiRVot6irjdbjAiY0MYrW1/DB97Muc1ZVGm9S\no0H1v39FjwdMGSxPgK+y2EiMqfD9vRGIFH2JnLGfmMhomodmdk6vwrFLH0jl\nfOsH0RmBJfRCUcMafSmunk1kk4tp86N2o+o4TaUVIAYXdAsP0U+cH87XDsYt\nkaJJ\r\n=UxIr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUQnYcdGXr54tEklKeq7QDCGvXaBTq27NInSIJWYrtBAIhANqOWgqYS8oKIUlhG1yD0lfG/k9Ue0sx7z3hyKRua/mw"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-launching.20190114154445_1547480902661_0.6472692470918606"},"_hasShrinkwrap":false},"1.3.0-master.20190114164829":{"name":"@atomist/sdm","version":"1.3.0-master.20190114164829","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3f828147c66cb0e9e6ce7d3fe139c59dfbd87029","_id":"@atomist/sdm@1.3.0-master.20190114164829","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ABh83oMmEp+TJUAbWypeuM9LElCPaQacjDQgRpzMnwUcQili913RiY6iESGX65BIIrCTGOW5lelxsbsCXVVd1g==","shasum":"8854ab7fa61d811dc7750d06c1f120237efb3cfa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190114164829.tgz","fileCount":715,"unpackedSize":5404700,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPL4vCRA9TVsSAnZWagAApJAQAI5l2e8UctTwvVa1hxa8\nfH887EDuqR5I3zJ/kygs3maJLsyHo9rum/Xe643EwyouDJXy4/Ls344qrr89\npSDgTCAUxvL7abMbTEMgeBGB5BJC+1jyzyYW111SoLlEUm7UMBvKWvecXeec\nARK4gbcb/Sf3UCaoP876FBd+cJLfu7UR6lLB9w+ASi6/ncv+SAieKwWtxsBZ\nypm1KBkQtiFCNRDQfmFdRt3rXdPJGzPgo/afTyHYIaOcRASahCew/mvaoxm1\nO32wCCmZ2J/I6Xf3Nwgo8Xnq6Ue+edq2BmpnV1csuzeccUTSvMEEkHp6XMcA\nRtkho4fiLjvGCZx6A1Zps/IZm8JG1Dp8t9q0WUsOQ2gcS2kzT8x1UK410ddB\nFCEqiR0s0hRpBDri8lZCf21VJfGzmQ1PlMv5g8iyro2aLE4sKC56FqogUkBI\nPrYl68C3XaCJt+rQnv7b/3UNlUrUzzfz89NlXMVy5JENnxwitNnNmvI8SZPG\nMSssZ2QSJySrjt33AGgX2eTtpAD/PNMQ2AZj0Csx7ZV1BhMX+6Kxm5NuEApR\n1EaZKRPua4BdH5Jmnyya3fOSKuzxWls8xVJefLUaDXzfZWSOR6buNJSiqZbJ\nRkTZqSEPeM1qi7fpNhnvcKK+0TOrGtBlJPr5x9ROoKs3kpUj1sWSgARnt1fD\nTA8P\r\n=woBR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDy3ED0yXpILu1fDVkGubAA7RUISVIUGf1XGPcyveW0aAIgLgp4PIc32VedOU4l/kgsfzKmGMk56drZlbjHM2nKcfM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190114164829_1547484718746_0.08917055200056923"},"_hasShrinkwrap":false},"1.3.0-master.20190114212208":{"name":"@atomist/sdm","version":"1.3.0-master.20190114212208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f4e25e8130fedc58962f1d28e5016c807b49ccd","_id":"@atomist/sdm@1.3.0-master.20190114212208","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2f+nPib7yqj9Qi2aopA36zV19AQtGGxZO//ymwPnT+UDtrVh+6y6sJnsCjln7VdbKCXpTXMOFGTbQAHrBebF7A==","shasum":"645b37a45aa4d43e1abdffbf5b403497d68c9a04","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190114212208.tgz","fileCount":715,"unpackedSize":5404725,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPP5HCRA9TVsSAnZWagAARFEP/jylo3GNt7fjE1zL87Qv\nfK1rFTX/wnSnwsr+gs6qW83m6a0BIEGTtQJmQpuFUIV/r9R6g0+hqxTY+iQ/\nfSUmuxt0bME3h4hYTwq3mQNRTcWptItDScZLgcCj1clz/jSOXKgA8Xq8igHp\nrWaOOs1B/MAguZv68TaB8wk8Fyq217G0iwolRcl1tt5+dRQcmlWqPKXsc9sS\n1uz/qG06TQrOGah28KIEnjBeNrZxKVYLVXm1ZfD+e8FVZYEb1jCHQAtvh0AW\nPUY71UaZVZrZ3ZlXaYxGjaiCNP15Sdgd1Fh5Zb1Bx0xAhSaG0meJu3GloDc7\nnVXSQzD4nATgp0dinj16kGwc0MFmnMF9j/8dPv3Dz3Gqh1gauRgGtCPf0FvD\n8tJVz+83b9zMid/Aaf90m8JbJTmQ5hcy80X5rrJ8UgnI9/uBgOBmuMDnutSz\njseJpaDGWSNYh/3JOJYTfOcDvmMhmiXBAjdMSuTtAIxxVCpmCXWMel0d/LyE\nHNl/9B1hJ4Uv5fK6syHIIDPMCJRTP6t/tODZeC7s685DEcwfAdJGPs35vk3Z\nnHJHtRdf30F/CmS2ZN/vUjNWxgrzZeOdeFdPntEBi6VIEj6t691npyIp6SJf\nRK8FqS6rV70oaOL7dnduWFAVkV2zBjMSLIyjPyqp1T+EEMNfnD1p7S8X5Dl7\nVofZ\r\n=B99v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEsYJ6KyUosA+PKVcsWVkXr7qLmu8SoyVzdaJOPtR7R8AiB++BIUf0Y7IEFU2ZjtqqN5n3pXwxjZcm3ZsgLsZ3YKig=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190114212208_1547501126203_0.4468686442181191"},"_hasShrinkwrap":false},"1.3.0-master.20190114224136":{"name":"@atomist/sdm","version":"1.3.0-master.20190114224136","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"301c00796a122cc1c9b5fd615bf491296e20c92c","_id":"@atomist/sdm@1.3.0-master.20190114224136","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-refnXkj6PFVW7BZAHP0ry5v7aYuHLZ0mGzXX5pJLfrbp6rP2UYbb3O+6nhuh2NWUdJQTR4nMXaSq+JeuUE42kA==","shasum":"c6c7b9481325f329dc1cbe1653234044733089ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190114224136.tgz","fileCount":715,"unpackedSize":5404726,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPRDcCRA9TVsSAnZWagAAgBQP/1sVPXupowUGqQqgSZJw\nCkDvfpj5iwOwkew+pEdk7jWXTZjOcz2u5iSi3cf5gDX8/QML1ix1oBtLaffZ\nnJFomFPlhlakowxUzVlfOJGhqhO/dAZlRVPpqBcb+egD3ixOwMKkvTL/b0nx\nlWQiWBY1I2HnawNLSO9eHOvhQ/fLTb6BSKasKdYKkHQSeAiC24nMBUW4YvQr\nmO1vwKrTCeJDy7SuiYHv5cd+jUa4PsvFJgD3WL5O5rXDAUpQNzvu3nFc4/SB\nblLisF9MxFXCW0vTnZH00OJq3EyRHcfkyYKu3NXSAlqbA8NwiDbcn5uxPK7S\nskCSyZZ0jM3ifikNwS7C/LX6RXkL5Jx9yFBGP7yzqnMU42n8lZJwsvlrKA5O\n6zedcQ0dB9FgLjBBQCZ/dP7MSi3Og6FsEo3z5TH2QF/phzRPG8gTGrzXGBf5\n981ZNopg8xeZ717kyJuZr/wE890fFGRRNjTX3DUK5GJLibWmHErK7TlsAk2O\nhF8FSzqjsQ0FhhLmA9E3k3iC2J3v7blv1tqvr5Mm8zGQBQpzVoj9lDtVRf4y\nEl+6PudoajFzpbFYbffnWUUlX5Cachx92jMv1wN28Lzh4xuGTy0w7V2ltmW/\nq/tAB/dXmkAx27he/ao+E6w+h3f+SpdkLXNIOU7G1b6Q7VNsyxplir5jQbTD\n64vI\r\n=nLF5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDXtxa84l1QYod+FV4gLMhnUgWPA3G0GVM8+ObLJBRusgIgVgxhqfTtIVQfahbmWwMX1oJ+CwtsL89paVIp5k1+O4o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190114224136_1547505884052_0.3356095334794629"},"_hasShrinkwrap":false},"1.3.0-pref-store.20190115075711":{"name":"@atomist/sdm","version":"1.3.0-pref-store.20190115075711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"46b6b9b4ef838447a207935a211a496741ac149b","_id":"@atomist/sdm@1.3.0-pref-store.20190115075711","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-E1KX9Cewfy688WRJcz+9TW8hQuCv11l/eofgPzHX5NC6BHtHM4gWtiAt3Xket4HPOYjew3QYV0gY6iTkRT2NBQ==","shasum":"cc844e0402e542f722828335ea54f5f5d8528cce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-pref-store.20190115075711.tgz","fileCount":718,"unpackedSize":5409919,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPZMRCRA9TVsSAnZWagAA/Y4QAJacKE9cKmy9RfsVaJLg\nf/ceanKgw7bOVN8m8xKwTJ3k4/eY6SdTKhG4e/6sH+G04LvEpGBVrzroFSSi\nz7Zx80CxxzNea6+zlRDUfylBp4vuf4s3CnSY8q3vnPXRrWg5avUZql0iKXnq\n8hyZgs31faB4EPKNyxe5ijT1Lr+4cXoc9QtyM6i15G6M96vTh7cz2mfXBJfU\n3oeeUnkavjS8SR4+Ffrh+yzWyVU3AsDOKI4yYkkMPFNYzVtD9xji90CuQBQu\n8LDUK5xUEHLdxJxA3VSBeBqwjYNHBf2idFLFVBb+6t9P62LdL6QTnEgXv65e\nyl2KfhIe4BKbA6Tge0pluQh0QbpAyiH3eM6p1qXCyzlJr8X43+7Ty+R8/TQo\nL5qzeUn4ipI+szQQp+iLxaqMY3SX35txKKyvoGJ7+WmbiCZY0xbh+ngzZQcY\nywUZXpzy1B+AzvnqexR3Rp1al26vF61zTNKXrPUIUiZQo9eQ5+lTBuHwh603\nLltTkJWPSl2d/RrOVuZjXs+DdWN5b2E4XNDe6DGhtz4ibI+yUxG5WNeMEFoy\n0GkVyP2rEPXtOSE6HL6cjHdN5pj2ffeRQObeYEVXg/c5YBFh38dOB0sIdKq+\nS9M4ciQGhEG6id8Rw3ys4s391hOgle6kx2q+uSdwK0OE+ikYE582Nk2duU8N\nbFUW\r\n=VNki\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZamhX9WAieieIjKGdV/tJK+h1DwGs/oKVtvnGgIN+AwIgCdiHFhzwSZDkILNZH+aPck18KHEVSV4dT2cfUKM96c8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-pref-store.20190115075711_1547539216993_0.13269074223888389"},"_hasShrinkwrap":false},"1.3.0-pref-store.20190115091121":{"name":"@atomist/sdm","version":"1.3.0-pref-store.20190115091121","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"30ba4456e24e54ee328697874b800ecc30024deb","_id":"@atomist/sdm@1.3.0-pref-store.20190115091121","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eWffLav54/6TExRaPonwdkYSgMaGSuYeGTdKm6HI+VQiU4gXBpkEa7q6YM+7LJi0ZbcCYuCI2quFHvVASiex0w==","shasum":"cc4a2272d40b167ebee8f6280b1a3874eee43c5f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-pref-store.20190115091121.tgz","fileCount":718,"unpackedSize":5410005,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPaR2CRA9TVsSAnZWagAAcE0P/j6s0SMDK4HI6mFTn/0h\nr36weng5jYVBhXozS2lm1hJWbjAZ/QAQM42aucSKUYsVsHFsCCNWFh/FuWBR\nxO436rAXr8QLTqtmUqDT2oblmnlm1IvcOOSshPs5jtv1ebjYCuh4m2e07Sa4\nK5cyeo/V1DbasPtGqXxYXuHTMhZokxVk9IvDjN0Ag33yziRby6yFDhkkP2HL\nIIHvLlmJU29KSRUUv2vsWpsF4RO+BUZA5NMXIZbMJmEQr2RFeTk+haP8lqMS\nDpRgSOcarhmwOuVGapY6n9yYq2YVpCIhE2v8O2aTuyPB7TlgrT4atevLRXf8\n2GaYdaprXGVACbunkgU/fW26hIsESGVnk4dVEIScoM68dJP1qxqAhfTbTmW9\nE1AcMgtIslfZVsXUbp//sNjMrGxY8fAx6aeHYssxf8YUtxHOgBZM2JYFe4DD\nU3LK6XtMH8jaZfGzFk3abJaMHI3GORU5+TBQ0bTOvnNkxdTJ0yOskouVuddk\nhFHsOO6iAMaji32ZBNO774l5sCzLk8/QUqlZR1otkgBivfdBOP7YJRlp48CG\nyKmvIN0md4Sd/ux++kxVLUS2q2obgTsxlsCrPOkkl+w4vgXTccy0WONhOx/v\natnp/fsvXtL3EcjkRdcSeY7YOviemJY3uFz5vQRe8fyVADzOhxXO8idwT9pu\nr8/d\r\n=62iT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAQBWpuLgHJuhGxdenve7ZoCGJsGLtOoeGffetpmKjQYAiBHhwwXAlzcICTUv1hsiu0jgVpOOEEI+rEqFu8tU8oMBQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-pref-store.20190115091121_1547543669272_0.41192764632189593"},"_hasShrinkwrap":false},"1.3.0-pref-store.20190115102339":{"name":"@atomist/sdm","version":"1.3.0-pref-store.20190115102339","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b16727c7131269eeaa5493e129057afa92f4140f","_id":"@atomist/sdm@1.3.0-pref-store.20190115102339","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kiN1IGjVdoZ/k2FvMI2lM3Y/JyCB7tGr5LiJPYMXyjJYYiMfbZyrQE2O2nNlCyCW8A/8fuai9hkpIYmmHQNtHw==","shasum":"250ff9fbe8d9ec6281f43b3b05ef408238fcb2b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-pref-store.20190115102339.tgz","fileCount":718,"unpackedSize":5410006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPbV8CRA9TVsSAnZWagAAPoUP/1Ebuo4Cav/ojffLAF5H\nLzXK1LQTIQqtc2IiECa4Nn/FtSr0/EAxEqVo8BSmoMzNsoWKp6Ho+6uTgPXo\nI5b7QdzWCyoQCzbveVH6vEWhzZ2fRle8vOF0VQBOUsDHflKxsYBJVsAA8hAg\ndYbjGszYIgN68Q3DqtsPNbhE4flhmPstWRnNtytdt6NdEEM6lRk2BFLdahZw\nqj6Lvp2JU1CHpRooBJaTp2u+yuVARRBt7WSGeCx4/hkQ/DXeKMkrkEtqM3eN\nF/hU4Pho5VM0RRBa8om3YxRO0Xaagr/BbpzZrW2kGEte7/AePa0ncWsnQTcJ\nGP5BDaPxKAJL7iU6t+hSpxscIhGzBkRzR+FgDHuwIQRLT1c3rdv+1dkCqoC+\ny2Cb2chYBXgLYl59jCIE0hGmL1leBS3syZMccK+J5oxQaN9A57dMBQqBscVp\nb5alllEO9XaX//eL2kg3Bf78HlFJK1fV1gog0IWH8tmDH6ngrd22ajMROSxZ\ntNkoufBaOTFjI5GxzIrlxGE2B7ojgYy8JjD9p0NTneh2512jEi+3VbSQT4cy\nh8f3dOCgxYiDjclSR1cnfn0j2z9lqUw9ccVW1OY/I9byfLh0gPK2ORq94yBK\nPs7wr3zumJuOLzYfkOPzl2S1Sbz2u6SmUP4Z7CfuTJ+SRSQ9TxxaGYOETgxw\nPTsM\r\n=shDC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBLSWzIHIe7Exy3KlKYrtHik5krkx+VZ/nXe5zqyF8PsAiEAnPuhEE50vPynvUz0Yk+YrkY1Hzo3703oLfyGKiTVS98="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-pref-store.20190115102339_1547548028225_0.4748425660685529"},"_hasShrinkwrap":false},"1.3.0-pref-store.20190115170119":{"name":"@atomist/sdm","version":"1.3.0-pref-store.20190115170119","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"502c8249b06d2715233cfd5d7e7526f451202127","_id":"@atomist/sdm@1.3.0-pref-store.20190115170119","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IPeh5rFNe9dbH9Wq5SuEeHUHpi8M1++J32KWaF+4wasPoSQDooKJ4CdrNo2FGTjCE4USRuLfpHBm6sA1b4TMVA==","shasum":"2ff560b3fdadff8b63751ee9d25684c0611121db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-pref-store.20190115170119.tgz","fileCount":718,"unpackedSize":5410323,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPhKbCRA9TVsSAnZWagAAK/AP/1dvkQORUGG32I7J8CdP\nT3EurfB2rwsXBVBcJoqyWOt8PvRZVToKc1warMk5ylH47zwBN+5plWya0jTg\nxDgkAsCD4MV5jeUlkfjiZLHdve5RikUd4ph2f8u2zBM14epxOgm/naHEQ2B0\nQHKSUiN3iMWpVwbff4GPThlcQiyx2vD9IEhoE3Tehg+Sosim48I+iupmo4eV\nMSRfHcy9xXXW08CHVKFcadLSPVrWNncdnTNlbyoZpSAz1kxD/+9wXWjSp2lv\nYtxbcylpk+CUyxvisO+OW4bw8nRP0d2rLzZ0pyMpx4VBMgXp2JpriEM6F8E7\nhTEElxFpVskHXBhqoEX7bSbcmMAJo7U2voY9Uv1/4Pjme/01ArsPMb1T+2wr\niOvXQfpKogEYLZ3s0fHL0zIZpke3iZYWzv9EhYbWcH3G6lbmbCkdcW0IJkyS\naJ+BPOICGs6K0ONcRwhe7uqNqyFHZLV1lM6wNpjDLNEcmdD91aMbUT+k9w4C\nvqFA9z08NJ4BtgByewpYLACTbzxHsu26zT4GgLM4dRi8HagKiY+rBFP7PLYO\nZNc+0kCZsnTsAiAuMk2PtF7PK5lwlBUAqCee6pGuoXZTitXV4Iwv+DYjrICY\nw9RtoDJqj4dlqgfOf/QP6bk9rRxdfzD9l3neONka4GWvi7loWTgsc26phoxi\nonLY\r\n=9Lar\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfbtcaktReOVgA0wCjoAUz8b1ananNAaFjdL8obWzuGgIgG/JdPXTpJijUoDPq+1L8Jq32oDDoD5UQY4LfxxiFXos="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-pref-store.20190115170119_1547571864824_0.7416088846382154"},"_hasShrinkwrap":false},"1.3.0-pref-store.20190115171938":{"name":"@atomist/sdm","version":"1.3.0-pref-store.20190115171938","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"816112cf649ccc833671de1198542bc58bbec54d","_id":"@atomist/sdm@1.3.0-pref-store.20190115171938","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-dCRvaSPOYLljjysSNVnfN3Pfm93JA0vgOB+sLL8EHJrwDPfYGaYaNvBdeYUscd15myYN/315tbfiNYkfzVpVCQ==","shasum":"f8acc77370a3c0fc3354fa65b86085eb339428db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-pref-store.20190115171938.tgz","fileCount":718,"unpackedSize":5410375,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPhbrCRA9TVsSAnZWagAAY4kP/1rfv3JejPr39xG2mWHB\nBq0ekLFGIpNX8QM8Z0vo+CUjP865zncslvEOVWFbxHab63tGEnhYSiyFlbmm\neVf9S9FPK19Pplk42I/xZjTVUkCCOvRylVjUriOOwfQgsxSu9Xlzv1ZRsI3l\n/LurWAbCXZ0qJd3SCcieQ3Dblc5siMpDwDVv9TzgFAubzXYeOk+ClxchyGta\nC0Rw9vsCeMi2ubHipJ16w2V0rUEqXKFutRvAbLzN/99DyKBCyxVO33vBwQGc\nGhJrmjmrHUjux3VYEl8OWmCYMMIdQjSm3M4Re01Fx4IRE5soD3bsqeGI1G+a\nRQpNoR73MHt44uTEbrZ4J4OB30PLCcIHWPGbVCVErRutwb8wMlyAd+I+FwHY\nwKZTiExHNzMPJO/lcmV7Y1pliRKAoFLbQUu+Cb2AnV5O9/LWwMdl+ezDEZDi\n/oRURU9RAmMJbUE2gz2nAQf09lQZhowUPgzngiwMLcG5LQPso1dWs5DcoViM\nVlWtE8m92YSZhSQV1NPNLMLNtWVYKN8I3aypFgb26cbSSvnS5THCo69RR6AT\nJpxTceC72nZN+Yzk5Q1NHAnl+jW43Mi3upnAg+zIrDKgcGxJnfDd29NOVvtn\n+uxJ2nJ6wXJVP7Takx35KcNKctaJCWh+o48MTCpb7/osbtmaCwjHYvq/6o43\nOK1B\r\n=gOMQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFNjlv4KahEQI41uqpEInCjiD/0Sp2RQnA5V2Cly5dEOAiBLZYDvyE5telSx8WncAJkHet/ewsi5nh4GXYmxc6ADfQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-pref-store.20190115171938_1547572971004_0.9196827473335027"},"_hasShrinkwrap":false},"1.3.0-master.20190115175702":{"name":"@atomist/sdm","version":"1.3.0-master.20190115175702","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"42c14d16c529b130f55d05b7ca9750bd9d68ebf2","_id":"@atomist/sdm@1.3.0-master.20190115175702","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yHCQiuaXJDuaEu+LSxtWNyIPDEKXiSPIHRWsMNyzY86UJKaFqOwT4ahK1hjyEK+tDwkFRXSlcXJnFm3tYVsblQ==","shasum":"8e7a3ed25110a96269d327f495cdf09af2c56f79","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190115175702.tgz","fileCount":715,"unpackedSize":5408256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPh/dCRA9TVsSAnZWagAA9nsP/0O6y9Ob34Qrsfn5tqUu\n2YBmdSUAEASnrTsAor4IasyBN845fF+NCX6XLjDIYDH5+fJvfq8xr52N7tE+\n3PmdH2OAguyeHpu7XiWeGko/1/cLPpOj5ncQokW88IFY4vjURhPG7njCec5m\nT9LOo2ITY9zFZSlYoiXFeP9bMCwwYVKOyx2eX5SZMS7ziwxwTZqztVTV+iou\nn0A7Nz0/Sq/+7N1Yfz67tv/erE8YrCs7xyDBj3UMUsh6CsHajR2tpHRx/Gba\nhFa8zWu22vrWXduydp87VwHvsgXjO+GnXp3jXbx+GEs8ltARm35tWsr3X1u1\nj/NedhTzUn3lVH8NX6V36FXBx1AekztP7scx38iO3WqZNxGqUN0l+WUURtsF\nkBhkFLgqS4Dfxdk/uhR0RR0SMBqZF5IN1EY5Rw5DA7M1ndCpvCwjCxzeiYFz\ncCggbNchEfqpVmfh+4/6LKbIuNkPirvwWwsBpxo6yDpqEukUobMX5UvkaLrJ\njNwNqWdSaz1yxZuHZtmyhasMmYOLjp9YTqzA/ANmY9S+i22QLnuQpO872rJW\nrlBysdPomMkeaE/aX79Hrf67EaWhNfZCzSBIqizghz2Vg0qvFUvytzNaa+Ib\nMq81ZEZs67nDzpiEmz9y90qCJHDRp1XhVrOE5Ppg3TmV5Db8KJyAqmkwgeif\nHeBc\r\n=0fEC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGzspgGZ1M5edHzegItO5GRCXqaSSZ2lY4VTAJIeo6W1AiEAiRW4Lnhs2IJwe58DS0G3bfJxhCythVKfPCReNMga+YE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190115175702_1547575260861_0.22054835111688287"},"_hasShrinkwrap":false},"1.3.0-master.20190115180107":{"name":"@atomist/sdm","version":"1.3.0-master.20190115180107","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"42c14d16c529b130f55d05b7ca9750bd9d68ebf2","_id":"@atomist/sdm@1.3.0-master.20190115180107","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-g9oEYIT+JxXLCmoU2cK/W5DmJT3436KJZDBGP3qaQUZiRcXl7UUE6rDrdRbRL1B96UtD52Hc/xrd0Vxgmw2P8g==","shasum":"30083e62c4ed4a4f85e20489711eac20782de50e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190115180107.tgz","fileCount":715,"unpackedSize":5408256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPiCdCRA9TVsSAnZWagAAAIoP+QB11lxP4U8wSPZi3HMH\n802fr3Xeb1L/CD7u1yZhjMjdUTTOsFjIM3DpWvdVbGK6VPg5Qf1RVA2C+goL\nLCDGwnG+IuNM4Q8AroR/5t5PGahIqy1GxcZ7cySh5VeMpPxz+tnLeXARR2BB\nSL6GkfLc6zEwXSqCCOyUDix/cwQQAq72+ci/AXelaoJ37x7uBv/oaskVHL7v\nimEbvpjj6TTgxSyRaja7pv/62jpI2DKfzHB2IYzvVILQu6A2Puen3AT4/G98\nVCYBxh5KuEbwAyw4dOkQEktyJ6nvLxeqQyejKSldtAGS1vAw3mSM4Glareor\n5BryvZ7ffZztfHqUDxOLas7DrSRHIJcINB0BpNMFjEvlU7rh7kP/GVvd5zB/\n9pEEKkMfhSkcmRFV4efOdWkZJWYrIaS1RJi59udMJu+fA1Vdx0vtbuN6gD/E\npL+j0XR2EPiAt2ws869zuqb3cXfkC27jXq4Nv5Nc/Lt4G9kaRSjvU6IdmPXa\njL2btFIoBLtMmoSrdNsUDIq750Jjd3LpJdtP3DYQVVYQgctUHHaIxL0dpIHS\nbx2pAxig5y2/7mAuGCFg/u3Bh/UPaSUIsj4HVL1V4fUCacpNTr+ez9mD6fzl\nl56Ap0X97Y112aNBkt/swUBysZPJKRCcU9ig9tLye1CJcbYITpngfn+hdN/D\ndqtt\r\n=VNPG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBO8yGrSBkFWIE0O0j8wgjs9jPSgdwQkx83jIWRoah36AiEAmEPSprbGoXMQUPyXV3p+smctsDYp+m3E/mhffY9vKN4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190115180107_1547575452685_0.9808951612750798"},"_hasShrinkwrap":false},"1.3.0-master.20190115211119":{"name":"@atomist/sdm","version":"1.3.0-master.20190115211119","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2604cc56c0e5d1a2b6127c191d73581582c1130","_id":"@atomist/sdm@1.3.0-master.20190115211119","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YDmaWKRv6asjx9/buM5ygMI5yrsJfpSS+j/+4qTwZe2NOa/q+fHDQo7diTtvHB73JSs2jyny32MPdLUu6TqESg==","shasum":"b9ee9475ca41bae3f3e07623cfdada16464bb738","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190115211119.tgz","fileCount":718,"unpackedSize":5413986,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPk06CRA9TVsSAnZWagAArtsQAJieIiwsZlgTT6z7B8fP\nsTzECcw1rcKJeOscW5qm7N4qh7HicnhBfw2JZ5sS3BlSWsb2sFv3Xl75bHd8\n7EsV2Vw1xYrewwMLIfXXeWBqYx1GcDxkZVw8mtx/OFiJ93+gxyzXXJxxLSla\noQMI5bV6TD0tir1s5eJ3yIU5pbfyURDuGYR3ltgwMVrl5BFgmWT4jvp7YK/c\nBoW1CJDt7TYueTyH43/7b0pGDZ/nklyrFxf8oGHEXP4EO2eYQUGcOsHavFsf\nP0AFnTyLXem3U7pWDGOjuWyLMTVzR3GgpVO6jfZLCETVdzFSxKI8IECLIm27\nnYKbnGT4XO7HEvmtxMmIzpVVMe3HpwEdA+FkeoIGjvN6v8CjV78d4w1PxC/6\nCtWB0Rk4PEhvjq75Pl+kMBHQphkXK3G6sc9QloFym/Ajg+wQfpV8W5c7XHi/\n4MkqZ4GZw/o3Y4+lkU1HxzBthK0HAWS+i+3U/rtzyEM7r/xwk1dxLT89y1CA\nFH9WFbuuvRTI1tsTq93/sTL97zLK20wMWtoQROfGW2TyXf1O2RVA87wmjPVI\n5s3RnyDj8bs1+0ssNs+WxQQXb3+ECQ+79VbMOLLcwvHZ7MNpJfi5scb2xwc+\nT1sZOMuJkWwIXE+8ZbWDJym6iRwIAdbZS2lrC3nGPX/XAVjjWpJQYWRF8NHY\noo70\r\n=+8Os\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDWTo6BdT/Q3v39ih2L4Yms1Th182DZ92R4XIC302xFtQIhAK8VwBNiUD4Vm3ZBdQgwXnPuIHLr9joj0TcmonukhRuf"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190115211119_1547586874143_0.0801917405281527"},"_hasShrinkwrap":false},"1.3.0-master.20190115223640":{"name":"@atomist/sdm","version":"1.3.0-master.20190115223640","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4804e7818f0adc9d5ac0be726c8c3fef14314813","_id":"@atomist/sdm@1.3.0-master.20190115223640","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9I48mxJOvAG91YDPISXN7caN7hMoApqwc9QD0sN+anOZtMDRtjtnQZa5Lh0688+Yu6iVuXoKpX29hwNWpIhXcw==","shasum":"c38c2b4d3855aab9a6e6df8787ab87a11439dd37","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190115223640.tgz","fileCount":718,"unpackedSize":5414077,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcPmE0CRA9TVsSAnZWagAAAw8P/1wDFBjVckgu4gfQDS5C\nD+yfPIgrv1/sXz4+1rgqY+kMjMztvydD/i5pMkZXrnyUxHn/Ka7hHTen/nk8\nvwq1W0HzyjFQEsrAx8IMgAkitvD8BJrXmfWeemb407haCMNMbVEMr7qY+pQP\npwqnBpW7NuO+/7CyeQm2mds4H9pVuB6Mg4fS30SXXtxKmlHPnOtKZ06ZcnB6\n6WC8dAPLPU25IEP1Tr2DnZnEM8ipTrp0voV1HvlMkRl1UXo3b4rbo8W59VJL\nAP6bO30+IwSbGufJfBOLoDlSWK/KAdsF7uHhpQlj4TKOnTHzbaUblnpPXNO6\nNYRj3XggweUpezKKjRzt664NRIeCO53JROLI6tfFyJ9lUCI2wRIMIxlc+Uy0\n03UwPIiVBoB+YS0UvrHLjoKNbXxNR0MLNXERAONg2CV9UJGtAgsLaV+aSB2F\nqdiuVBcwvgBwbfGsrBJO86Nt9HL/NyC7kXZJEcoFKwY3YB5H0EvVBe0WTdUQ\nJS4yoo1IjBRh08B9z5fYEuk0rJMfU3nQu7exbvppBMNiybXhbK9fFzZi8C04\n7NjvJIm9LM6enu6ydAfp8L+aDZF/1WW2In5AdIDQfMkUZew9/yOM6QvH7M7Q\nYa3y0SLfu9LOH5CttCombg0rOF6q2cqPyum3jfZQMQjp2mcReD5aLpktpLTx\nWd9R\r\n=Y3tT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDSTpAcVFz0m7CuV/r4TVFfg0n/6FDE/IEudTipkv109QIgapeGO/FIOQAcbpgbxBNSUUrV6j6b45fIHvY37q5sSdM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190115223640_1547591987720_0.32339940802911626"},"_hasShrinkwrap":false},"1.3.0-master.20190117162257":{"name":"@atomist/sdm","version":"1.3.0-master.20190117162257","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2dc5788ef96b877c0908484626cbd3b46bc5f25","_id":"@atomist/sdm@1.3.0-master.20190117162257","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+Ic6Nh0LeR7np3G3QUS90uij47EQtL0CgY6GZ5rI7xJdts+BR+gtfJDCIpQsSSvfVBW+jBuTknxb7zo3Zx3t+Q==","shasum":"26ca689d1eefbe5d686141969d34df87a8ef0d9f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190117162257.tgz","fileCount":718,"unpackedSize":5414068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcQKy5CRA9TVsSAnZWagAAYK0P/RyfbnDuKm/xq84l3v5y\nT80deZOHNUpKz8em20+QGTlJEE3NXWDGhzQzMMbTUgqJtCs/GLxt9RlweBRE\nL+kMghh11zp6g62aMmc6pU93UfFW4Hd7ckWH3Goty/nnQcfYzuF/N89B4S1Q\nOxjzsN1olndK4pITyI5b0jvQu/HQF/4w4SEqWh77Eo/Pv4PdK6tiJ0O1louv\nRUdcv+FbVE5DmZ3mkZpdZZi3+E7M5tcDwis+YdwqmGA4EgPKjNKqdfGgXWf7\nT47rS+wwAAMr4YMczL/SI252dI9xUqM9L2mqedz3If7vVCEsjyLFXfGCxJd9\n8I6eur4EUlhUMmteY9YBUPkpmXQNqujctYyR6UlpViPdLQfyOLxbCtL48+V3\n8vtlOOwZiEO80nY1i109nu5xLhp4GO+2zqTgRg5AsJw8aiRCDdTlgXXNci6e\n6Q4yMJsTbNxXoXpDaTMNHjAmZ7XaDgCLNO1dWDqbOO+SYT3oMNW/4xltp8jF\n44ZaWe29OqfyeeixxcPj6Lfh7yr1ZTBl59OlzsMKUROZdyEg+WwpRt5IHhmY\nn+55kjlzoy3xSv7ujmfIgkDsGYuFWyweg2hbjeLHw6Ia30PcgGmaW7rs8Clg\nLlLq2LMM0+D0UdhNj/iFjbZIxyJxxKzcU7yFtV9OV7Xs2WeYRRDPQsnN8QJG\nng5Q\r\n=g7Gy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRTOu/khr7r324lAOwbu/0+oJwxGyK3bqBU/R9KJd7nAIhAKewuDnJeg3XAZgFizmgj8SA/qTgMrIRMdInW6F+ZN9l"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190117162257_1547742392256_0.9269426796139653"},"_hasShrinkwrap":false},"1.3.0-master.20190119144619":{"name":"@atomist/sdm","version":"1.3.0-master.20190119144619","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"003d127049b7c81b2e90998f344b86dadc31117b","_id":"@atomist/sdm@1.3.0-master.20190119144619","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-96Sb7W1yMj6RvJsodIvqcnhVvjI38ouisFb9QV4Ph8m02stfuXynlbPR4cZgZF12wnv3A/oOAOMXWvPzewOxHw==","shasum":"f1fad34abc61e3e197b12a8f203bb721d5df89f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190119144619.tgz","fileCount":718,"unpackedSize":5415913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcQzkcCRA9TVsSAnZWagAAkKcP/A73fpl1M7T4B0LUAZ7Y\nuctaXBL39LHmSZolVvsMf2N1pSYvfjGvtgoYksrLQZuEMhxUFf/oo63PV4Y8\nsF3V5UH/XjndMOmGJnpOz/FkPdsfPYeuR9OBJAHVXEmU9IPUBWvE2DN3M/pV\ntO0cTVJhci3BL3hza+nhShTtXlI3t5Tu9mWRzajKNpVBV53Py6XTqcUy7OUF\n3XEzhXEnbTMCxBmC8WQbKWEPnuEnoq3MlTvRYi0tEKv/727A/qb+19hRXFc4\n9ZnjyYY/fULqXE8niNgNdASzWOkdRS/8o/07/9WrvBhRIjy/tn9FB+yZIK+9\nzuG7gwhzNMfa5m41XeePnm0EVlSP8+WMvbWESww2FJctfWb4MwwqRuchXsxH\n+uxdoPrrybh10dz52HvfhYiOgnW8Bz71ScMLrEmHBe102u60GtF+vGop5HvF\nzQeg3VjeFNPV09nhihG+4TAS+IlKSzQxuom2rY8VTgNjqGV++6kGDSl9Eq5A\ngbTWuglzmtNbX9pauccVNx567HPHeAt1x7rrXkIIDz1I6DwrC7kHAauzwwEl\nHfMPfAeBQYGt6stdblA8ok3ke7E4bSCkyoKfjpGgs0lOnk9jE48zge0GqJZs\nvUJuSzf/+F8kMBaRwdIU1IpU8Sn607vEnY30aSx7tSk5pw2A+2/THZYE9Syo\nxStA\r\n=aF1q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJfjwPulMsHqMoRuJ1nJqGpa5vmKmOhsQPEHM4jjvWnwIgGnIcwVYwqU+gpNSwd+b4EwmQX8qOMpbjFyzaE2hL5dk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190119144619_1547909404151_0.18257565047468538"},"_hasShrinkwrap":false},"1.3.0-master.20190119145642":{"name":"@atomist/sdm","version":"1.3.0-master.20190119145642","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5761b01b0c74b64c49553841f3d02b8ae972c1b9","_id":"@atomist/sdm@1.3.0-master.20190119145642","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tXCxZAMl6NCt1oDMMLLNaY3wrqTSLtTsnRZBJTbK7ol5XA8LabXowSdU0xSxHKKCTegFNxc2c0AUM4Sk4+mFEA==","shasum":"92b97158df3ec9c42d10500216017dde8a927681","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190119145642.tgz","fileCount":718,"unpackedSize":5416037,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcQztmCRA9TVsSAnZWagAAOyYP/AoR4nP7bzbU2URVY+3+\nLtLdsi/jNbJ5Fob2/yNLaC+32BrkyXINK6q09O/b6nPMRzIQpujkQ6AsmC54\ndUMghAYnXhvoGXBeqvNvwNihkzByq02h5I2ZUd+h+uIA+/fv+4HP/Vqc/m4r\nTriLfWHIxW89kZDki+YkfO1EIjZ4pwAB5WHLloXUSlAKugVrabD/5unBcyu2\n9uMkml8150i9iDzG1PxIfmQfgBj8ZfoGyIgRzdV89kVOAMZpXAh5IWoQO21S\nsrwbjSSMOeYeOc+eZj8KpC6oFvkCGr8PpT2pkvTUyFDMLr9y7LySXJPqGauv\nvyLP3SYTfgyEmeFmmLRlJid+0z/yzPc6zNL0cLKMeXLgQZCmKfqsWyrFYaD8\nY7W2M2g+B+YTRy7l+QOWMM25mypOBou+P5+ucD2l/+keIE5Gxd4GXc7m1Pi0\nMWEnR0/3ns/38TMuEuJco/+OXHEKhSSfl0GTajSRTLlNlsgNZZL6maUmwad7\nmoOKFPJeeWoMeLw0PK4QQ0EHM56dpy3R7RpC4UAPH02pky8XYXyPE5eSMiND\nWodzN3in/jYWUz6F1v4B7f/sHTP6txCHrXnPKwrpHEAF6Y2UzAHyYdyjaCP9\nWWIJYFmbYBgNDmBZ/a31ddj7Nh/5dVtaQY8UyVQ8YrbJp+2wWcDAV5XDhsPI\ng9G4\r\n=It70\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDJ5ica32WGtGbK1224g6wGOrRcYQ/VWvWGAiHRXH+zLAiA8daPeNQD79PCf8FJtCm5xhYh90cWQ0XEUun4rojAMzA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190119145642_1547909989401_0.007887957846073768"},"_hasShrinkwrap":false},"1.3.0-master.20190120184307":{"name":"@atomist/sdm","version":"1.3.0-master.20190120184307","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3a7dc03dd147936ec20dc1ebb989706c6136ce95","_id":"@atomist/sdm@1.3.0-master.20190120184307","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3i+xKXbIOKKAiUlNTIpaMwGrEP9Ww0Rh8Zyl87ODIa8NPJQXiAkoEFo5GWUsn+B+90cKZ3YrkxUrrzJLyWntfA==","shasum":"097b0ef33d3c65e09075f60a4d4223331755890e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190120184307.tgz","fileCount":722,"unpackedSize":5425401,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcRMIeCRA9TVsSAnZWagAAEW4P/iNFzTuHBqevhHJSLDFD\nNOOOZZkqGN8JD21/SX7NkpxfiXvCzuALxevBJCf/H7gxIl1pIZHdvNllls7A\noTUNwk7KCsE2XwMnnaNYitTg9cLDX3O02IU957pbORibM8tzTwuVG4rS09K+\nUNvU5ngjIMDXU7LmoAv8V0MCDLnUoKIG46RoQHYcbXIGrNW6xO8/tQ/ko0BU\nkG2qQlz9wqul0k277GBJJ1BkC1GzW9laqtkJTsduFmEp5s4oCdsAZZ2Aof8I\nk/YA8vov+KDYAGJ8yd6WO9D/nvWiWiK9Jv5bFn/6Ti4O2F30g844q3Gc+BCm\nAINfyZD4IVosNrBepiWdp+xoF2LBoz5bkwncONAvWABhk5SDlRqd5mDX4Jp/\n/YdSQNXAEbFprqXmduAaaVxeKwX3jkKe+bdknLz2X+zRTUHyNhG/u5NdnqbQ\nwbrTxY3K1WK2al+EV3bVLdwTr6TXP8Qz3GMzY5PDhYT3fE4mY87IVS12TAA/\nulJwYoA7drdKn4P+4U/V9fJa5tXrpIYVIYEQLnIRM8hEEnh+Zb5Er5t1hePM\n1RNKQHHOlPKMVfWM1ZO/jJpFfGs7nz0JbsstPcMenr13pXEDyVLMqX0QUV1B\nALtMaNW7iX6ld+SBYiPsQvWAA+yfGfuu/dfAITwLWxrLyNOIvZo/7m7rh/aY\nV3xk\r\n=5T09\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFyAz2UiC5hkzCrRY1j65e2O6dVVz2P+SOukOMi0wYmOAiAHcmNyo2+LhOAaq1p7uwOKJbTr1UyopjWuKoiptdII4Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190120184307_1548010013169_0.12191538350550135"},"_hasShrinkwrap":false},"1.3.0-master.20190121093726":{"name":"@atomist/sdm","version":"1.3.0-master.20190121093726","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"efa8751fff0b9d8ea86cfb3ab53f70796bbc8e39","_id":"@atomist/sdm@1.3.0-master.20190121093726","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ECQWtVf6KgHUu+6Tvy3kaU3j/MzfG7DymBC5/MrZcf3lcXTdhcx2k91NrP+4F2a0xdzrsyuDLBg7w76HXnWvTw==","shasum":"5e7e12857dd9cbaf6ffc07ac547c28c5c4c37e50","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190121093726.tgz","fileCount":722,"unpackedSize":5425502,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcRZOXCRA9TVsSAnZWagAAd/MQAJb7+ORpUf0fIdhD4lUw\nmxGsDG7hY6KsPtLw5/nCIMW6+cSn0sn8NWUGPe4mcoWd/aE1774SvZPvbhtw\nVAlYuxfLTsmeM1f24QmMpfkjwAwxmA14uzC9cUU7zG/O6ROOPJe8/XOncy+7\njjlIVM1EqGleHs4AhaCTIez6N3v3aJpQxXeUuNLmjhGkV3eSmFO7cElJKXx6\nAy7WFzazB4l0g+RQ9wHTYrVtLkvOIPmx6Mq2OVcMO7vxl4k0bdoK2W7q9UlK\nRp9z4PYimAvIbvpgEt9vS44kB1lRHGri61z9wHYWtG/VVu10mtvkCAQP8Xmu\nPzw3oxbrmWXSdCv425XYoKKZ642aOqkVHmLLM9Ak0X0Ev6N9bKffZXU0H/f1\nNxsbAd7AD2TllZif2i7R0Y3WSoj5tjw8cGPkUFCl6rkll5B28ubtdm/hDYhf\n+SiEjlUxYAqUZ2ttcOUgAueFRn2kGt5vZDZ4PM8aa2WROF3uNUrR1fy2qWdz\nlc7hLek7+5cDXMl48eNNyiYK2eaqLo/FvIxuiVkwOzD69CB5h/3jB9rolv+N\n+7Ix0NXd9tKGN0C/Q8KoBzR9I4lA4mf2M39a20+ANFfracRum1VplZoIrSad\njFQ064f4qK/C0NbsUiV2XI9sibwZgHLVPtqZDk5dOGmuDsEElWIgUwaVrsUD\nirK2\r\n=P/Sa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDVIN3T735sy/T3fMvexl+e2GJUxHo9+apYeZoFJfZWMQIgZ1n8D9zsVbJANSoN8sm+YF/3GzdwiZ9MoL8eS0D3n68="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190121093726_1548063638233_0.842457231221305"},"_hasShrinkwrap":false},"1.3.0-master.20190122140855":{"name":"@atomist/sdm","version":"1.3.0-master.20190122140855","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d905b8e5dd40605633d3f190a7293ed8fa845bc6","_id":"@atomist/sdm@1.3.0-master.20190122140855","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-B3Fh6ZZ6g6Q1lB7dKOL42RPRuZxbFgUF2lPZEcXmUGcARiAjMXxVH2xGpTxRkk8Rkr96fV7UXbhjpeLGR7G4PA==","shasum":"75f446a9610dd2dcbf10dc8b9fd9959b7391c966","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190122140855.tgz","fileCount":722,"unpackedSize":5425953,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcRyTYCRA9TVsSAnZWagAAdH8P/Asd7xfkp+Byw+At5mst\n5W4i3irF4G62cbf+TBoXDgbLbfZUzTrFglsYRqx5ldw8kiEdukVlG/UBjCyA\nM3cCDPWX92JsPhM/Eb4KYRO8jfvTssPuPri1Kd87+ee0vzruzZNlK4wWzr1A\n7rkMfyiv2bnl56tPTLaBKSF0DD4wrrc0dp9qlv9p2AqR+va3jl0+Qa3HJ19c\nt0+kbT52Hsksk3TWArNYMWdcf/1OV0Y6v3WF1/blxT+7Tx2l/rqIidPAiop2\n3szogsd6zPoH+YWmyv6b99whVjFXTP9YmYXgEKrtHTohxjtlgnNrIlvrTOel\nH0AFCyinqDQ+PAoRe/Km9vubp5eDMR4QL4TOuDQRcql7k/mHuHisTT1UiHHf\nhHufHfehybhlNaYTCkI8hrVcuVAn54eoygMDjSslLe5U0CIdl37AQDLrCkO2\nfZCEAakZDY1U4sb/5k3thmL0muunMYjETAPUZcjrokY3+gXoWbMQYsqs6R6U\nD5juDXCioMYpcc5Kqwro+UobteSIFB56PtGKkcmOg+AzOeOvlKWp4KV9r+t2\n168Do7tt86XCG8G1ErzDwTr483NPO4FU+IqsZixy1kvy1uB5WLH4Okk4bwMf\nbhCR2eL5cRh5FSahzEcbcg48eprqa/3P6noRGH+3LDWM/xrTQfcaVB4nrHeZ\nLzyG\r\n=qWP9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDLdFaZ7GXAVC27EBQzI5n3GZ3aKv2opwLO0x0nmf2LDwIhAIeOE+5El8x4qC8c3F8J6X8rBOXPtBU1i3hqg7KpmTSL"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190122140855_1548166359337_0.9891337085519889"},"_hasShrinkwrap":false},"1.3.0-master.20190122142907":{"name":"@atomist/sdm","version":"1.3.0-master.20190122142907","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8af2692221b12a096079fa430be73b81f2b06605","_id":"@atomist/sdm@1.3.0-master.20190122142907","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Zmk5HeQIsYPoPpG4nssXDrrlmafoZeookaMHHPdPf4B1YpyvuJAwMNMhhT5O1srOJEuQgeVSNUtwvyeotCOnsA==","shasum":"5080fbfd0eeab9c48d822b9fb4b74eac6dcda27c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190122142907.tgz","fileCount":722,"unpackedSize":5426036,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcRymqCRA9TVsSAnZWagAA1W4P/Ao6WIZzi7Uly6wVHCaM\nHyz7e59ADIon+nWbogZ8rN6pC6UXfFsAWlhVg71y3dpOSfdLXVYr0aluNUrr\n+9QzHrjRF4bk8/2SbG76lvNwXe6RDWW8k6yL5LKyeL8YbrNn9TEUoW7ZEqCG\nl+3QGcPFb9GGVTlhL72AomhckifaUIlGHwWAvb5U4kJEawQdzyStzpoZcShJ\n+fOBczZ2nNn2WZvLvAKbFqdZnElOKXYzTi/Nl7C47azyhElhD+/MMUOvhOs6\n8qtbeXudNgLgr1Cp6zWq0/UI9NkCqO0TAfUaEhV6byp2KQCrWrqfbFnLeGYe\njrllApF2lbwPM8VNhFGVsAtmzYrwrT2dYhib6qCr7gKdYe1DwtLqvlkIJUmL\ny6p4TOWh4ElGOOxe0pJBo3GN8mz9FhfhC6ElVIy9eN92uNvyfDF60Qz+6Sjp\nGTqILiALzTsYWAQOZI+vegJZIee6LOOZTDuaRhmWubjwN8srd93So21wU8xC\nKVg8as0ZkFqJAWIYHghpdrT47A0VVFDKd58QLBHFy/tqePycEB5PACNJBzJA\npFmf13KFiRHTM8737xBCequfAc9jqn8E1ClJgAtbPGfQMdYj78BV8WCAUEIZ\nO26lA1BJo1Jo6paeG7fk8Ty1j4NtvO1nGGQj1R2HMu+9rrvqJfpqWDzM6JMO\nzAQw\r\n=wtK3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDPY5ZaVRyhpJvbZgbSp8iTZOdVJ8ILyBaen03tzW3wqAiANPMxrEs/bTP5kqONT0wbBKsgZ/xieVl1XwjIyftRSJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190122142907_1548167593832_0.6605061093257591"},"_hasShrinkwrap":false},"1.3.0-dyn-parameter.20190123102941":{"name":"@atomist/sdm","version":"1.3.0-dyn-parameter.20190123102941","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"90e6e0468af0f23197040d754390fc3ec8d500b3","_id":"@atomist/sdm@1.3.0-dyn-parameter.20190123102941","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-c5M2vOxdJHdarEASR03IZjzjsfIOXl0zvY57XVp5YUp7gYCxbbxF1Rji98QNaD7KTbCZUrvIMCd7OBklDB61Xw==","shasum":"ee4366e93ff77c8f37a9be25b51943cc454abe6f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-dyn-parameter.20190123102941.tgz","fileCount":725,"unpackedSize":5432889,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSELsCRA9TVsSAnZWagAA+T8P+QBnpmcqGd8yS/PNkvNA\n/CkWI/0E/3rttrYnqSUcEuo+ELy57zbaVZCfkAag7EXpsyIw4TXipefvmJxJ\n3XV9kDakaDhYzpV7lP11LBq41UNLNQG3oUjB5oQlFEDjUT3e8cMHDFA7uPRD\nlG+aK3zqHLwow6PQTH9ythaVl7CKas/OtaQNsx/SX9jRPhh4c7O55l+5URVI\nPwBFmh+G8y2achSWihLHSj5rd4TosWkFQQ52GAC0FgVOlWiMBcPzN2/cDhKe\nrxcysLN+GmBmh9fijZUfW3qpnObWsi7cLclb3uiDvXdiie3f/dzuQ7CrQuX2\nXHuKmlFs8oPHCGSotcwpxv/Tv2qjedfY3LTqnQERtEp0r62ZY5JDu5hiM4MY\npeNto0mLNI9yRtRQXASFVc3tPrpzoafHqDn73NhVZF5oFiP1XUs+zwFBcqXf\npUJJYI+G+O1nRBGqiJ4o9eQ6DpYomAL3a8VKI6oqhvE0BYjMam9MYHGjRb1u\ngfbpBZMbBmT6XXJF99fXt/KXEpM7cDZPuFbKNuzC0k+es+7+0N1TI/F6hgp7\neECU87S2chXBSai7zixNZ96mtt4kBY248ZgfNbDeYSmWxDe+wYk0bXhibqPe\nWLNDT+KG1RtrG40uu7MM7uHasdhgSBkusDJPuMi+U4bkEqF395Y6XY3/d8p+\n3XNj\r\n=2pKn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE6/++thaxFi3OoQuNmpuQmqUP1Uh66GsvZ/INRklyjSAiEAtULTRZuzPvv+x7Xpe1yMvUILuPkehdjdmsOmjnfK3OM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-dyn-parameter.20190123102941_1548239595794_0.1335021233267888"},"_hasShrinkwrap":false},"1.3.0-master.20190123140058":{"name":"@atomist/sdm","version":"1.3.0-master.20190123140058","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.2.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.2.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"657fab74968bc59d42e355635553c4f74496321d","_id":"@atomist/sdm@1.3.0-master.20190123140058","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-K5zwr9xBG2VJ0EA6IFCWQp+hzYtOgP4qrv7CxcWP0D+w/m891elMCjV6hQ7/AiQzYTTggQrM9saXG6ntLIxMLg==","shasum":"955e7276fe2f7f692e2f0795c92aa302e1e8fc48","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190123140058.tgz","fileCount":722,"unpackedSize":5426142,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSHRbCRA9TVsSAnZWagAAIooP/A+h48aOXS4efSd4ksJN\ncOCN7o0tvA0ST8d0O41bSoK/6Qx6/RciqQcOXyzFFMRRoGDXnCl2vrUKEK61\nO2u/Te2LeHtSK8pRwFeQiSONKkI7k3lQqxbGAE7EGbJ2HsWizZXovBO6A0bW\n3/C2/E4inix2ACdj/0eEKJ3hxf8nxHKfpXDwYpmboyfHAL/nQ7U4UWEGiCwd\nooZLjU+CJ1psmGuif6BZOWW7nbkGi3RntqhCxKJnlHhifAsp7pD1ajssTRlV\nS2jqXTPgo8hjX+uZkCmPnHsO58ZBJ9lCfvJ+7ZQaO0AglL0LM7hEiBZ+8bN2\nea4y44GLyy2p86jDUZbfD5O14hg9x2yPxfyzkhUTK2K2zkJbecxQbG2ueZri\ndJB7bJE84/bHFYIr9Pa5ol3ThALjbFTxf/UTtXpV9ddSR1ud3ajUgOkMpxM7\nRjIQxlRuVGYwFX8z0uI8KEDVzJH0z2vr0DphFCuk8H5VSwIqc1IO9billMTT\nLhp8utk06Z6ErxAZWB2Dsu46SeABbyUZABBKlrWxDPzYeNt/6eEhPjm6Fbhi\nXNhuKDGFgdw1jZxdWLNXTo6okumIk2cN4S3eVarnnuuVRXpVFt7meF6wAEDU\nm+LYNgPB2hjsJJdcR+7GZnkWIA5VnrgMjJf78/jFSXu8nJzQm24AGLps5Zod\nFy4O\r\n=mF51\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHSGJbZs22P/hrOnuh05x+VFV0WVWiNix1j6GubLjtoRAiEAzUB56rQrLBu3lCB0EwJulnRQumLEsbI1r3ticjQSuuI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190123140058_1548252250719_0.6854081857644205"},"_hasShrinkwrap":false},"1.3.0-dyn-parameter.20190123145143":{"name":"@atomist/sdm","version":"1.3.0-dyn-parameter.20190123145143","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b055391f1231d378f2346c0beaa6bd5e3572cbe6","_id":"@atomist/sdm@1.3.0-dyn-parameter.20190123145143","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-O0CVOO9iG+QS+LiBhyQ6oAYBm78dD614SLyW3ySafSZXIHts43KYycfth5nJO2wFpnlje9/F5p1uLYM7ULpPIw==","shasum":"205f40229094765bdd98d08bcaeca9b53ea0e234","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-dyn-parameter.20190123145143.tgz","fileCount":725,"unpackedSize":5435738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSIBSCRA9TVsSAnZWagAAclcP/Rek8T6gbz+1AmztXpnh\nkL67KT8Wolv3UeNQb31IBsXKfnH3zfXgtk8hxNLvZZsBqJXH8P5sjpTemFrj\ntI4NEbipsbsLpZOE/Swc/xAuoyjHMVixgCIpAiMCORnrcwLKe0HOTsxHZqr6\ni9L5sSx/Nw2Bpm4QZqgJv0+olSVmzs3KwRpdn+SacEkDEHRNiXvUSeyTPPe8\nHAc9jJsisT+FH1F/mc0sWDAOX/1l/vw3rzNQFjsu+VCiAYPn53D/X7Vb2IYE\nxIQdpCDoskE8ltFpKwwfPn1SY6dSNShkPUm77rJGdwpC4GsvtyMdR5DUT4sp\nsZEGGlnyoGfNUPQuz+FqNGggCRGFJZyidY03P7h8EHa09CnFBSqOWJweIsqp\ncdtB31juLMCYWgYMwE7ZyONl2zqmnPSYb+CtHnAB5ANpKQ3v2s9zY/x9Fawf\ntqiFkvEJgy+BwOV1ighJtB9fr6fZfbQ4iXw+kFQycu02oZHCIzDFLvHEnPy1\nG56wrcPUxtaTVDjXndoggzuDXglYGHHxUZCdI2u/j402EmqkgQSK7BaA/vDD\nNYyGVexZUvPItf9SDdycqkFDqOq9IzO9YdtyB7jgu/19x/pgG2Hddidizvyn\nearyWI+sAnbSliIlZ/C13AJaI+6ImQBxyDWMqG2x0IIMUAGX/lJ+8Pyvt4SD\nziwJ\r\n=yuMR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDB9hcOGzd2mNkPTgDxieQQLMmxOakCETnal1wvTUPd8gIhAOZ2B+UisLpEioBf5eSQQrqLlt0LnfzE3pzz57exuCmd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-dyn-parameter.20190123145143_1548255312975_0.6527387225638439"},"_hasShrinkwrap":false},"1.3.0-master.20190123192146":{"name":"@atomist/sdm","version":"1.3.0-master.20190123192146","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7258b29a40a3ee1dc9fba3b140f0d2c4fb9a03fa","_id":"@atomist/sdm@1.3.0-master.20190123192146","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Gwb025UZDpOp2x7l9lBGRNphamkmYF8/QLn3VCENP5oMteX6Lp1w5+4B1JyZdvqfCyNY1iL21sx3cNOTTH60Sg==","shasum":"1268fd07eecf51f5e9fd8f618327bbfa6a6ca182","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190123192146.tgz","fileCount":725,"unpackedSize":5435837,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSL+4CRA9TVsSAnZWagAAo5oQAInsYQa/e4aKYFc50fxK\n9bn7GFNVuY8Po56kfabZrVzpVrDLHz6aqSw4rC/OlMl9Lwg4bG+VV31U53lD\nDl99GkOeqHZ2r1Z52FYwH/nW0FF7WxzdSqe2qaEcm7BoAS29ULgjcHBkXIMl\nLg3C6qq11ZfzKt0pKCFCMxemMA2vhZnxQrGvsZh/MeR9/xaw28Dr6H3m8WN+\nBa1TtJHn7wDJbDAQaIjV5F6jnLL8NzKgoJjsAJAl+YTDjAoEA2QErXuXy5HQ\n+n2+dnoW0UFHOExafCwDyjyK5NGfy9EaNSWL/9TRn9s+Nnmz17Q1ppdivDuS\nXlatZr3UL8fjQ7hG0We8sHUpWQL4uvusoEldnYkqPtJsqBW5mytg6NLHwM2c\nfMReL4mFryDlqsTERsiNMb7jbHMT4xEamRzqIOmNcbyW2i4P6X/x17M/UsTB\nJaytL5V0I+RVEFc0qG40dZOdF18FPlr8NOh7ShcmWFEhd85dvoPkxQGUqHOp\n48kcQ8bfOOwhsVa9Dk5kBycnlRL5AaCWG3P6+snWL+M/KZN/5iFiCJWxdIjS\nKjPjRoVuAXcheW4SGANaAXmNuT/QUeaasaNTSsa+qqCmw09nodEsvZCwz7Kg\nQru3W6rRI/Sw8HKHLq4Zg3PN7Q16IEy9C+5grwrOXX+V45WYrjp+d5wzSMS1\nMpBh\r\n=QOKz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCBNRPcGygrZmxWOBr1D7eO4xsUMci70t4RYzWazoVCDQIgXAILSm9reDSY4MYQJcaZkbupksonlW+17XHSfgt7cUY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190123192146_1548271543229_0.46292458582191776"},"_hasShrinkwrap":false},"1.3.0-context.20190124003249":{"name":"@atomist/sdm","version":"1.3.0-context.20190124003249","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"660ed8c37e9d8ab39b5fb8ed15264f4482a2fe9a","_id":"@atomist/sdm@1.3.0-context.20190124003249","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xaN3ZUuk7wUOYSSasOVYK4Y6AMG84n9dOgRpmRA1suBIPYNWBRHy5QztfPITK9paquURkuUO0V/RXgk6mUm51w==","shasum":"b27e778b1c5530b25582ac03374ebc8a308e8078","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-context.20190124003249.tgz","fileCount":725,"unpackedSize":5436985,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSQh7CRA9TVsSAnZWagAAOaUP/jeuY4ojXas6Utk/LLJa\nRL6p5dxhq3gq790ZbhaLcZLAE593ht9Aj0eI4RgUymzxZ2XJBzcY5KhiZIJ1\nPjwzx3m8T7XIryN2hLyMiKFeOCeC4bSyj6+mHqs0WowaErr0PXnKhb55MGez\nXyZfbiKgW9JgsXkatZ+St9517CacWc47kDcnCwW5eri7KUZYLglEfcOylaEV\nmwPPWeh2URlD4u1/TpNwdk46UH3T5LUdemi3uqd/Ig3xrGm0kop+xA+UFOX1\n5zFxb+LGTM8RYnIS2M6xbzGPReiqPiB2YOcLNhD2S60DzRyy9s3buTh498vL\naGS8T1lztBhrEaB5EUEhMVC4egGSUlkLF4mVYbZpVIp35AjS9qLLEc4Bg5V7\n1cV1I2au3h0JFjs3w4oq3yoqlbtve2iTxkx0gXXyRZpf3BZqQOnVJUIwYBRG\n5GMPwj7ElHWujq4jEY8utZgSvhk2Dohm12tYaUpuy14H0y2ttJ2Omz0AHzga\nh/GrLViTXeMprkiBOMSn3ZuRl66earmPnI7Sha2ZICCVyMXxO3H6CNu0vxaX\nFTy9taKpo4E7ZHzX1jaCy45T3yC3MYm6V7Hs9mBiB82xCMJptjcbmA4EBjFZ\naSx3dleEObeBBWREzMosPgKID+olLzZ/4oV/Bl1xXmY0DF9btR38lJq+ggkv\noerG\r\n=58Ek\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFt/qG3gJ0/XaVMMLWQ6OddPRE5GuBTDjtLYGI5eV8jbAiBR0hyl9JeCFWKVo342yekcbaEQWL/p0U3qXao6m9pmwg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-context.20190124003249_1548290170380_0.5161634037840468"},"_hasShrinkwrap":false},"1.3.0-context.20190124005641":{"name":"@atomist/sdm","version":"1.3.0-context.20190124005641","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"81c470168676fd8670406b381b7379f4a3687661","_id":"@atomist/sdm@1.3.0-context.20190124005641","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bvMZQWCL1lhYrM7FAchC2IBhASSBzyzBEmAHkV8bP7p0khkWVr9nEKwLkX5AV9QBpTC615OI4GqOe5UHPOq5GA==","shasum":"448523baf9931123a2384dc1e3a2e0688597675e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-context.20190124005641.tgz","fileCount":725,"unpackedSize":5437258,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSQ4ICRA9TVsSAnZWagAArKcP/1WiFeZw6bB7b8NpEISi\naeo8JVJS3ZUIUw8Qm7sQnqZrJrcrNFaXfQzeeEg1ZNk4r54fmOWt45y7B4Fj\nE7r4ZO+UEyszDDv17ePAK2juLkwL4nfLXA6Dn3raecQMaLRWb0EmMoriY3o6\nMPVYrbdejt8TunnM6NhDtrBWeSFD5bo3mYhfT6OkUi+MfkLj+kFyDk0+PQi+\nHPBUEsz3h6KIN2CgwwxC2K0f5ZZ3BOWEKCeCyEcBhhfjZaRHV6vdz07rSM/h\nnYXds6N8KlX0TjbV8AWLElBK/nnOKn/PW75nKXeuDVLqWLovwdcMsIJuTHbc\nY2ys7QvrLF1q40I3Mmy6nqwr+vA2ukzqEnht2RngJGmL2oyOrGuF+3BxqK3r\n4b0QtpgS4KSnzSzU2raEL+4JA34iSmR8kzOtpYi0MQP9M8O0d3QDdu2XbdnM\nwAOnGsqWbQJc/mg7/F9/XLFJw2ZOYnTOL0dyRKfGWY28CbgndXrS0kQSoonA\ncowIKkF53tJASoO+RmbUqfTYEcqAQ1kV42kmRevcAcd11zKRv/DKlzCkNYtV\neoEOzoe+Cfd781zkV8rQlJruFEvLf0y7ga38yeqsWkwyKg+ray94QxzW4fOc\ng2DGDj2+k58kqO8XYPmbGMHVYU8H/cUQvHM9m/999G6k1e4xyZ2WZ0Ei7d1Z\n2AZZ\r\n=6iCV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJdC+o1dzY3HvNT8qIdFEIRtSor0vcoowB/S/D3VYtFAIhANnc8SeJrRtUlDIo2AgYXAnAZJJ3998K7YO59rAU3rux"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-context.20190124005641_1548291592253_0.5550399077758363"},"_hasShrinkwrap":false},"1.3.0-context.20190124020424":{"name":"@atomist/sdm","version":"1.3.0-context.20190124020424","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7597e61a17b1215a85fe45f82f39501442cf2a26","_id":"@atomist/sdm@1.3.0-context.20190124020424","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-9zL88WicZ5WVIrt5CqyFCcsXrIscXvD6r+bBikWKCj239+zoSxhDibAiJTw75UaSA9lgt43g38pzQ7xkfM3RSg==","shasum":"17910645a1b628e82249699a34bf5558afd71160","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-context.20190124020424.tgz","fileCount":725,"unpackedSize":5437189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSR36CRA9TVsSAnZWagAA2MwP/2feynrBneq7TYBIDs3U\nGkJsokNEbRzBdYz2bNKEE7tmQtbM0j/NPCmHLeTmyH34Nd5ohSYcFQK7cxUO\nBrg1ofQGvE7oFGjOmnRSDdOZ+/0Pr+vwNx1Vyj7xV4154obeFK0Z2vZ5i4UC\nnXDGt9dtD/BNRVwB/wxn6hK4qfmP6A9tGD3rwQ8IKJDNw00BrQfrTQTyC5Z5\nOl6UPRDwSQR3TMC7QEAV4QprKg0BWu8F9IYC74nR2ZCuC3yG+Z1UjWwFrvMy\nKj2czg0z1jzXdvjrrZeCExH2Fz5oZ3rp4SpoUysJzIk7NZntfTSz0CxMfuLd\nnZoK/m2G8h8gwvRSBxx1Kp+iSHc8UpDrhtne0gwxiUsnPI/5aNFPKnnbs1y+\nZe6Cg8SEyEwFt5Rzi+t9GHBFNgFSSF6fuOgOG+YmREqQ7dNkalYTQiRu+e63\nZmq8MjvPWkRGJAV/i1z5hD3SdWKD3gPBmQXlhO/D7QDEpZEYuo+e8x93vAgp\nK8Hh23LpOLfvUEpjC4vIN0+ps53UoCQeakmm0YO986o8SfwgyOLHooCM5DaT\nDmQcYHI8FU6sIHdrcLlgjm717Roy+3F9mgTp095orHE0qPyq7LAh2XeXHaq2\nuIKphCsnOV+zQgFIqVgAeYD0tZ0qn/ZGZgJiBVzyvb/h7rcn/xIFSl/KqdkW\nFvdM\r\n=DeLZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEQ1yPXFywF35+p2IpKp03uFBgBFWtTiy+J22SG/r8DlAiBnczT+PIDqVGzhBxfq3AIIqq6onFtGxN/mboPM8D9+oA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-context.20190124020424_1548295673359_0.7262650929870431"},"_hasShrinkwrap":false},"1.3.0-master.20190124081217":{"name":"@atomist/sdm","version":"1.3.0-master.20190124081217","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fbb5f2e20c283dee4414fd7a39c77474575bbcce","_id":"@atomist/sdm@1.3.0-master.20190124081217","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0aHwvNxZrZUBGq/sTNVSEPQI+o00bAQU7I87JtXMKkuksKfMehe/1rVzz9SpmctgVnFqe43KbKQLcAbuIr63yw==","shasum":"987e1419e35bc87c4859a8dd0e0c726aa2b575da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124081217.tgz","fileCount":728,"unpackedSize":5437404,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSXQwCRA9TVsSAnZWagAALv8P/2tzcWn7aPBeb/3C7uTj\nGbfkX+Bo4EFL9u7ww0wpYuTjlKD72w9w4URQrOPdXqaNGFAfh5UR9Z6R2wQG\nqqgduFcgsUhpE6e5ZhpOBb1A37OCatDRBgJ7QxUEjalaq3BLuFKrIU/rQu1E\nd2NnB6xaAPfboKePIzN1N1gKtfvNTC8O12zu2iGAxR0VunOkiP9wChA2tCOY\nF5cNZXC5RKSBo5hBmvGwOhwYju9bSmU5vYswtF7+1/Po9hrt8kap/vkMwEnj\nzqE7VzY0+gsYq/R9UdTXXJ0IToQ3SACyk0U3ZVs0kqpYFHn+LE0CfGCq/O17\nVtWSHdddPAnV5rsypzL07rqfSbmskbYMAnTUPu46wZUy/yJgKYpgRF9o62HJ\noMTskDVcZctQgHN2EFrhnE2hlIWFUR4uyXxai7AOzHN0enN/tIDf89cpQKzV\nY7YZpXp/+3wtY0RV1RDTU1A7SIW+87jWxN4fd4vvOmYs0ca9LY6uEPxQJ2Vv\nyFZGUurMQouz8LdVTAOuINb/JHtNoBcLVk0qRwy++gFdd/c+072otwzF0Qln\nLybcqtfkvK6SJUjyMcqXMOWRqug1hzKqLaft58r6xa0d9+0fIuA4miQoZmdT\nx4D/cRmqD5g360jommwNp836xEyHVzCAuwDr0NGnc/Aj+RK+tUL8CaSHuIzN\ngK/r\r\n=++8k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGZ0qwpFEz8nTdMz5hzMgGwP8DPBEPqxeH27LLgDfBiwAiEAia2GSCmu/2htDrzMnwX9K8f2+4n+qxWdVjeQckSexJo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124081217_1548317743784_0.7176866517386677"},"_hasShrinkwrap":false},"1.3.0-master.20190124082220":{"name":"@atomist/sdm","version":"1.3.0-master.20190124082220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5c4b449ddb4eef8da0e08a5eed72c28fec003ed5","_id":"@atomist/sdm@1.3.0-master.20190124082220","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l/6uOiz+eDyAdd3hmKqgNe/kPum7PEIebqosIWMDX0dzf4LwM0jQ4lNa7f/O1T7vTC1Ghw8dXUuppsU67SCyYQ==","shasum":"99c6df90dbaa7558e7ed4bc4a6027d9d88f19d27","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124082220.tgz","fileCount":728,"unpackedSize":5530700,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSXZ/CRA9TVsSAnZWagAA9TcP/3R1iZZVy9GSULg3/6t1\nExhrzTHRkNNhV+GnU5xKgQojgH+usbaVFFt6qNPLa8ipR4S/qVQOb5ILksuM\nxQvy0/IYr62UmbdOUMXCwC2R40XLkNkVwr82ef3FJU51j9OPSzW4w5iJfWDf\nTo/+FeRuRrzXPfu6bFiSRiqYRHD04Nag8CnwWiIZzj7tTbAyMzvXArMszMtl\n4b8Yu511TUEvP7SLrGgtceBqWngSUBKnyGIkekoP6y9tZs9BKVjkHTVxNG/m\nv7eHaBtMPMksl1N6sQ2WYWmf0OV4G+vGnHdNMjVqjKE5L2+i9h/EePQ0DY+8\n7TebtTt7HhPriNPfwmrqhAWCeX9Znugu13dEuv7UthgRzlsvdvlLRc6Ox9X7\ncOyZUa/Z5pry+YUQ7op67I9nuHfHfCvIggIpCX/FeYuzyqeH1YgI+AWKZ69X\nYa9KdZN1lglfO6LVInOIySwzS9FFeB50BhGOe5toboUjdRtroL6zxd3P5h90\nBbbkwrTzHAv3GHAejiF4gKqffr1L3LA4ZybtXyZdbeFUZ4dXdCKSu5RNQJHX\n2Eyf42TD2NMPb9Z7F1TmP4Fi2OVhNRfXp9f+Q1i5/oq+h0oka6w/9K/uPky4\nEvstqT9DgPzlC4HRymWqGk+od+Fzgy3GCK8g9WhRnokcc+VUTStX7wv8LvgN\nOVly\r\n=XZFF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDml79aQPcKfz+ekcdBqmwCkujb34YzAqGmCr5dOeo41QIhAKwNMRJYBz/qM01ZafMikqzoUPYJfjmynMVzEkAoovgk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124082220_1548318334572_0.22055829140534167"},"_hasShrinkwrap":false},"1.3.0-master.20190124083739":{"name":"@atomist/sdm","version":"1.3.0-master.20190124083739","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8728908adb5ebb93550d068e13d959a3ec4c0593","_id":"@atomist/sdm@1.3.0-master.20190124083739","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IgpElC3wXn4niSf73+zRAQ1z0wDQe1O+pNsd0VkXCavn5EH2Fo28Iwy9rxdhNhCvJyE3vh6uT195VNj3N6YPQg==","shasum":"93c893d17df5a6753f2ecac586e3e4ee4a2cde4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124083739.tgz","fileCount":728,"unpackedSize":5530736,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSXoQCRA9TVsSAnZWagAAtTYP/0JNh+uBgfIB2HZ/s5ep\nGQ6PgCkUxerCeIdzhPf5wJ1ombVBbjEQyhfzekKn5PV6lsxCshP++/5Gwvjd\n0tCA1/I0PQ+fcMCpddJ0Dw/VDyy6ZSV1X/CD2MoP2FXRgPO3XIQmrmiuuA26\nEFHemB48s0WSy5wJ+w+shTydRxtqPsEjskJyXpWaVyE2dheKMkpzC8Fk3bC3\n0+fhJWweXDs3Xjo7s1OokVC85mQZfav9Z8xTx68cQgD8N72mKD8rKKJE8FI9\n7hRoXiLXGY4vSLzRtm5gSYSnpPxkfwk236XaLEgJh9pZv0eDeIiY0lDYKjtI\nMJsyJczBTluiN9GDhS6nWTKjgl4maE2PuLekb8x7gGngy6BtVs5A1xh10/Jm\nHcRBKj/wNR3+wXpXJJKDXyYnYhYFEVPqyz9bA6Ya8APBzQILdLnT7N2ee0UT\nJXZSWngaVGt5oFGTTpnL8ZYUi57NnCtv3zdy7w2xoctlc2ZJ0SLfvDYsL7BW\njpshI2b1S6/UUvsJih9zmh8kJO+DraNsCqg1OvVXWyTJk9fOoOYlzGQYMMAG\n3PBKHSxZ6XLw/2Ea2QJpI3mpl28HYm29erUDU4VvWu8inatsTN21X44JMJ1b\n3J2S9z77oAJg3SZGsAHlc+NTfBgUbgmoOAsWX27uaU4K+nwL8Yxclak3DC6f\nT61m\r\n=Ng43\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDmFarrTb7FgWlM8n5bABqZcMyEZBaX1Bi2A4+WOjTy9AIgNKua836jj27wVRzz+z7XX41SisCtZ8vzXrnixHYDu9c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124083739_1548319247870_0.1577447924473614"},"_hasShrinkwrap":false},"1.3.0-master.20190124112759":{"name":"@atomist/sdm","version":"1.3.0-master.20190124112759","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"60ff6686ea5eab8f6d06ae99562d335702f964b7","_id":"@atomist/sdm@1.3.0-master.20190124112759","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-lkvlYip6wUs3g7GJSjy5a+IkBWYHg8l4xdkqzdAevV2faElMkSDDUd4UAAHiVg2rZCNa56rcBGVsxNdqvvYF7Q==","shasum":"68d53dc5d1d358aed98935c7109594b3c5b29078","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124112759.tgz","fileCount":728,"unpackedSize":5530874,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSaISCRA9TVsSAnZWagAAWFkP+wSDTHrXE2vlD7ZTIKLU\ndj/UztWX0LLBZjULTw623zQZJ2xFlI7KuRgZU3WUnvBz/mZnZNezRnK32ZUh\nOojQfT5CGdgumhLbQThMeFZSRDEaN1v9y4CB3gYgLYQ+uiilJJ24gLb1Gum+\nZnlDfrBcTsVXvcUUWVgLs9TWl/zKeuOCikKf2xWIxWAae18SCYXRM7qZQqQX\n3n0pUxYEoxLZsg7z09SBEjA/OIKb6IoIVjlP1iG0cLapUImd5ALWqm2DMI9x\nBqjJ8wZLa8W3fS6yvkyN7UER22bpuL/0cRpib1e1tOGFOJ37Ocwjvf5N4Bln\ntpUfUfCBOr9uKtzrHoUAM1wOJX6vgr/iRaTPIVvygxLnlDzWqsX32gVl/9RT\nfXdgaRHbecPNewvavuspd1s/7fiYSf5JSsv7E9G72+j8Ql5nrIIGmvHh1yXe\nVtAMbnIWAQDiU0JJvn0AcmQSHY0dEdIppxT4ZFMT2UCdwtsqmbDX/MJkXjK1\nMLEdnZVvd9D9cJmD8Uy2ijVLA39+NIEradwGLWZV/kzr/CHv8EMQMgvBi90l\nSu2+mVKCm6pLP4i0eZyTFv0g+65QbvyxEUfMOIXQ/0PGXPioeyMmaQnO1v9f\ndEt+QDTk6VA+mx3JF6H+sncmGRRXGdXomqZJaGMhcHcJdfKZM8/Ta1vVj1/h\nNkbf\r\n=eOH1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoRTC8nhcVwyXkrj/KDkzicvnqk446q8ukPheCa2I0TwIgUIIXTAbKvwedYqGrxbabUk0bjg/2T2mXkHIj20PrupI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124112759_1548329489833_0.483499864895399"},"_hasShrinkwrap":false},"1.3.0-master.20190124114347":{"name":"@atomist/sdm","version":"1.3.0-master.20190124114347","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"35b4cc3eb27e2483047926eabf8c55308c669fd4","_id":"@atomist/sdm@1.3.0-master.20190124114347","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kBfhygfe+jK+SbvqksDsp/o7LJhca9saItbrzTH5ATiwKV+QqcbIuqugXrULPwHYldFlcrr4ujaIz/eNZMgEWg==","shasum":"c932b358016305595c696605443e4eb3e7b5b396","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124114347.tgz","fileCount":728,"unpackedSize":5530900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSaXGCRA9TVsSAnZWagAAJS0QAJeRh6TK6h+552sFq0fH\nYJqYWtcy8FovygwVF+15Sbv79v36Z6hEyNqfyLO/SOIVUiZHEq6u0+XXSVgS\naiIDJ3iOYXPbU2O8uEKLmhnztMb2nqex9m9ni/6HYWFD9xRhV5Xsgsc/Sp03\nDoK5PWb9q2fHOy7HFazGCOIUsdWGgLQLut9CSOSem1JgZ2fQQfsmy6MAzWPG\nUPPrug5CLi+L4a68mK+e23Ewb+upxB5mMh+i3rveEqOjA/YAmNTLyzibJsJO\nT4q5BtLo/OrL43ESE4QX2/blQ59iLrV0K1rSGLSBmnnW9F3YfuS9P2O2xF6v\nf1cQ1YLAK1+HuYLv9iPk7nD9iO83vxOBHPbrabBxh5rBKLbnAdUnvPBof4e1\nPLmWGrlVUMhUSk2Vl4c0sco43P+CTfgl+MHEm/cRHo42xPWR9l65ZvMcHZL1\nnYUpYLmNdurSLt2+r2Jr65BQFH7R0q6UD1RQWUGMGoxrj9AzyDnyLfiRQ/Sz\n9jnGxExIj0fit85fbDyCu+QwqTOakvC+MMCi1/cgdmZ3+EmT8N/wbolZKPHI\njYcrzRWeliQDS/wFRV5kkZU6tVWWiH8cgUez3l1J6GjBXc2BWaC0/fZBUSRz\n1RG17LLvioQOaBmJTs7hdVsQ38ZQaW22PRDoPQBs3bw3FA5qkDZQe9No9aZ/\n7+gO\r\n=S0qt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGOOPN34+TGEAl3iZY0vyC3gvXkRA/BKoiEhmLLoEvSdAiEAxPEtd2O2PykVhwIg9VxSuvheBSNnodksI2T1SRtF/uk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124114347_1548330437573_0.4054333230750564"},"_hasShrinkwrap":false},"1.3.0-master.20190124121344":{"name":"@atomist/sdm","version":"1.3.0-master.20190124121344","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a4f5adf783373cc9d4fcb14311e9b31ced21a4a9","_id":"@atomist/sdm@1.3.0-master.20190124121344","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eHWwxdUODqA3Fs9JET7iPb4ZlPFAf2MuVOAi0ld9hEi+QolvEYCSnrIImkuKNthCUInZZxUN0n5I6Q+dc+DRfA==","shasum":"f484821fa490123adc3d3ba21ebc085508621c0c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124121344.tgz","fileCount":728,"unpackedSize":5531299,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSazLCRA9TVsSAnZWagAAln4P+QEZxjronTofWjiead2Q\nkgiy9dcm7gzaRLaMCyaOLvwdBIJc22WLLYI0HwjvqnIlShZnkKkP7KFyMiGV\nKrmVNeSmnPdmdYmeLoGnZYmBOFkUvsdASs6I6X6CRNtdQ2lV8TisYYLkIt0o\n3jzILIUDx1oHDpTgZ46rSCsfbeZvSe5pZ4DFKZPTv4juGwD89Dzjb1YogE3x\n5BvN1iHK8RlCufqx11GhpqAhaxhy7Mx6F8A594vEQyGB16lL4Gc6QBui2YHA\nsgVRuOM7hjS/K4BXpcUnZ7GSjPEHzADbqpmZ99VDpjuntTW2e8RyoDAQoA9E\njIegaDwyXuxzLv4lipXwIsbUZZB/53pqkAN4oxXDz89PdNsJs/KFypaAgzF1\nCPP+nR2jDS1h4ybzaxaHXXL56TMqbV00TT15OCh+meHHsV2F9QyoIjgVfX9B\naN3cAWkxwwKcW+NZdaljdpDcXrHpSv217EK0gI93sCYFjUzZ2MZQmZu8CMYV\nU2HqzRWzoX5lxR3B6b15wnHzY+gv8cJpc+RfLcvFW8INAweRAgDCcENnUZTz\n9zLiNB0TQIUdCZtthqTL2imwDRcC11IK5uKd+ggSFngWye9NN9Bqgyf+n1p+\nxkfuZzbo5c9V54tRCd27YIJAPHF0M9TjOCB3VR0ZjJ4nEz8ydMowdF53Sh+T\nHX4J\r\n=7St2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCYpJvNlY5h6fHob6FXkXX4h8v6aNSxFI9mx2b42SXQgIhAM0mokzUqGGis1Ijn76PUsXOvvG5fVkxcbBdgZf4RiS0"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124121344_1548332235232_0.7796487221340844"},"_hasShrinkwrap":false},"1.3.0-master.20190124135729":{"name":"@atomist/sdm","version":"1.3.0-master.20190124135729","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b32307c141ee238efb79e5b89d4e2585b99e3325","_id":"@atomist/sdm@1.3.0-master.20190124135729","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-iF7Ec5eGA+jhJUHwa8NI3kcS/NVIPbVklBtbnhwHce2CJVvkHyDd07Sq5qDXLPOYvAhxZyokaYVSN6I9E/hTTg==","shasum":"b45d096cb53feeffcb10f19e03c8a6310d5a78b2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124135729.tgz","fileCount":728,"unpackedSize":5532261,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcScUdCRA9TVsSAnZWagAALugP/j+yqwyHkLRRXkrazE0R\nC/ire8OJNXOTKTrziP014cMVRpFuY0E8rWlFMz257hGXb2aQ2mqSqafkdLwl\nN47nWqCOs+IZmr5mIG9sqc67ySsd1gymMtAqeCCdpIDv3cV41E+HV/D2UBuZ\nri2iY4bLYeOhoMFUGRpBdUMg4DRNxDNk9nxRrwuuHfYgV9SK6YDhRKPtRASe\n0+1IgGGK8h+NHQ7YAONg1EdFh/GrCBXAzsFqmG3YFsFGKVx29gwXGSYJX8Dx\nNDXxb2V3wy+adKfBCvPjQoUWWUIaIkZGVWQrUMWNJKg9jT0ZHty5Fqau29es\nEG5ZVxpWMHaa2s/d7HUNRzQXa1EglrDhiBtkosdkIiu1t0QlZ1cJHIDcVS0i\nslg3fRtNZFo7m5EWTch6UujNUbuE3mt5QsbRsRInLzc/Z8nt8TBDjng+PVG5\nvW1dyhN8X4A/wY7QyhQGyxqOYTD71If3uBQoEUnsLJ0rnglsp/6PasG9J3KI\nlaQC+/SJY54lGboEBoVPPFxFKK5RmeZMRWcRH2MhtmiIyK9iluB7XYDdF0iT\nvR0G2Zw0pN91v6LMUQFghYUGcYi1v6Kzj9iNHtREwcKMsiuL4AsIqFjnqEsL\neQ2VeI/kHyFiXcx5fPgA1LWRrZ4u7ZNoUDtrOvooOXzgZ05+BYKg7pKDsAGn\nMu6t\r\n=Ll7k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEuKCRW6O6GThPVNQ1PJzEQvOthSflzShfuGAZOh2MBPAiEAsWnboRZPVvmQH/5/VbJ7QQ97pOWhvLiys6JBVe315mI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124135729_1548338460352_0.5626458552279991"},"_hasShrinkwrap":false},"1.3.0-master.20190124182949":{"name":"@atomist/sdm","version":"1.3.0-master.20190124182949","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa0fc833d008d47e9fe7a31fbba1b93ab33eec00","_id":"@atomist/sdm@1.3.0-master.20190124182949","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-VhqwaX0w0GF8asBAPodrOm8GoEKJm2GdgbE4E7y1JBXrbXRIdFI1IiboQwPMp27AOqOGjR19hKZy8OW/xkjZpA==","shasum":"87a494250e0e285480aa47ec77e8266b80a87b49","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124182949.tgz","fileCount":728,"unpackedSize":5439234,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSgTjCRA9TVsSAnZWagAAuMQP/1S3xPM0T16Um02bBD3D\nOz0/Dn9yPOw1uJUn6Qn0JHU7apgyLpAmHgBWWYhHJIuIRcLglLhX8N9VyJuY\nfqYnJL1zT6V8tZtGMSNhTAqrXucEFeyig4fs3jk+KjI2T6nLuJfMNd5FiYK0\nqdr3J+jo9LU5ji2D3zigkek6lvl+sg5s8mJVDXlO9xSbaNecjhmyR/K6ygXR\nJY1YHMkKTXtDFulEz43+WHoaVO0odFrqPQ52pN0M7fDq4yvJBy++D4oVp1l9\nq6f9ohnmwQUhOYEbaH4EPs0IiKjDLXJ6W133sKeB7eJLk6Ube7nI4qddWMQq\nPXwQJCI3UzwE4VhW6oxH9I93L92nDDtFPqikWSWyE/fN8pwVGQ01WW8Rcgjw\nxSA6SAEo2vpYivBFRbN1J7zxPXqlgGTtuBCj0MiyU++ghUX8ziPPi9wEZD9i\nN2t+sJrxrCdi7penVf9GKB2JLxWSck7FQh/BcbuEi6rf7teYqSXjoKxvXUCN\nVzgfRL6KfloT+TrMbd5XC90mIhxaLRk7jqGlL3DD3Meu49XfKhPPbgEYCwDR\nYKmRDel8kdDZSe4iDpFKiPJFfXLFcSctkWR9gQlBnK1c2BoWPBQW1qcsTiQ5\nsRftcjh3tQDwxDL4AMd/Sid0G4GDRS8qjfjLaXut8dUsHFxigcZ52J5l+1QY\nTaGg\r\n=c/8r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCtsNY0WoqjEmQiKa6fsnigDylS0N2DbIGf5RRpES7vmwIhALJCLTu88btSwLJsXlgVk/cSSaBKdgIBAWhM3YWF8Pft"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124182949_1548354786981_0.4804965006309807"},"_hasShrinkwrap":false},"1.3.0-master.20190124204210":{"name":"@atomist/sdm","version":"1.3.0-master.20190124204210","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3362210db7ccc442c1c803cc448d3e93956876a6","_id":"@atomist/sdm@1.3.0-master.20190124204210","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+aEYUlyJ4WWQpMGyjopoOLHCOcAZLCrS495GdMt453233yJy6cCFMy8wv/kVBZEBKFLP0nWrFiUhfJbDZBVS5A==","shasum":"4abfa6b511f6ddd9dc6c8dc54634121e8590476f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190124204210.tgz","fileCount":728,"unpackedSize":5440685,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSiPpCRA9TVsSAnZWagAAcF8QAJIO34EQXAz76gRoRLSl\n2XsINJalVbf1zV19F8Ybs69G2FCkJIp+zY6BQGMwngj4R+Vv6r9Mfbw+xuq3\nC//7mFBXnYJ31mFd4WPbT2dlebl2YE9ciAY4oSnKmalIe48ssTDfoCQzbg0O\nc7pDQV7C02SMd29FXtjlo/8OEpfkaPtnRDcfesRLHOgX78cbqjMqey80aZO5\nk2slCb/+kO56YKBqH0ADQDs82BzGRGGRHqaGjYh7xEuGS22w+ugMlLfj5HyN\nmEpm8Xhgp64ayD5RM7d1+wNPeX8BPL55PCk+TuEx8CfKSjW3rOpc3DF/B8I6\n4aM03bFuUN189t9mqH07m18c4n/knVyyATqEmpkuOBt8NVkUsKHEGuaFpph6\nnG65SEKVnujn0P5T3iyE0JDLth3vE4UCdiuf2KWuouZTU/3PnNFrBtBCXR9U\nMVMAzVavnYnUS5ljA2NwTwlPm3zV+Yjy2FNkVPdoSfkU0tr48tYYRWhcMSf7\nq577GDuO9XtgLCpfIGX8IN2Qz4zjobI7u3y7dIV2etRVYHyWnhZkuCQhc+TM\nz7p3JZsphB+CyLliAMDBGKXWAImJ+ToNqYtXP0wE0TU6+zTrjWn+DCkpSpqS\n7wsekLXoPSgjVUK8l3TM76ux4f2lVtWi2N2PvUFcFJc2BpI8p+WzZioZ+r22\nobge\r\n=cnbu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDAOEPUQwWCwK0/ifCEiRKKCHiYQ77DWSwxAMJQgdJmiAiEA85fN+dUoeZqi2Q3KC+DR0MqaH+gGJe47pi7EaET9Xyg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190124204210_1548362728527_0.7833540017818066"},"_hasShrinkwrap":false},"1.3.0-master.20190125092715":{"name":"@atomist/sdm","version":"1.3.0-master.20190125092715","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"603f9ff547a627e40498c0e61482ef3c661021e8","_id":"@atomist/sdm@1.3.0-master.20190125092715","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ploWEYj68yyAFTxSjErlacRWBpSA5oXLy1AONmQ2A6iQk1G00eMEV+KGIUWTnVSg/orIBqvddoqU5ksqTmoC9w==","shasum":"d2740771583690e46b40cba8bb1cc280ec33cba3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190125092715.tgz","fileCount":728,"unpackedSize":5440723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcStc2CRA9TVsSAnZWagAAxaMQAIdRTG3kRUy/9wCqoxSE\nM64tzscQuZFjPipQOW88apvM04JqbPWcHVCXOPgkT42QP3kz5y7F4ZpcuyQ8\nFs3G9WmafbMEt+LbCFtIIIH3/2pvG+v6dC1y8K2t1whHefsVl7CIctZYYK2M\nILHIw7DJshEy9uqcym9Tf1b65CLWGABlAHkxP94OV/84CH5x8SHKOhvMBBtm\nvkizt1ZXVQUhVSJY71rF+fP+ewWg0zW1OPuxKqlvrjf1xwyS33iazMYXh8vz\n6G8qMbKAmtZ87Khuh9XLT3j9iZGS/iZ0IyEqogVj3wNYuVMkGd9AmiomjrHw\nWtilTJIyoPanjATTb8+5zkgLZH2D3xZdRQMuGqCuHXxiSt2030WxKyZ7Axi8\nzEKJzNem+uUzMhyGrGK0Zb/KwnyKX6DGdBYi0M9JqXh/xpxoEvHcYCltTojR\nzqdOalT1relUrta8YUSn1aV6lEIlRuylbeubsioyAinFcKy5fjr5XYLhINRR\nNVsuGh01SAdioZufUBsmIBwoBlDsWom5rgkZUEXZAzTQ1A2R2kqqQGu6dbsJ\n0mGQOWlsz1lSOOKYAFgKP+DYUsHig9fYGKeVs9wVvBQwll/vgLHyXIdu3UHm\nBljVAtnJtXQ749+bZoHvDgfvaO5A16IZsAcC+ThPlr7BhyIekw4LW5GBC+w/\nz2FT\r\n=v/0L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDhde5ntFoSTrl3VFkpG4yO+rF2Nj/59G8YuD6Y2N24wIhAOo1pum+7AZZSHcd3b4qRPMFSoNRm6j8EEH5OLis7ngi"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190125092715_1548408629524_0.8222007617775737"},"_hasShrinkwrap":false},"1.3.0-master.20190125112534":{"name":"@atomist/sdm","version":"1.3.0-master.20190125112534","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fbea694c4a07c23f9d50319083e5456bd197843e","_id":"@atomist/sdm@1.3.0-master.20190125112534","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-pnzADjYY9YOdD+mxJmxMoMaBzEvK12kd37wTiBjkAb8ucPMEJgYOpr4MNbIxmj8q2pfPKaWAun6+ZuHP0Ht4Fg==","shasum":"ee169ef05dee59225e1e94cae2e5a166bc519617","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190125112534.tgz","fileCount":728,"unpackedSize":5536319,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcSvLyCRA9TVsSAnZWagAA2NcP/2xvYb1/uZ1HevlRlAXo\nfZQtvQm2JMaUtHE1rYb8ANNeWyNZBan1swixio0jrHiJUo1DS7UZvm4OJmuL\nUwPrRqGhNXFBgPTGkl/lsZ3YkV84Io9b3M1hCUGxAYnSBHCgPv7ko4bW0gyk\ncwe02ncJ05A60n5OquC7AXYxlHc3AmHl7jg7U7rbL2Afttx2bqrXqczihl/q\nnMFTmlKBJ+1LTS7t1GoIpkSK+IaCcQNu2ACp87zFq2ZrmzTIdAsPKLaAJWdz\nXtwMpP2vgKogfqptLJjEt5oKheJcg5qlgkcMNKCVcM9cvqbrCD54cpqxFQ13\nNflRXKMhSCaObRnJ4k/qm1ZNzCT9mTbbRcyMHurf4HF6vMgxXFiyrS9kMrJk\nSCWsyLTXeZVvnt8BXnHhvxkKJOjKSY80ABNNofeGS1Vf1AOQwBNYuq6wRycj\nXHMWlvJlM6Kjn++HVYp2CxSbSr9ZwxFZS8xa8U6PzjiYl48or6QEJT8SMuVK\nstCEVqdt2zjEptgAn1A2agTIbXnJBbNCE9Z2zbd/rr3jd/kqfWsk1gQsbG9H\nQVnuZev8m8wSnRgZhXg3PEVy0W+ea+zRDtrsBYiNt2hfU9wkuqJQ6BhtNXB1\nzqeYkZfunZsxR6wk4/PvBKp1nB3c7irwV/GLNL80MJTeiewN9PjdbN1XpQzh\nFhTi\r\n=Zj24\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCk6CdAReKqR6auSRgPzfTIg5JvUNQmMHLIk4gj1xcyCQIhAPkkkDzadyBionJ3pxStvRWGtZSc9JUYZWBMDzPO9OI6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190125112534_1548415729738_0.9744894815403558"},"_hasShrinkwrap":false},"1.3.0-master.20190125192906":{"name":"@atomist/sdm","version":"1.3.0-master.20190125192906","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dc423e27b05df03fd936859e7bde68b5c41438d2","_id":"@atomist/sdm@1.3.0-master.20190125192906","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EGYlqFaLhkIDK/9TuCklLpE8LgwLM0Z5H9lemLuw65L6RicxbTGiDrok/s0CYUkhonfvSvUeWWS/9HuZZ9ZWig==","shasum":"d5b50b31963737d7c4a4b9ebae63b4f9be59323f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190125192906.tgz","fileCount":728,"unpackedSize":5536315,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcS2Q+CRA9TVsSAnZWagAA3pcP/0sOwi/ui/BVONtTDw4P\nPt+3iVOH84XpD0LZt6KFzQGZy4Wy1Chi9KZiRe4cqEYzDMG12G0GNTU7XOXO\nCA8wDItz8nA3oIxNvSHasJa3wp/Gn5PngxzDEYOLONPUZnhCb+fgSW6GdaYW\nmJM3LR1UHpHznbNxngc/sWdDVVv6LYxQBrd7Q0NzSNhyeUBdVUfTDwi0Pt24\nWm3KHTo+7w0xiddJz8TqZj6DwtGTYIQJWcO2h3zWe+oyxWaTL9okc7UTBp5Z\nmnzznRARISOFq2Uk+kHmetKL3uXXbUPlLhClnICjTzoSR3Bd7AQi1DRUFKus\nXR/aFvKtT+6SbdE+S36Z7INuEQlLKV6eF+kWRYwa7ZKWxzAY5DwihRB66cx0\nwtk8GVCPGyh37TPJ6uSYVNPjkE/mJ22oZLjbqeR3UMpjrP6x5yEE46hgRVa5\nrNyRf73zo8rmzr5anUxkM9sPArHUzjdEMNFM60ra4H/sOn4MufH1F+COfxzo\nVh8MhtnkDsNK3l/KgAxwkWU4BpQ1Rs6riDHJfWKSNjjbEJIOtRAGlMmihpy2\nKZ7wbj4fpiGuxHwX+4ly+FNajwSmb9kLR5vENIYL5moo722wVzC8u3k4UMop\nVTrPUJG3qTv176FNw9SRv2uZbYJzArMnoJTTT2jNmTxjld+geWNSSslaqluu\nzfq/\r\n=zf4U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDB/EikFTk+3NEO39I6AoK0GTiXWPh1RvVeUgFPhNHngQIgKRFmjPFwuaVo1AOgGuvBH+RNL4/Lv0OBimswPokQckY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190125192906_1548444733422_0.01667593150417135"},"_hasShrinkwrap":false},"1.3.0-master.20190126163729":{"name":"@atomist/sdm","version":"1.3.0-master.20190126163729","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fa80f8fd8c3d8db0324759225ce44dd0aadced43","_id":"@atomist/sdm@1.3.0-master.20190126163729","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vrznCypNY80UwxBCRbXOSEvCjU7iKocN9FWsTHxucCm6ZabYEnLrblmJvfBuDoMScM22IBxC/m5k0vlGTRajcg==","shasum":"64bae0846c712fe2609dada5361e3012039ea33a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190126163729.tgz","fileCount":728,"unpackedSize":5536509,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTI2ECRA9TVsSAnZWagAAcvoP/2hWfbzyVkqgQr5FZe1r\nFriBhItyPi7rtn3RHOnRmjlD8yAjgrysZLBh3EqtBOTWbaL84au6azdqNU6r\n3t56ce6UNL1mmJPA9wpR9lBuPbqK865vQ+fHHtAEn+P/xT+PYJ277Qj9VC0S\n1xBlPrTScOpnwA0W7IHdlEnkPrXryic7L9x5RDiiuMz+SNFimi7n0nbwRxoN\nyZs4s78ai+3Oqjx6vlk7UKZA70mt5FfSFnsUI8C3yoM0pSH91I74wnUcdZmj\nBr2GpiD6Rwy6SWUviga5wfCIDJCOoztYaXIpr2Za6F82p/n30QwBiwwyidSI\nzWUmJe5UJTNlzxbIkbi0r1HxYSCThlZkBRbcnh7Ab2DR2AeTyH1LVo4y9QLm\naTZ68o0MW40wj14giCWbzywfJPyeKf2/k6BdqIZCvRaof2zRvgjpasjt05+8\njPG6RRbFQ4CZcL/W6cUCMcUMDg4x0S8Uahm514XeUxtivCyI8nf2uSPENosO\n/omqp1qzRURYJ8cvX3LPT+ntHO4P1viIAx7vtrdeum19pshxhdkfGAEdrEeB\nsdF1I2HWRE19CK1LXpRmk5sT5ZWvcg4H4GNaXAUu243t6dc3CSvhHslkXgYl\nMZglMwhyfS5ED3c5Otw9l4fWQkI8KSysCAhRKFVqpXQdRp4cGI3jwQv4GCY4\nKOQL\r\n=eh6j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC/779lPFkIqvR2kF21zonioYhcd9s5ikF/aM1i3q7jgAiB5DwVCE7ikb8VMaAGV28DB+tbE6nRC4EgjM7sAzRWAaA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190126163729_1548520835782_0.3159587904108032"},"_hasShrinkwrap":false},"1.3.0-master.20190127063950":{"name":"@atomist/sdm","version":"1.3.0-master.20190127063950","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ef96ba500be048e274cc7b3fe8cfab73af58a0fe","_id":"@atomist/sdm@1.3.0-master.20190127063950","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-4lqygi8OoE0H00hwW1raoHgC4p8aP+VUdX95Mru+pkq8rjQznOls02lBpwc4nq2l/z0/I4ClAeSgorQ86eVJRQ==","shasum":"19cc9fb0420d67473b63821d9671e0c1376c1826","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190127063950.tgz","fileCount":728,"unpackedSize":5536535,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTVMKCRA9TVsSAnZWagAAN6YP/0m9AF284xnYyHYHmeQY\nUeLhB9vRHKyXY3SArMOQ4su1GzIlRLATSy6zXsprO15MZTR/PCSPRJdNy3Ze\neHhqONDKjMF/7UlNT7NWQ/KzNtV2M+qLTmS3nTn7XlDrYdvuhoKAM3O+tJ1L\nOYVTf6tD9iUbhZG2bpbzee2ig2XtMHYS9wMUQoBrd/MKNuJyRICzK3Wi/MFu\nXt0kgs5APt1NediNFIGWVOgepq+xnm+vpvpAzox3uLYXenumYseokzgsOkaW\nqCtUPlQvzx0nU/0SBegaorSCkTzgSVcbxZQm5kFbxHUHPHQCM1l/MuC6Lj5G\nbhVkHVtwZcTkU8xyUfKiQbDltB89wU7WV4fh9qMXdioMEFTi/HuOW/o8cj5S\nzML74XJ3QmSfPHTyeSPIqxFxGPPKbgOKetQWbp4ym4rjVVDF9DcJ24l0NLdo\nSrWj7RPTcUXFzMQdum0x4JqGaC8ZvSR8G93xx5PzMpaEtimM3irRLQOD20rq\nmWhz53LQMQZe+6/atXMyR2EpCriorHVlFGh8dNz2/KQaMxfpIevMlooaW1FV\nDa+ei6UdldrT0JLO00cUzysrDd9d1+lnjruGR/ADBRWEgFZntxW1MFZhptLV\nHS1E742277Nc8/Y0KN9F1OIcoE16B9eRVJ+Z+zbiQD5dASt1CwSFYCkX+MeF\nC/9t\r\n=VOQp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICE2hvDdRofc4qrxuiKCkf2mQ/9GHKiYoophJX1W8xAgAiBGQ3ipu+8vA4lzHK6tjgjJoaft0cCveSP4r13gE9WTmQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190127063950_1548571401891_0.3365008192151788"},"_hasShrinkwrap":false},"1.3.0-master.20190127102926":{"name":"@atomist/sdm","version":"1.3.0-master.20190127102926","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8c7181c53ad85f127276a08f2fc50d59c41de7e4","_id":"@atomist/sdm@1.3.0-master.20190127102926","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-kRG55Bn9dOrqYIpqvYQdOAUx8vgHmFozmo6HDgA8grtq0itZkfrFrvq/JmkPUD0731pBwL/Owb/qLydqhvi9OA==","shasum":"c1a8380c3ee9630b9d114e305a9e51af53e7a5ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190127102926.tgz","fileCount":728,"unpackedSize":5539181,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTYi3CRA9TVsSAnZWagAAQukP/ifmpM3PL7hlhuvmeRkk\nq+GDavABOX/t4WfYSQfWSM/BuA4z6lBbTL/PFdhN598N9Pz7DrmnO9RNDpdI\nvd5+dl/+P9McrJC667+Chvbp3a5/2yXVVAoE1sXKE6rwlfzCAE8JygETtM9Y\neA/JseCPtvP0SwcDZ/A4hYrqRuFvjy+tdHF5S239lRnRr6dbMvDwEUNCk8yN\nNOai2FM/yLzvOpIrfz+Z5k8H93V91HJb5Tdfk8/ENgJfQv6+UGam4k5hEJCQ\niheBaCBG8buYiuhMcQDM7smbSd3dQKXXc2LkEBc48tHzBKcGiRkQzILXDQzf\n9//xsJGjjXTeWPtSqDe36CnqsLTOoCosNdMcyqtnnlSItYdxuB0aEcguSL4Q\no5hgJuUyM83/oCYjA90M9SsdqOx4xxa7oQznc3r9k1BVspTxiYzs7PbYcVHK\nEwIvzCT/KbeIblLBuVL7uo18JKii7ieeGlRCqHUFc/qFHz0i3cY1LC1mlv3k\nO2caq2xdqaIuum6Ug6RSmA5bLscAp70UeULItMM7ChyZB4xdwIpV17ro+0LX\nVPYIUhohvUQtP34GaVlVgYfYMNb98ZR3GKgyBOjrvaFd7p5un8YL0P5By5qa\nxPVV/86S1S+NEEorwh4aWJKT4Cgx2PuS7h5Cqz9dybbVd1FyfpfScz0dW+7E\n2eXM\r\n=Gboh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGzYnce2tDLAZuHAPqu/7xKN/YhgI0OHHDLIcg+Rdn8pAiA9E4s2ftdVgC5ODe5hquaIBW8wkF/oadHxeZs4KtCkxA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190127102926_1548585142783_0.6631775668639948"},"_hasShrinkwrap":false},"1.3.0-master.20190127165149":{"name":"@atomist/sdm","version":"1.3.0-master.20190127165149","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c1a523d70d812866087a818d1a1feb689f2bfdc9","_id":"@atomist/sdm@1.3.0-master.20190127165149","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nemA8+H24Ny27XqXdzDgBsSyUrFWDRxGEOkIh4rlXI6wMy+9SwCOEuUEXbmlMtMSwQdI842g92f4TVk58Y2+yw==","shasum":"36f8df297485ea7221d1c4ea1cf26a2ab5330ec8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190127165149.tgz","fileCount":728,"unpackedSize":5543201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTeKECRA9TVsSAnZWagAAzv8P/j9Ot1QjwBgk88e9r0Y6\nlcbwS8nighIuNDKcPi16JqyqfIy7EWp6hYs871IYgcSjF76TF330VsbesUDY\n3/v3oiZidxlBCLCK7RLLd6ZIf/JYTyR5Gn3v6fQ6iZ3hZeMFWPo6Ba6oTnJ3\n7vW/AMxoEsmYZ4gSkjxM1oH5EO4s+efvDJD3Y8pcEBjL8j274u5eIUx584Sq\nu6a2kH/XTKXWpq/c5c12puqY/YE33aq3fFraDu3lCOPmMCcIuuHR4ow3swvP\nh0dEevKPbSPe87zCcvG9HhTZ0x/5B7y3Tal5pemBS9T0iyAutFrIxNaQab2d\naQQywNhLwT6YGiKc6LDENU6w2li/xWwfgy3qQzunmUPC7FcvjFvtqFe+tx+4\nxO6D/FCjKgBk5XMlnUOTybM4JmuVcHHmZiznydLUhKhegcf3pUbLSO6dErsN\nzWbpgzWbSsbMHbCM5VBiDVaLJfhHK4gmCPFhlv7ucLto1Ns7f/39Ju+NO7Mz\ngTn2ISsGA3HtIFw2fZW495y/SJyPy5S28aciEDZ76T7n9uR/qM69XQfpvOG4\nCSH/DnfQci8QediuhWbaDRlh6Nht46zmv2ZW5AIldEqnmufR3SSkY/z4sDY8\n1sjW8QcX5pe08xhUCWO7IxWQTCXKH6c55xnlReFZcix9izrY0s5f9fMt9OY4\nQzbz\r\n=LVCW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHg93cxpRMH0EfMBRelOg9zYosdz0Uxxmzser0QAkjJHAiEAo0UHxsx1U04ErTcEmvIYJ4oEXXVlz25jfAz9YJoMIRg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190127165149_1548608131266_0.3988822588295047"},"_hasShrinkwrap":false},"1.3.0-master.20190128152439":{"name":"@atomist/sdm","version":"1.3.0-master.20190128152439","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"be995076c54cf8c32e867c468c08d693791e4056","_id":"@atomist/sdm@1.3.0-master.20190128152439","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-jshNX6yvgljhMmT4Amj1GkobmKVdv0FPjw285Hh1XksaVsLpitR7B3dtIJwUUgBWoyjQBV+FwhcHUUBbI+dwwQ==","shasum":"b159ef18ec5bd2fd9b330217e285725607a9e9ad","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190128152439.tgz","fileCount":728,"unpackedSize":5543696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTx90CRA9TVsSAnZWagAAIkwP/0ZxG+nsJZ5NpSHC+DS8\n2tlJMVCsI0bqY7yadvGcAE4YxxAJYkGw6xdQ84sTY0AmYpL8YiFHR9x7m5Ys\nM+HEeCekK2BU8aqM97kEJiFeYlvXXZGK4HQFrIbsyXYsXiGsbvgI6ElKAg23\na6Ir2HSBlvf0gmMdsHwpisfjLBIHg3gl6076lo8jfEAQfY8TEjoVKbNmeyfz\nNvjT96fPnDPB38fkg7/Qh+8X2sx5OK2UCpbWj8KQ7be/3i/T4Ctf7fFMpGKo\nuz2bsTb6S/FL+QeXuzOqIhdbUFC9Sc1j1JipyD97cR3vxD7hkLR390fpR1VY\nGEJy57r4nM1PUtg1TLWA+hK6luT7BKAItNpfHlsP/STJ9wdjTrZROhp4epq3\nLvyDu6/fUx07RZbsbADmoc1lFxVyKLyIMFjX/xe/0TttaMKWF2vvs93h6AwI\nfsLL6GQR80WqhSgCP2OhAitJHGx9auyhEPdlHXgSqZJB4sjtRn4ew61ENK/O\nBbs4C07uu5NlM8fYk4bG0v2Di5EzhqNbsEbdCziwM+RPfZdRdg1sIf+OIewG\nLtwX9qb4iFoZRA7PRbBogSkYajyKBON79CU+cCmbZFjegyUBswviRzMBA76v\nTznjROHAMErDDOMYeUwZT3LxiYwpyqWxPkMaooPhFoR4zlc4q9LanQ9Cd+Vd\nYW9k\r\n=8f3J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICsmgsspybac/u49C86ivzTsirzod4FUbdKKXb5OUYvDAiBgYLLl3lOA92mhMZq81wT9ylFtN8PnO80Vrs0qZN48tA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190128152439_1548689267312_0.826577953611636"},"_hasShrinkwrap":false},"1.3.0-master.20190128154917":{"name":"@atomist/sdm","version":"1.3.0-master.20190128154917","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"212118246757554e4e87bacdeb53ce64150e320b","_id":"@atomist/sdm@1.3.0-master.20190128154917","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-a/SZ+Itdry9ZuBlU2U7eNdTTdexx862JM29+eh5oYKxe2l3ilttyXrK0NaSva344XJtq+5Cn1x7qyApkcmjfgg==","shasum":"7fc92aa25c0c81ad574eb3946a5634ec3bcec4d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190128154917.tgz","fileCount":728,"unpackedSize":5541595,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTyVZCRA9TVsSAnZWagAAFI4P/0O9Uke7SRMfTy8JiPTT\nmS5b074uo4y1vO/IprJgRgkvsGsnxPCny8WBeIVN4qDBoxQL2ApoI9WcZZl6\nfO+yck5o6BWF+F86qqKmSlYCtk52EOqBt7/bKTwlXjyComCc+ULRMXH3reZB\nwOn1fB/4WlN7gSmPJoRLFWgEZZkqk+s7/Qrelgr2RlGocNhNkiquaKT3nalC\nIgnpTmbIOA+PhJXEewcU5s/bUOCETWF72KPeJjROpdcYCDZYVORkrL6DSScD\nO5HdzkTdOXbb/4AyNfh7VXMcH79DQ+5DjbSz0wQGCBnT2p4d39iBIs4t9Kt8\n6r3e/hGMdEHLnsiaXjPzLLLaoc1nPnUWhq1dwNN1kPNdpAlGy/c8MHSGkZvM\nJ5Yp72dtLmdOd05tZG2m2h1ALCrXKKgypHX3ECXgdhP5EyDz8/tFnPHG322v\nJgax//YtCJv3V1B14TAgW8UIEQaWKCtjuwFG9YEgFJ3F1n5AAPc6xKDbS40M\nZFOm/F0PUt1BaKg4f2Yy5hxKetM96DkGfHo7Nls7zCznsI7YYoAxTlpmo7AS\nVU1EE8DsKnDUdazrb4yQ6DUJfqyl2GFZWM2Z78ZIDBbY550w8TQ0J8lN3JRH\nC2Xt9r8etEAXkn6bQcndKFFTieg8ECYa4AAeeqLqekhK8Jn5YDweBif2K//s\nDGpx\r\n=eEfb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE3SPkXGo6Zflnfypb8KwvHofzsy/ZYgROm18Ef+nWCjAiEAkSGwm2JRiDLmTYpBS69SINqA9WF9SBe0bqrv8yLECvA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190128154917_1548690776944_0.09220906834897913"},"_hasShrinkwrap":false},"1.3.0-master.20190129200809":{"name":"@atomist/sdm","version":"1.3.0-master.20190129200809","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa9be874a5c2856710214cc55fa966a03fb26037","_id":"@atomist/sdm@1.3.0-master.20190129200809","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-A+FcB1ZEXT4Wf2idsLZPBhR8hCVTXBF/tG7giP7cSIxhlsr0/xWnS2SmfR3b4ykMNMmqxu3/ZXkT/btCHpKIug==","shasum":"e48511d7554644f9604926294923c0786a7e385e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190129200809.tgz","fileCount":728,"unpackedSize":5542118,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcULN+CRA9TVsSAnZWagAAMj4P/RuAihjwmWEwxl86GjW6\n/YpUOKCu49mbDy5s51MDxfbjDSOJ/BotlZP79VljqKpOjA49S+Sn3eHenDMz\nSYiAXreaXKvi/vqwx4sze/QPmoVSVwwW1767CiGaqVVlMrTJaDmMCKO19RzL\nkisB1ZsEAm/RHZxS8gtbeVr4iZI8g+hvoYc/tPNefxM+XUKuPMMuNJSrzPpR\nYGWh5VNA4NNowgmHysUB9mxJV5JLdONMWpcAQ/3e1/POYAjkhoEmLUAXQdY6\n+CDni7Gp1GA6TVCs/pncpcK734sSO1dI89+XzHgpp6ym3l1Dn9MItl4iBgIO\njPRei1x10e0cW9ggGi1S7DbzOwAoGaMm4SqjlVjXs7GpTTqAXP1Rlz7mwjhj\nyQ4PN3EyOvB5TE8SLhR1DTGcEtEsGDVD6TF74ZO3cX19FFTGGLEMDY/Wcy6u\nEgtH/JrK2WVUpafI1XupurcKjJ/s5dTyuw9hd6YOWoQ2tSKUdcUCjKm/joir\ndEcHArWP0MTK3UtBSWyxzNqiwUFjIqIYGgOP/TPJVB+z5Uj1nyFXyuI0iiES\nRTG59p3mqSsQ3t8IhILJ04rf5OguvFtl0o6s+nUyeJ/aA12qxLFdJXRCwolw\nNGWvXOa0a/QFBi29kb+zzekz3sMoHCqDnCCdyQF7JrlArxmjALIAq8S2QTOU\ngURR\r\n=L0dm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICd4vlHYs2ABkp7Lziw/pWYQZa6ZWugsHAoTS3+EvOD9AiEA42cVq7Lqpt2DYuFMog961sliexDsNmBPb6B+//2eOv0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190129200809_1548792701745_0.6175651212486317"},"_hasShrinkwrap":false},"1.3.0-master.20190129201953":{"name":"@atomist/sdm","version":"1.3.0-master.20190129201953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1385f79357910c9d9bbc2ee102e5fee43c199fc8","_id":"@atomist/sdm@1.3.0-master.20190129201953","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Wa7+1Rszu0cuu2jD6b61Bo4XyYJ1OwWk5CvJBnM44hxAZzhapwjGYfFuY4MiCJyXyulUYhAp4Axet1GVdwJ15g==","shasum":"e4401dcab43cdc12ea40d6e4228a0586cfbd2f5d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190129201953.tgz","fileCount":731,"unpackedSize":5543149,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcULZACRA9TVsSAnZWagAAIigP/iCbMjY7a0NoPvf1Vgdj\nB+7Vyb2wMPkAajlP78obytY3mMeIR+kZTBJzM9jpGPctYwRkKboON3VjhnTB\nJZzcRRzk7rYa2Yyv8RgSetZ0+zOtpPU0Dn66i2AXyWtq5xc4+XmEwrNtNNOg\nIwhhO0y8sf1SUjZzbcI8y1vIipV70TBXqufSdUfJfYTo+F96+y7+gcf/sMKK\nmQK018X6hoaJJzad570+MPGCq0cE0YR2QWiUJqYu0nsIzv5Hzz8pJW7N7pny\nFAldsAwOQ61SBfdwV1V4QzF/Jtzwn7QJIz0qmRg9HATJ9nfRkKumVyLz3Hkp\ntmUA+7bqpmhxu+wEdgpfpvxuLSQd7LAzY1qc14OU61P5ivJHBNkag17NMnlG\nsQHyDlEl9n8rmTU2JU1EOzit2K88ql8McSRFOCwlU+QRSPMBiYR56IsDTKEm\nnHxiahLvYEVOSztdeHESGcoeEpXnx2gX5D5iXAo8Y7+e0hGSOmvTffvnLeZ7\nNVCRUJFzPrgYanH4G5L9IuS/S5uf0aEUMw3X1MKJ+vU2fu2WjZZ9HU3YwWuq\nb+2WIKnvITsinzhs96llRzhK6zVlVKqw2PZ6P0FY82R4SPU5d7nGa3wpi3pn\nH5fsXnmT4tS2zBwCW2EW77Mg/UmOFAMFXq5PNmj2Uu0/WydSWj4ynXcdc3s/\nDkrR\r\n=iRIS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGZaV0hLSG6TwegFcHjT4rJIsCiBDoubEhgAqWVpilhGAiEA2NJoeskAty3m1xQxzNTjV7JUj98gKKiGJ7gNgdRueQ4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190129201953_1548793407277_0.736774243589422"},"_hasShrinkwrap":false},"1.3.0-master.20190129202210":{"name":"@atomist/sdm","version":"1.3.0-master.20190129202210","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1385f79357910c9d9bbc2ee102e5fee43c199fc8","_id":"@atomist/sdm@1.3.0-master.20190129202210","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ymoPcusMWs9jdx09Xk84XqsU7gChbCxc6y29V5yUdlPkto2x0MUrl1Omlbbau2d9dUI9pzRQhuyrod40gPMJ0g==","shasum":"2972cbfab17216a524c5d3dfa2d248a5148f6cae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190129202210.tgz","fileCount":731,"unpackedSize":5543149,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcULanCRA9TVsSAnZWagAAPa0P/ijc0/JsybIbVVXL3NSu\nGdUTaSMUb9W2ioX2ci/mcoIYoGbmRgX5woCfvksvC72TOE6YrxmDoGD0Suws\nMwHYO8U1uXlcbCHp6JnvRmy7KiZ7DQ5pk2KPUWPu+94rVipq7YljUlVp5VwM\nQz0uD9vMiAbnkNZ444F8IYIqhztJJ/jTt3T1u9I0VDl4uhZAeTM6YakyBmts\nCLevULGvxkiYQXlLo0+HklEPJehc8v2SZWowVbXHBk2smYCU++hrKgXO/Aqs\ndUnJWtUYQldvdFTb1EobFVuNQZQu1PfwalNdEXJWPIZZVwfXREkYZZVSkCrM\nhhJLi1nwxVaCm6PIBAxuq/iMORr2SvVtPJ1s728m7VSUSLbVVyW+no3UAa/k\ncXjNcK2FVlAw0Yb5i0OzObRXBkSbBRqr/wKQmAIZ7fdkDgHiq+42M0u1Z53m\nuVYAVojoILSA8jYvhwnviy/rLP75MiyzsPZnylpZuP7IZaJJV4pMXQ3wWHNx\nvA0/uvW9bJPnAWyYA7H+dYyv27aJ5ngx0909JLdUpkraetZQ2YdVHZClDQOX\nn/X+EwgCdjQQk2Lvryqv4DrsB9mQLFDLJvS4jdAHHwHQpTJVDECS25+GrWfl\n3rQlzUlyH77wI3HoxSP1F5WWLZgrz0OFRtqgL4BSg2BuvHnncXP9hXDxajEb\n+56I\r\n=Uf1a\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDMDCE1nhKxvHkLWaU5XhktlkfkiHse3yY2Gn0DRRvusgIhANNVgzJqU/+n5IWWg0KCbXb2W/xHlz2FR9NdbSvNZQb2"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190129202210_1548793510628_0.6186638868353411"},"_hasShrinkwrap":false},"1.3.0-master.20190129205714":{"name":"@atomist/sdm","version":"1.3.0-master.20190129205714","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"87975ead7fad8265140852e50c6ec616b9484ce9","_id":"@atomist/sdm@1.3.0-master.20190129205714","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-onNIz4zxTe0KMurbcL9ptpxt6iYqJEWtKxIQn/t9Xsl1A9A3U4DnYOKKiloe6s+EGANW/cmtSAafKdmL7N5J4A==","shasum":"c1ba6b75b72f7b53e82214171b7c40ae77c532ae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190129205714.tgz","fileCount":731,"unpackedSize":5543421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUL7hCRA9TVsSAnZWagAAl5UP/iq5OMuXWdO4C9+N5Bwc\nZ/Rti5058EvO2zdyHNB7fEXVwnnCCX/i0FtUJHFkgvp1cn2qBAp+EFRXfl/B\nTMSPox6GUbSqqxkmuSk8C8QLVSVVEyYypW9f8emJYXrnqZw2/pcUfQD2xRND\nwo3LZm+c8tsvsA+iWrbaDuee7w+hFOWJB4TQks+zKsHAQWUcF0tcjZA+Y6vh\nc28C3qF7/xH+T1eLPI94nNS+s6MMq1EofYKLPKlrE+tMCHyGy4SHv10I/sww\n+h/m3YiEu7MWchJ+zRLF2QeQsOsv9XFR1mnIJuqVkFUo8E2IsyGEmIziOWE1\nxCXN5ae04B4yWVMaQM5NjVQVu+ORPAolSTGtveECy7loBEmV01JjmHGtOW5x\nsy+sT5KMw8+c84CPX/X37e2yqepR7TDpIhzfe7C3NmLFJDeAcwquv4ZmqbVN\nUPWmw9PlU3qZBoO9KaiXQmJqfV7ObV0aArY3XCfi8B1JzYP43rpp7DCkyY/z\nh6u9eyWq4oS3BAY5vWhu64LfdaQnBuoa30TkbscO/shQamFxYAaaI3+ENa3N\nZNRDo/x5g1MfIuUlfuNDk5CYAbNRXlssA5BUhGLT20sfM6KzHH4d5oq2KQ5W\nKQCnJj62/BUhjmTizCb8FOTOLxIK6riJdPnRNZLI9hVj511AKRlC4SRvJxI+\nCwhm\r\n=MR3+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDihtU782G9fMtGztpc+LymrEadGbzwC6awG388s4GbggIhAMTpBocGQO+U4PAZ2aOK809t/hONx4k4jnGyONKW19T+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190129205714_1548795616603_0.3257663978187266"},"_hasShrinkwrap":false},"1.3.0-nortissej.typedoc-transformPresentation.20190129220519":{"name":"@atomist/sdm","version":"1.3.0-nortissej.typedoc-transformPresentation.20190129220519","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"986fc4802868e60dc9cc510d3a5af50c41690e7c","_id":"@atomist/sdm@1.3.0-nortissej.typedoc-transformPresentation.20190129220519","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yj7ygdH/W1RdS+t8KdLar9c6y9OzEypXQ1UtkW3ctJatZodgUcb/v2iEHpEJC7BYTXBuqoiQ0s3zze55Q95oUw==","shasum":"9340a8060a76bf81d2834c00a3de3a157b63a164","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.typedoc-transformPresentation.20190129220519.tgz","fileCount":731,"unpackedSize":5543874,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUM8PCRA9TVsSAnZWagAAqJ8P/jORWTfmgDe/EoFWPB9a\nC+250Z4PF9KGE7ivpQOnzCC0S36rm2drZRAvgcOoQbjSv3nH2tOENBtSX/Y7\nJofN2fyxed7W8GUVp2YMWR1uUfCxAA3TNPXz+9kwJFncIRg3IUxIoc7tmMhF\nE2KCDXxkUwEYLqsfQdDdM2BMTP5TDE+wU2hp28W3iLaz5HLB6GjK9qMRZpSN\n4155NtTpzn+6ph/MMI0Uu3r3BWBnnJpd9l1UZiZOobCjDpQVDwMvnu64l5c+\nL49BocBQ1iGY8i7FqUUBBTdiYNrOacpVY7ANTl8uNA/8fsqUmB+qCjDs6Yg/\n0zx4hYT4Qd5MAWLXBAA0ahXZ35SzVZ1kRFDV6puk7wFV1frtdWCjgA9Tc4OD\ncY7S8TCkj6dq0PtH7+hPjl7vZMylDcon41mjO3oKAiwFZIG0hd72AaVk2GBz\nhbFTcpSNpNJpYHkv9WJTJl1rDbuzDTVbgw4viETBaxnc64ESimbUkRZKeSD6\nU4zImxkboUDP/bRWLLeDIrZtxzKXuJdvhCqyRRb/PGPtsXodljQ0lqmakpi1\n1q9EFcVQYzK2AG39OOhXByVKC+VtSg9upYudb2b5el0g7qfMdF6aMQNb9Hne\nDmcOW8AIkbsLJJopWYEdYbPXToig2sWsQ1Td0qTz1FSIrgq0z9/pOFx/B4DF\nsxZy\r\n=lAtB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsq4/VN/unwq9WziRlIOvPE9MLgNoV3smfa3/RBGINAgIhAPUVU+im6esILSlzX1erFwKO2dLYzb1Ciff/RwvetVmp"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.typedoc-transformPresentation.20190129220519_1548799758658_0.012456430353093184"},"_hasShrinkwrap":false},"1.3.0-nortissej.onTransformResults.20190129232536":{"name":"@atomist/sdm","version":"1.3.0-nortissej.onTransformResults.20190129232536","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e6e2f64090d0a89020d0e8e54bacd2d08ccb920","_id":"@atomist/sdm@1.3.0-nortissej.onTransformResults.20190129232536","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LTz8bF/wjgiy+bNovnA++srSzqee+DuR4guFspln0ExDVsyK7dSE7TJLbeKdqkVVgmpLcA4+5veZBQpYOirCKQ==","shasum":"7215b6e408f5c7831cabf6d8f366d10261ecd3b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.onTransformResults.20190129232536.tgz","fileCount":734,"unpackedSize":5550354,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUOGjCRA9TVsSAnZWagAAd4oP/0gV1GY20pc/PiGii1J5\nEmwhj0nvW7u4ac5lxiJx7kMZv0Rh7/oyuUU20yu8ULHdYXqyb8LDxoz7rsqN\niLeg7AqFSYI8m1kSCq+AnEVGMhOC72I4Wi21b09oFdtCd+c5OztyHO7EZZEQ\nP42Zp6IF5Y2swkpe6k5TbGtD+eayh/1tdmFQm2QsdNTHqLJlI0942sJtTHlM\niD13ZB2EpAO6Yg91TRiyJvu363UKAMDAj9KlTvSVlUnL0zIP2Wsn+jgrzvxs\n6v1jzKMsoaxrqfXYPNv7eqm/3mA7W4NxLcRruKqkiP22pynRGJG5GFqvwbRJ\nbuMCZ5J2eMR7oNCxkyaL9U5CQvdBN5EiqL43gSlPJ9LwTn+oqNM+hngYE7LG\n/zGvQAMfo3AHz37cEia1zSKoTZNhk3kCi88N1ERud0lpK9oaWIo5/U4QaCSi\nUqLBQxjtOZaoEQWVcQd1SqA919sIF9PahrZbhuVo2OReg3pRVXynl2E2WkD8\nrRgoQL8QgTpbKEdGS6rh8mQ8x1T5U16bSiEWL9IqObsWtieddjZ7+qvvoA8i\nepzFmmJulO+CdM1/I/x9PrYSksxwJOMrYIuISvkgX5OzDwzdIYbk8ksSC2Lp\nhL2kgRsaRaciC/tmjlE3kmVLdEHGqjQXn3czA+bJkez5ekJipzRVLwu5vYyw\nwUlx\r\n=QQGS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEDUIgRWNseHukxvguQBcsbkNeQ0mC1w08T7XFod3WiFAiB/GErytEsvnOzVfHJ6kKBNaUn3LzjCdGNifHCEF3IoTA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.onTransformResults.20190129232536_1548804515044_0.6517840547161771"},"_hasShrinkwrap":false},"1.3.0-master.20190130172518":{"name":"@atomist/sdm","version":"1.3.0-master.20190130172518","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b53e3aa4355eb0df71ee33e837cdb55e6aa05d53","_id":"@atomist/sdm@1.3.0-master.20190130172518","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RnVVY818ly8cG4ohTXR1nfIFwTNuBEHf8cgOv2rFj+W5LsSScKmzjuuVqYuLMFnxjG54f4MPuZPT8HCjVYA79w==","shasum":"461fd0cbcc1c49dda08d7a5ce6e7d4a5811e1dd2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190130172518.tgz","fileCount":731,"unpackedSize":5543949,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUd7KCRA9TVsSAnZWagAA/v8P/0WyUKidaMRtMmkqQzjw\nxDWJm1eJ8jUIzVX/12U81jOPfJruMRqcOEw6a/Z4JSWiAWSjPB122MIBjIqk\nJQ73WASgfAQ0wToIJK57yQ75KPhJ8brI7RMI827kC+XQ8HWPhgTEwbCe1TvV\nOnFWof/KoGwvj3gjzJGCP0LWs2zlA2UouEl05DADbs0AfS9ljXX5U1I2zLSs\nIOe4MYdrT+GJZgMgWjbSLjTTFpwGBGTuhMVmbPbnOB40JTSMHTSCy3jgChWt\nMmODuLJcL4Rn8kPjI/UG26kswaExZyk1vjUpTpVwzT6F6EPnmMQrcNntWFam\nwr8Vm6DrolGp1CI9f1upu8OWPMUhZvnm4lsO2m0kpzZOBfH7VKp+Y3sqruFp\nmc9vkC43OWZi+LeqmmOjGQ3q6bFUd1V++ESUpGD2XBATg6Savkw23GKGJ9C+\nuV5hIPPfzzjTCs24R4xEtI07XQj82WOxKKIPoJ3BJRmasuCwgmLMhQLhx6su\nuD5LPfk0Nh+1rRdD3yudbJBMiHj8nJvVNdAej3hat4WcH5qBC6Flf82dMJAb\n+KqZ+DfuyPoSAQQ4WkXBrfyv1ltUvTd7StuavvxPG2qP+05NfoqYezaNEH3K\n+KnhHyjq1Pny7kk5z0/VmxtBX2KN3lf2kPZ9FOQ3lQfK1sySDbOWQLPrM7TO\nZkgl\r\n=GVtX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBkLFi5C1b79ZJMTUd/m8AF+aofzF+RdbkFp2UA2FexgIhALnRXXCdRCIhfLPvYUY4mjIDVkk0XfHNwUdmfzahcEm+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190130172518_1548869321481_0.10977486416455529"},"_hasShrinkwrap":false},"1.3.0-master.20190131081428":{"name":"@atomist/sdm","version":"1.3.0-master.20190131081428","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa3d27682c0f595c91e02af7875d500a622f83bd","_id":"@atomist/sdm@1.3.0-master.20190131081428","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-czdghkoP10kld2YqJXEeFLoyH0IiP2xACBCnMjthVjgqcb7r4ww2uOBHdRn9oCVSZcQNUMt4aqK1fNJp+gqIZw==","shasum":"313461af5a74ab3e2a03d96c4c757861e2ce2932","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190131081428.tgz","fileCount":731,"unpackedSize":5450841,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUq84CRA9TVsSAnZWagAAzD8P/j6V5W2sAQeaLj5c4ndq\ny4Pa/uCyXxa3Ju+SeHwNEOl7H/rB/B2MnxbiFIzK7or1nn9Qsyz7tliAlCgI\n0OnRJvMzfLdTH8RtxgskLlN7e8mI0P+T9SCDXqySl5fSu3bVxSF/GAAea5Df\n0qPTetmbD4BUVOdu8qb/XECeKRli0CYA7OhM/z4Xd+CMK0yFl2P/MmV94Sxe\nZDwLzCv5P4YKOC1VOLyfnXgplMrghbA9d1vdyjimRh2YmKFhVN0y15ix0zy4\nXuBYILA5n6ZLoRFmo2zvdb4a7yqeiugb7pr96kAcqSlmaYnJPZEVA6wzGnu6\nY+AHrhMYjYT0lc/PJiWcB60PUwa2Zqj/bd3667vBEr8kOihdfF5trTf91SN9\n+fCnW5DpZCcW9NnqaxzypxbkABb6iJez08jg0HPNpDNG3Z40HyxC22In6RdN\nrBunOeF8G6cpkP4hVH2AbpR5+ru0oe/+Qp4aMxw8nZVfKNGkPeJz0jFHfq0i\nENO50aZtM/hlb1lSNAqb/IiUrFXklI6EaQQ8+GCa0AqjSE4MYo0pyboxlmxY\nOfM63yAl/ZPpv7aUYlpP1edRgCohgU0NJ2u8PbQZFY17NjWyJ6q5JEvSKnQo\nCDwCM/v13bnKmfQdu1eJUiU/kKf/0bfvlTjYXifVTpPdxQjjkW8DwS6W2PGt\nGhH9\r\n=iwHb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCTnZaeQaBUahdhrPJ09yIU3PrKp9kEAJnJ+3qXn0cy3QIgHNqff7c9Rlwrp1WQbapiiKI+X2l1WB6d91zz8a3as0w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190131081428_1548922679592_0.2833829984966869"},"_hasShrinkwrap":false},"1.3.0-master.20190131081656":{"name":"@atomist/sdm","version":"1.3.0-master.20190131081656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"aa3d27682c0f595c91e02af7875d500a622f83bd","_id":"@atomist/sdm@1.3.0-master.20190131081656","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RvhXEadPsc8wVRv7xUlH4EYS5x733l+W/vrx5R9RvqWR9bJap74ZfuL1r2+mXd5DOOyNbP8rhkwrifqh9JBGyg==","shasum":"f95a12b2e5fc2128a41c52befb421dc5227fb067","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190131081656.tgz","fileCount":731,"unpackedSize":5450841,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUq/BCRA9TVsSAnZWagAAM1AP/3hL1lqfCZ+E3tBfyzHo\n20dq4Dbs3Tl7dZ0STQOudz8eOxzURZ3W8lkOVtJj/EVibPW+yR993BQGIMG5\nDSSdLAfrb2/yvLy+SCThuw3Pi2/l4nGKqzF7viJvvZs1PuoIAyC9P4DaDhFV\nJkdHTJy9oseIwHssiCboUX3mdSVGWIUxPfDk1q2/jo23wDyImJJ4DLmvGAcV\nT+cECgWtOz+3y74iSOZF+by5P7pzNELHwmqu1WgaQ+Dua44w7JQd/yrihseD\nFdsg/MIPG0pJA+75ni1+Pz7SX3qFzXUoBu+O2X38iFIhzu2Uq7YG5g5oRY4u\ndxRj9CCEYCX1aQ1Z/NoGq9uByP2wjq90jlBQRoteoPp0tVa0xzGjkm7BoXmG\nf71I0fQj7fe1SY8qS+JofNtrbLmT/l8Yi42HH9QihCewLQD3ZclQ4PH6o6Ee\nPC8/B+wp9+VycTbluSBN7mFOym+THXxnFb/aM2Nv6hn51Rzj/eV2fy+suDA9\n/GKEcb/2LPHQxojHPLF7Pv/B+NkQK6q8VlTnoP9iAPEegybJj1o78KfX//D+\nrWhNxmT32nvqFkKC1YC1aBLh/zV+YBlX9FjkP3fNTFSdnoj31tFcB08u2woh\niJ1qTTD2poPDtS195MYoRmy9jmtsN05FHfRBWJrxtYlTkc7DYJfQA5Huzp+5\nVANW\r\n=1JSG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzQkJs9+PWnnYPBCwrdryaurIORnXTywlHaDtf9dXSWwIhANGCkhjAzJOUU6XnB9kGghMkTR9a+nAOm973/4gyiKRt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190131081656_1548922816506_0.902663396041222"},"_hasShrinkwrap":false},"1.3.0-master.20190131082854":{"name":"@atomist/sdm","version":"1.3.0-master.20190131082854","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c04d1a958460ddcbf83c232b6b6da44df31708a9","_id":"@atomist/sdm@1.3.0-master.20190131082854","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-RyHMrBUzwC6OamXry3BqU2XyB+tz2FSQeJRur32Eoz7/z6OE8291Vi1zSgVRfYIDLV1yTUcClpYhcxKZ7eukmQ==","shasum":"2b98376561bfd137e0c6ceb1ed645ce913166707","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190131082854.tgz","fileCount":731,"unpackedSize":5544322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUrKYCRA9TVsSAnZWagAAq2wP+QExTK7lmyS9zutudGD4\ne6X9ppZHb5PF6cQK/j36aRNy/OPJBJxDSSGzRVbJY2lrW80US+40FPlh60do\nM50k1R5FeaGF8ijHMsjULriBS0NwMmhluGnUeXcIRLPDtiIX/S8c3z/RV12n\np8egPXVV4+mZGMUwe63D+UecZ199ZMgm/xQ81iKqwsD7gYdqSWZScx38XXEc\nZAhBwXIMP+cHqldWOycmwKJXdcQKDuCFItkxN6Lae49cGefkdJiOq3tmVHO+\n/HdOhgtwZlEbIuAl+dOt4W+6DmCh8QglX7lMm9ZXoAi8LtbIm9YUoLxIXkb2\n3Mn5LN1AAUSWZLU5CXpwJJEoTz5IDOg1FE9VuM0n5y5lOqZfJOATbqDYrcw6\nHd7hba+M62RbTc71ReuNOB69VsXP0C34HlmgxSCGUM2TXPn82M3UAPGj6djP\nHBcuf0hfmX138OOgli1rlxtWf1qsrYzuZeDM3cLMYTJxIDEvAVAavfOt6xGa\nZFRyDnxaTK0EFbFevbkCBhGmQoiZbEIvS0P/xnjcHekUhm6E1TvUdhwIn0cX\n4IXo0nkXHraIDA7aXYbp6+rr4IAsfGI+5hx3ckxLmsOvbJIJ0cZglz0wy/AA\nO2ouuYumw4P9OHCaQ3+Lj+14UjJwJmkgKRkUfUhF0oKVqeYiiBEXdZkcoz6P\nd9is\r\n=4//z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGFoCFZJPnq9oF6lj/D92uNh9jEyC07Ru58Y2gEhOz3YAiBAswPqa676jAKUMWm5voBNosXNli2yKYk6O4inhPCMOQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190131082854_1548923543710_0.34914338603084416"},"_hasShrinkwrap":false},"1.3.0-master.20190131083054":{"name":"@atomist/sdm","version":"1.3.0-master.20190131083054","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c04d1a958460ddcbf83c232b6b6da44df31708a9","_id":"@atomist/sdm@1.3.0-master.20190131083054","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-DXjYjYbRfma4DocGK4XN+2JC/bACuJfzilchTQ+RleV9NFq4ZhkuibVpPKpoaO5uQs69i4wFG4tZ8QajfJsUpg==","shasum":"288aa0bb84dabc587f46dc984552482b4be88528","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190131083054.tgz","fileCount":731,"unpackedSize":5544322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcUrMECRA9TVsSAnZWagAAz94P/jvqjLA0qeUG1G8jXN3f\nNNo4wi/M3g4jdnLphjfZ8892fBtP5lJkLqgkYUu12bPz/rktmMCnkvjtHje1\nctA0teWNSS/XgFuiiHXFy0f/MaNSW5HnFQT6gDpt5PNFMRQRPBZOXiVCx0oi\nAzTlRuvF+Ype2nSuwz2knVyqnMpw9T3x+dJvj+RAqtZ8H+9oNA4Fr0qvwyri\nkbUN4+pHRH0hn1o1vxLRenR+9J5yEv1UEuA5Fvoy7YPjV6CLtLRe/t+mAWC8\nsFHFAcIbO/AXrrbxVl7BuYf28ZpBo7Ci4JV8YLonmu8Ll+lgcHk4R6fEX+Cq\nGW/dw9nneD4LBJWiD0qwRZzGoe3w+X+zT5L8CcZ4gcQJ09qym54DXxOFINyk\njDyaniwneL3susJ4lWscTzIc7VpXk952c0AFdGxmj2HHyEA9J8T74skCUMwB\npW17PHXHW8YmQ0+R+nfqLk2FubkK6iq/EV1BMxj9VisrFNwPFvszshGmEYlO\nbm9U2oxVrTbjZ+hUSQFE+AuisOxYfKu0UC7RjW+67CpKFoJhSfC2rz5JF4ZK\nQNVoy0grEijXKaMPkwu9zal6sNnxWDdTh0wsWrKCwZBTwUQZRteM71ND0xoJ\nz9kqaAO1fQgL4dnWHUb9cbv1OcZ4l8XyfQ9Hnh328r9bMwB6yxnuoDXH6y4p\ns9y3\r\n=8Acl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFHyeTo2MfEf9Fc8NCAJRbStcOZC9TPyVFkcY8Yc29zZAiEAqAaIzTnEqdX5hgxPoEuKUxVdAbKWMemNq30y8bc8b8A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190131083054_1548923652062_0.9264215054196709"},"_hasShrinkwrap":false},"1.3.0-master.20190202151920":{"name":"@atomist/sdm","version":"1.3.0-master.20190202151920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f836dcfeb1342cf1327c0c4aa990595b3a636c3c","_id":"@atomist/sdm@1.3.0-master.20190202151920","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-cYjAWgLv57YC296HJhzvHp8hDpaCnRrgeu+qkq1t9WhE90BXBN5xQAg87/yNx0lGF5xBBgSgJ8ey4SSVR2cVCw==","shasum":"7069755d13b792a2488f9f35df1546b13f64580d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190202151920.tgz","fileCount":731,"unpackedSize":5544392,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVbW8CRA9TVsSAnZWagAACbYP/R3xncUtpqLrAseRLsLY\nz/EwE9OcqBkBe3QK7BcW9FwU6nR0ev3GfzrbYd9fx06h0AgaS6lFqK9Dx95f\nADlQqQxMUoxXg07+5xNMGt2Da0S+c9SVjPyiavvSJIJoL+5SkPTPLEE0Tju6\n1FstQEHEg1IYVUEYf1M5AAVgcP9uvXXZS6ysIqyTXHXiH9cFLOCSIgUjCyKa\nP8sFcx350ExDnVRn3+9nFhvunejmn+B1eLls6gZbdkBk4wsdlEzWmntk8Fme\npr3v6Mzp/7uFAXMMwuWKMwe5gedfwSAABULe356BJboghJeGviIZINlv8Y/i\n20Jw9ep9xH6IW5cH1N4mDuDKMsbAcE+hrm1E7U0TieHx3pj3Y9e/EQZD6VPM\nJpHS+0MbQ+oCOoPP4j/BNh4Pfu6DXKcTv6jpQZ9C93Q4OfduNnw5RsBNWQgJ\nmO03Y2Y7ZDzr/ec5wZR4KsFxldCzn2z0hoQxUPCwFzT0P8h83JAI1z2Iz5Au\nlmdckQBFMEJcDbWvJNEl+68bO8rKLGbSgIZJtGkhymkXQhL5hV23tOsF0o1V\nA/yZqMlapYk6FsrSvCMN/mtDfO+d2cwGVUNQctFbh14U8Z+upf1jMQrAGzdb\nd9NYASKXKRwTdeg/0gRIWbtZvrdi3i+8FA5VFYUdpjoVmqd9ZgKSCwN7cK33\ndjeo\r\n=r8J1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRSzx7scAhDddB0RkdVXJ9C7IdUKQaSVIykN8q7/M0qQIhANs7YAxAAhQ5fhJG5R9X5rtiAVjfuKJejt9TgCCtdazD"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190202151920_1549120955982_0.739892133717593"},"_hasShrinkwrap":false},"1.3.0-master.20190202163411":{"name":"@atomist/sdm","version":"1.3.0-master.20190202163411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1d54fc40d9e3871ad7b02ce6b4755b16e48e25cd","_id":"@atomist/sdm@1.3.0-master.20190202163411","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gIi6hOupATDPi+UVaMqC6DTy88822IUcdm2fx1/3iUGzdjyiKsJxxfgRsRZDcF2lBiZ8PG3UdbXPTyXxPXIxzA==","shasum":"0e69dac7a172c53fd1bd117722d5e6ac287b16aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190202163411.tgz","fileCount":731,"unpackedSize":5544546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVcc5CRA9TVsSAnZWagAAsDkQAKRXuWYVG6prb2uxfLjH\nCGAzdUmgRua3LvO9hP54bs3j4PYs3QFWtqedKar5b44Q+aFHYZ/ybGqZ03qt\n56+NpsHCYgB9nAEIIHNXkbkaEVKy7UgHnA+/XgiXv+iG0YWMUX6aY/rxntpT\nyhTYhj7gw81zVcfnTYdUgX1y9cvz1i8/I6IuKn1pEmkyyB0qM+K4RmqDvd8V\nkUYld8MTJ5FM49HMKs+AhodI06cHG0S9n2aarva9EoDCWHorn7kH8mjnSBNZ\ntTsp1TCEka60ESNtEifSjDJiihWpihp/MJ6/lsV6co4HTVvVZ6urqyXjZtIV\nObTAc3PqNcbLR9T3WOzjWBpL9MDxb2eMVhWKnibSjryPws4Sj9IjQ8LsZitg\nS2nBDUY5kQeNGfP7b4KKLWi977oV74gfxtE28JY3mwcJO20sYKg73IEXnvJ3\neX20VzuwWBSSXve90mRMKu+u0BH4boFTW6UWyhN6WO0VhSOOfSiO1zOrhTTg\nOw1dAIHN/Lrt26od6uPmQLMDnI5Brk+HFb3TYXsgjF5IzEAjqPJ32I+T787J\nznkUQb8/6U3zGc0BV5qMFBKIDfjAXYaW8h2OuPdNEVioPHlAeYYZLV4Ae8J0\nkeSMeOTK4LeE84gVHL+LI1UXPtQFBurjmz5nlPhvfYCTAb1GOoQlLxcjKlbO\nqcW6\r\n=B9ED\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAqEAWR22P4JkStb5Ch3IUHoP7mB6z/zRZWem1/ex0YmAiAgS56XDUCDxivg43ocXMsfqAsGT9XlVRXvnoFT845klQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190202163411_1549125432980_0.4474855823664905"},"_hasShrinkwrap":false},"1.3.0-master.20190202171433":{"name":"@atomist/sdm","version":"1.3.0-master.20190202171433","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b6355f5f53b66e00931caef624caebc2629b38ac","_id":"@atomist/sdm@1.3.0-master.20190202171433","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-bfUvoh7DDEuLk3U6ApMdbcmIXwmRPSnX0MzrsVnB2OiWESwwqfM2R9t3SrjEuZmMmmq0NWgvbi8E8/URB65hDA==","shasum":"e740ffbefa0e5f862640c5c45dc82688e09462ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190202171433.tgz","fileCount":731,"unpackedSize":5544590,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVdCvCRA9TVsSAnZWagAAE68P/0tvNBnqLvOgbT16xpqN\n6I1sr3+3HxyVwLMKKBRpiXdjtohg5+rsW+a8u244rqJQ9aDNqiim8x9Yq+cV\nkLTxKy3It3Qh6A13WzSXLXBOcPuR4RmElI5VgSHvfdg4Ih3+EJ1KmUh2Bhfv\nIJosQabWmA9gZkFHTyXgBkKUxYPovVxU832hgwgv3xs4mw86Lej3dd/t354b\nZIwwhjQftOLU/+DUO2gNY3JeIicw8/AJXefCrkcmZgc0/LImMpLXywI4aaft\noOtTdZojGRE+xdIio/OIuqSGf03C85xaJNkzMRJhJbzcLlc3htNL1nf/kXq2\nXgd+XKAtojwQ+oSuqM+g965883rOkWzrDvQi2S9L59tj2+i5kmk2nCd9BzN2\nLITpSYN1b2aODwXOPYxtVy8RkPurDBP2J2iUABMmZRCrVK1FKwbcoNnkWQce\nqj7NYyk3UNkhk9brrqNgPIp0Xhp3VMN5fBhLS0nPkWby/cmX0Hgg/Rf4hfYm\n+jZYGOaA+aexx2N88RwoJit8W47F1Q4G22mqwn1FzxxKGz08HVwOCV35ARdW\n4r5HBqkCE7iHEST1rS+AB2BlWvVO4i+NjFlzEHqPwvJ/M1Oph2rjCqzENaoX\nH5pC5k4SFanW74rv2nBTg3pL9f8kSV7OK789r0a1e6OF3GuETGgY7lF9w3BF\nu/M1\r\n=wpNq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDyaNRf0OkVut0prn2Tj9rgtJnXIfSfm5IA04HOPbxhDgIhANBVxdlQcF63SuzZOY38LxEvY/TJwBCoFfgyqbqey6r6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190202171433_1549127854350_0.7545090994791368"},"_hasShrinkwrap":false},"1.3.0-master.20190203161514":{"name":"@atomist/sdm","version":"1.3.0-master.20190203161514","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190123083033","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"62eabc3dc4c06458a11ec23ab6c9b66d71a690cc","_id":"@atomist/sdm@1.3.0-master.20190203161514","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-F5CAexUKoAsAdzV3ic0AAK+s8H0EbNVyKES1coYzbk995+Du6yoiHqDa6yAdZ1+9rgRPw6SPIYY3oaH9Bd2CQQ==","shasum":"e624df95826fe77fd111b7afb1a5d1b7eda0fd16","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190203161514.tgz","fileCount":731,"unpackedSize":5544592,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVxRLCRA9TVsSAnZWagAAhMgP/iXghGb5TzusljvavQ18\nr2Fm8ct4lnITs/eLROy7VGvlYXDXneNagDFaRZlpBckqKAOwJ3EadDsAS6sp\nOjRa5XKWYZsU1BDk4QZZRJ3xnWo5u83VN/MAlYnBCj00e3f6kOETwS0zwdzM\nw1kqNPF78ejJF6PVlWAfrwxupayVcWTz6VC3N86y8R/dskw3nxDCzh0V/WCl\nYOTJt4ZXKtmco/EGmti5lcQtOsg8W8noY1D7lWSAGpDuwQ/rMICigOYJozEE\n64asVzrXA4bFT7JH0B9gpcnnFGL8cnLSPnIhQR7aPD+7ckseI3iD4CTnY6xU\n3bUB/W+fIcXb8V7YV/pzDd1X3ALH2ho2xARObqLkjG5pkOCBsV0z8YhV1wHn\nLx/DH+nqTTxWkzKeg9jMxcspaT8Wwuj/W3l/EFwFxSIzkoUwTnDxTxvCpptm\nbnwcnk7QOjmLLL0sSqoBo5PUWHZ9BGKD665Jq2MtOEmK9Hd57ScBGtsp+8R3\nHM1UyVFnL+BdUwNqMvQODudDzoCDay66C4ZCryghqES8oXYl1GXzzaBxWerp\nYyhQD/iDhrNnVYNT+xKpIgSt09haOVM2iRlg4VFpk9406dVbj1wpYXknCz6g\n3w0Az/a/2fEWyv6krer9Xc/oFjCOv3quGTomyZwqiM9O7uBO02O3MzBztqDW\nKjur\r\n=O7ph\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICbOA1vkAwtEWIQlCYSiTdTt+SShxGjnQlivO3CPVlwGAiBPUVIbjbufmru6Joni9HkPCGlzvqEYyHYBIuaP0lzpTQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190203161514_1549210698460_0.6614517925786374"},"_hasShrinkwrap":false},"1.3.0-master.20190203165108":{"name":"@atomist/sdm","version":"1.3.0-master.20190203165108","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"70ee4693bd5bf0231a7763147edc077b6de05ff5","_id":"@atomist/sdm@1.3.0-master.20190203165108","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MlyhhJkGdPi8pgJyRG0BCez3ZEG7FsWpCQW9tMbW3AHbVyhxOfAUY9U+h1GDuMjoIaNsanm78QR2MZ1d8u+9iQ==","shasum":"f154986edc297647180cca010244ee1e1a061d7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190203165108.tgz","fileCount":731,"unpackedSize":5544314,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVxzSCRA9TVsSAnZWagAAEAcP/jArPr2CYZzwmopSErIr\ndvwgwTUWP1R+znLjCCXQ1aUNBoXGpU6qG+68fkcYwulLY63EsX0oK0ksDIed\nNAxZjTrVjIiDxHLLEHiF4S1K9NhjRH9HQUDGrZmEHEY2UFHcEBbgha+zS/oT\nBFHQChmjIrcieg9mr83CQD0UdHTICfnk7BCMxWtZEeg6kfHnwHYPVgXuYNS/\n5F2frTRcZt/0PoHIj4quWclqVJGtvR1Joi1JkTHaEw/YCmvlfKpfuux5GCMc\niYmjdh4I/nS0KdApGYBHWq31SUlVdJJa9GA0t9odeudW1lZyhtkxQ6VKcYTj\n1Sytq07ueLKpp7Oj3gzcZDAeR9MqEJJlNblFRXPjzwPobUaMJ0WHnmHblSF/\nYckFTUYual+d2cEKONvAnBxbjgXewYmckak0fgY/bW8MiIi3b0jQYecJY23S\nLjrLaufuFRxV/NgFY39NNhT0+FV5wlh91ti9buU8pNTccnS2UruXAKgqebqT\nwGZgyO6UuT3q52sDKImtqCt2bdrAsZq0VQmWNl2bLuiKQ5tvPtJ6kGLQnXgI\nAIfq9fmXfXYpwcoyH/bL8PrCp7fp0U5mbtLD3amkLUuchZaO3Qr/E/LGyTw7\npMpGRKN41e+5emcDMPVNUxZKAubWMRbLypojxvBuB9YyVTIDMA/s6Gqt8vGL\nMhie\r\n=Mt0A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGhmaTP2nQB8CTqeP2gfa/xOqr0woxIfQW3IjlJUJ5pgAiAZ7jCp1K8tEgyNhm0Sx4evEzPq1QRO43RROUhPNogufA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190203165108_1549212881918_0.740883753834559"},"_hasShrinkwrap":false},"1.3.0-master.20190203165250":{"name":"@atomist/sdm","version":"1.3.0-master.20190203165250","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"70ee4693bd5bf0231a7763147edc077b6de05ff5","_id":"@atomist/sdm@1.3.0-master.20190203165250","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ExEcOR3WYe5yN8s+R3L0ocjVwY3RggJKh9GX6WovDQ1zfc2pqJ7eIuQbyMbaq/w9/V41l97I5CimepL5crRDnQ==","shasum":"1872c1722977b4ec83fcfdc1071394ab10c1a73f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190203165250.tgz","fileCount":731,"unpackedSize":5544314,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcVx0YCRA9TVsSAnZWagAAGDMQAIA2qQRUMEMpGyq938OE\n4nQULOoS0uFyJcZQTUC3azqmAF0YNh4J9cgxAtqljbdFlKXgDdOunF4EgsPI\ng6B7XmvOB+a/f1Xk5UstXXh25Y25UOygNmOU2NLtlK1IlqKg0uMGVfkOTeAP\nGdLicMHX+0gtu4BtGtgXwmS0Qvg4PpPV12DK2Y8YQCQP3D6HoFB8Y4urNQvG\n41kcLkedtHavbFv6DAtLkdOTg7YVrKxrLkaU1tWMmvQHVW92I5Hg1JFUwRDM\nDBWzEOf/Ugv7/h3u5cStIbh1/8shyPJGjefkqpV8XjmSsLBbjoptmCgNLSnR\nPoiIdkLxd602PnSGgxn+wX3t1bis/QxLRVwOCPR7oUysE6VFI0iV1VEsjmPJ\nCQrAEQN7rItP4HkaqdLgYbByhTfLiAz6A6OzUiLmv1Z5vUHNTiN+leEzScSh\nQFibN/iQ7i81Yel7feNqnTTGj0cq9EaC7JlyiH8B1g9mbtNUk6V6oA/baA7t\n/ZULgprzD9jfp/EYhK5mmx2cFV5U1eMOSvQLcRkXpz/YASXH+KKqEgW6l8Xy\nxVVcHNkeiVPbnEVSe6x8VYYHKYS8guMkTia04wqwP4AKAfTC/GDd6ESbuzyx\nz42uO+JQYWamqb8600vICDH2acVY+p4hLyew/ALMExU/7U0MQ+z1OB0SiDzH\nNFW6\r\n=60+9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDnt9JH452XYeqfXbsYpi7Tmn1zqFCB6g86sBosTqUrJAiEAoQHOLv7U+qCdHT+xOPGmpK10B28/WgVKUsY5SBJvdNE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190203165250_1549212952051_0.009125961545358496"},"_hasShrinkwrap":false},"1.3.0-nortissej.progressLog-codeInspect.20190204203552":{"name":"@atomist/sdm","version":"1.3.0-nortissej.progressLog-codeInspect.20190204203552","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a057559ae3befcdf6048017f1555c246bc317fe1","_id":"@atomist/sdm@1.3.0-nortissej.progressLog-codeInspect.20190204203552","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Cp2V6qSmlBbSG3mzGcJZNYqSicGbqkEV/nnI5LpuWYpBL29uSsZvuTUh6kVtDw0z4ht93u2HAsTlJYojV20vXw==","shasum":"053c4aa2bc8f24f949a94a19d5e6ac0d2c8b8b19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.progressLog-codeInspect.20190204203552.tgz","fileCount":731,"unpackedSize":5545164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWKLbCRA9TVsSAnZWagAAyQQP/iBM2p0kdI/yckurshcy\nW31bknYOKWFFyITKj1+RQxf8Y8PxswD+pRDEv+31RhcGpTH6GAQEbAsBEtM1\nODGY/rkJRhvsETaRYTd67uWTsnyoMICSDoGMBhLu7lbgR1fTa/4NaojSVvIT\nzR+J0K+uziPI6dreDieTz/qlzZBksi3HDxFeGLqKdKBWUHw+QLRvqdNNuz8x\nrYR8CfwN4dnAt+0xqV3Na3uN71DuDBSbWm6ilfcz4MarwaCa4c/Z2tLKOcSS\ndRZdjbsjmdT3dPERganT5CU0XwazxHNDHwgXd2ZZrQFzpFyI1omsmisLyGyu\nYNu3biQiEprkT7fITls63wgihQKvQrqyFlcHCwkEuxKIZFimBPg9VuiSAHtB\n1iIbaXo3v5ym82OjQxrX6OwvGI8WsXwT/WNvphdUq9s6d+X1XdN+eOMbGl8Z\nrkmrlCqCb0EcoR9wIMhktt/D886Efl36GOyTkfThDQwWtH+7FPv2aczK0LGc\nK9niUVbFH65PFLEzOKSwHVTEnElIpHrGR4BFDcwdF17h8jGjge9xTUGXxXNt\nl3ob5xEkTHbveXo5cEEnSPfVnKxDtATK+ICRr50rW8t1L3a6AQ48i16kXxMY\nZD2J1vG9byCsDkvYwA3PLc1JkVWwaT0wJHHKtNWf9BP2BTZx4fUNgK3grhef\nzhK8\r\n=g1wI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCArFHO+hbQcQvQoa1sdGHETjtTAvSRW6E8WDMQ+FMdwwIhAIMyAMTN2gfrazzhwKFKEn1vCUh2DnncpkS6zW80ZPfh"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.progressLog-codeInspect.20190204203552_1549312730294_0.6856408441314465"},"_hasShrinkwrap":false},"1.3.0-nortissej.progressLog-codeInspect.20190204211245":{"name":"@atomist/sdm","version":"1.3.0-nortissej.progressLog-codeInspect.20190204211245","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c1018dd53e8458625b7f54d521ef900db924f51f","_id":"@atomist/sdm@1.3.0-nortissej.progressLog-codeInspect.20190204211245","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-eCV47TUr0/mnZczI22eXxByRc7dXhJn7eyHDk+41U7At36l+A1aRdSzUyiiQihd2hq0am/vOIby5Bl5jZXD5kw==","shasum":"993cb686e348553b839c19a5c4be1eb3b3e0b666","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.progressLog-codeInspect.20190204211245.tgz","fileCount":731,"unpackedSize":5546064,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWKuaCRA9TVsSAnZWagAArVUP/17CKz7GRZ7X99LpypqM\nTPzyCfamuZWcS19seYel++Gq8mPgvMn6DTsVp3j2P+0WGJ00ekZ3OZ8jootw\nDIEm8mB8d4A6lbI5fLtR1AJqwISfk9LVazEnvK+K9O6nneb+h+SuxdyTxD/T\nnzn0vkDLcUnyP6s0G+8Ffq3/ipTygtMFZ8Jlje/UeFzrdDFaW5tUnNX0sx4T\nh/fODMhomm6P5V0CePY9vB9li7HnrAIK3C9yQomT1bKYILOnbEf8TkwhYKds\nwmSGsckbTc8rXCWwXjOdkvRgrG3lPilkXKJQK86zV+Zj8cAtCv9ExspZCeaW\nwfLMRtsACbPphrYNqZ7UJB+5atVyc8QR4Vq4xttg9GBFT7o9JET2r1Qsm2Z2\nwgaPZMC5T0LV4zMqCw/pECbBz61GyRcSlsAq5C/3QVB4qtkPBZtM8JrD4COi\npT6yCt1ekFZWNU8cGWEF/PpQuPLj4UNh3rbImKnnc5tzjM3avpHIDNaop4ri\nGX7FMqcqszGHKdmW1hnKO4fBpz/3dIABuZXi6mGIPGPaxEy/cWxHuIEpqcY3\nnfQPP0uYd6ElOD83Ms4Gnw3XySJyb7EE4MuwcCKX3XxCg92vylJNSfVsNmD3\nB1L46bEGjy40WSK2z8PvAolgBy88sJm88QUmn1A8law4GzTqJ5kPdLc2/a/f\n8qlq\r\n=f6Fn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDvc38ld25v7HzMLPlldEl2mdFfpj94OylgKpN0xxzRsAiEA3m7c6cRhGQiCWxlTNJ5wz0qPR++ZiN2XWnfvult6oa4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.progressLog-codeInspect.20190204211245_1549314970339_0.4867086191032757"},"_hasShrinkwrap":false},"1.3.0-nortissej.progressLog-codeInspect.20190204214545":{"name":"@atomist/sdm","version":"1.3.0-nortissej.progressLog-codeInspect.20190204214545","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a454d75ecf21674d40dcaad27f284c6806cf5c9c","_id":"@atomist/sdm@1.3.0-nortissej.progressLog-codeInspect.20190204214545","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-EH4HaP29LNZpJEbrHLQcBSyimIXa/ekl2bX5i6JY4jV4kiaDQ4gUO9llqChVa4SesByKh5rL6DlkSlwEPBNMlA==","shasum":"a0a14abf63a439acd9dbf4111e32f97510a013f7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.progressLog-codeInspect.20190204214545.tgz","fileCount":731,"unpackedSize":5547432,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWLM2CRA9TVsSAnZWagAAEgkQAKOVouN3wEMAINvKIy1B\n/rivYZof8bXTzJiDTlbOCqCB4Gr2mmQmeZ+q29rKdbOKam76pcWET0SWq3jE\nBBhnN5WCcmdgYDH/V3yo6cgBWWsGPSBkgxu9qmnq+Vn9KdgouADLHBAvYY2S\neDIYz/dOICWsBdtO4+mGpa8xQF1FgyqaxkJ9CpcYFrKHmZH8mb8T8fQ/MNWx\nRoEICf9TLTAjK5Uw9Fs5zBpoQZjq0rOdVUCmjtiVsK8/OQQFdzRYdgWhvND0\nyNbMH+FfYdLlKpN7fYAum8qtdomOlLE/hqmmfw+1MlQxqhsWdmoYUVIO3ctn\nWLBN6F/IjWIWGqvgNyYcIWxVjKFrTMjChwLwe4V3wTM/UlEttAM0R2h5cqvY\nzHhPoD5ExG2Zhv3zJCkMXFbAqj4VqkMMJIsvB2AfU1FWtHZS/fstgJjm+7/f\nGfXo9kzx5YeMUJdXAcu/4X5YtIdhYiXCyQdaoibLGSshxcv3+XJqIEKz0xEj\n80tGct+KWUhEup8//X+mFKxd2O8tlMfGDpGLheCji3IVwiGqWluViy/Y2zD3\namBOejZPYxUNPRRNhYF+4B9U9kVPfykWeMJCvNAwbX3uGyNlFjFu+qMWJkVJ\nLYndmsHKSrJwFjwZrbMiYsSVFs4u4HCKgQi3titvUh51peQT+Bkvk+a5iFxl\nc06S\r\n=7Noz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICDObUBGljQQfJZ6j9DMNoluR/ycRpjbnzIn3YNARaLWAiBhrw/mAQ45GPMkMIAdtGbdTBWtaXLrQHhYmZAEb0QE0A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.progressLog-codeInspect.20190204214545_1549316917818_0.9630494142575516"},"_hasShrinkwrap":false},"1.3.0-nortissej.progressLog-codeInspect.20190204221749":{"name":"@atomist/sdm","version":"1.3.0-nortissej.progressLog-codeInspect.20190204221749","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"add61770ca67c921db077fb32daa99937f8a671c","_id":"@atomist/sdm@1.3.0-nortissej.progressLog-codeInspect.20190204221749","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Nd3XaoPeJX8DK20d+nBn8xng4KjWrZDaYo9IOjKAZ5Dw7aZtPy7jVg0BnF6dqDb49vUstsVtaB8/hFJKXy5VLA==","shasum":"40eea4f5a95d976373fcb25e52204c31250a3fa2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.progressLog-codeInspect.20190204221749.tgz","fileCount":731,"unpackedSize":5547435,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWLq+CRA9TVsSAnZWagAA34QP/02mywcIwj1+VbYcwugp\nmFjIKYJL6JOwFJtIqMKOLExkg3b8Q9K37ThploNAlafN5jNn7aubnmP/J+9C\nqk8RAHFr8F5P8wxeuFdalKIVJwQqfALwuXYca5rc9Dr9I5DSQXdlkgTthudw\nPZzMIO4PVHev9AFlR6llLK20mzp+I89LP9SxWhF/wtewFua6zVVJWwQdxCrN\nFX9ZkwO6xiLVjCwRlWyGYIy4ysGvzlRa0jxLk3ksMCBzqyP5GAnSk4ihlwtk\nUoMy+c1vNlSsEpFFl32nts5+/pKnAFh4q8nBj7BsYUAl2ych1PRAymzAPX7/\nLLDNJQm/XjlJHmbhQCETjaug8jjcU6JVpdNxamxkDr5E0NCYds5TVEYI265Q\nC75hlenZLE3cX7vazR1OkOT7p02hOVn+LFJAkqCt4/VWwCWyYiKX7hgwDvGd\ncW6NtDap8IwJvmoXoU1c2hr7nA6BfFNQH56fQY1woO5fmdL+d5qNQWVTcC1V\n5kSqzIv2L5JjHTuUOKqp8n2vzQy8ybuqNSkaUchBTTMjWFUxD98RdfdehT5J\nxCCB6wJUYoY8Jp7J7MXNQqgBAJ1en4LQ8q+KtUX62Gv1ifQ0oTyKSbbQlN9s\nfnKhrf1PQIUwkMGReQgOnHe+6CxY6mKPK604Ejlj/7v7LhCOhf588erRPePN\noxsS\r\n=eaYo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEbERlWbD7cjcFFDjw9LtrPAcv+SpRhUnQIVgUulU5u9AiAl54K3Jux1kC2o7cbg6Av3Vg6RwG0jlkehGi9vl4m4eg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.progressLog-codeInspect.20190204221749_1549318844846_0.4475381229899258"},"_hasShrinkwrap":false},"1.3.0-nortissej.onTransformResults.20190204223823":{"name":"@atomist/sdm","version":"1.3.0-nortissej.onTransformResults.20190204223823","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d20ddd1e9801ff6a2a4ab53655a0fe595a905cc0","_id":"@atomist/sdm@1.3.0-nortissej.onTransformResults.20190204223823","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S1bmZLAISkE0QN93X7saWHDf3X0bhjhfj0SEZqRgmTt0dEfwsDwKWoG4kz46AQPGJsUwfv4TnkzUDGtLudzD2Q==","shasum":"1fc31eebcb8e779c7944b6eec968f71a705237a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.onTransformResults.20190204223823.tgz","fileCount":734,"unpackedSize":5551247,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWL+TCRA9TVsSAnZWagAANBYP+QHSWHiT0NpLsSlsYf0S\nOlq9qtV85JFXyhNZFXBhXt27r1nQr6pkqT++tiwhYi3jcOE3fGe+2f57iHxO\nsUr5BhFMPRNPpyRlZpxJ6e/oUrYHY3NLeLT4UFgU1peW2Z21KYXW/Irnn9ja\nU7iW38vxJuU+Nbp70W8dhSqlTl1BzivytvenepDJ+2NMTDVCz1cDrOF8ioKR\nDUcOV3s2Ai0V8E8LVlMqUPlb27A0obciSPZ9RuozY9f1FhZZtgap3o2wYSZX\naTte2jlMCHQ/UNdPTjKyvEE+y88FxK6N7vQRID6eGH0QvtA/MKkLs7JsLdTi\n9P+B558oDyWDHCuOuhjGsOSOHV2h86ihbemlDsVHB79t2K6+b7Cj88sqiq33\nCjQwjwN+07UNQckT3aS1K2EojJB6pECu/RDwlL8LuBYmdGrXENN8NyZCcgqE\nWNArTKPB9EeKaDiH3eNbR5/2eB1r6rJxs8RIfm4ocyZmQL+kWRS7l8Dz/Ene\nTR1S0uCZJZfGERNMI1S5ng0V04Q7fPBJqlaiUeF/0+uk7fVI2OrZPdPx67+K\ngrZiYiaLFBwYHNt/LEUh1llcv5VRBXhqRW+nlCPtkdSiMFArgBeizRv+VpVX\nxu4fZaq7PVKajTS/DYD3mdBnItdphyM/n+lnjOkiYotTT8pl/k6WrXA6dpQD\n4j76\r\n=lE99\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICfAu7QDOgze8pO5AIh2Ugo0aYVuSlRbBB4yzHKMBPKpAiBeNsVO0lKjzZ+hyC3YjHGW7hJpL7TAKveAl3O0cg2ApQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.onTransformResults.20190204223823_1549320082923_0.06666597977485322"},"_hasShrinkwrap":false},"1.3.0-nortissej.onTransformResults.20190204225549":{"name":"@atomist/sdm","version":"1.3.0-nortissej.onTransformResults.20190204225549","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2672fd59ea513bc7ba023b167d8a3534a403380","_id":"@atomist/sdm@1.3.0-nortissej.onTransformResults.20190204225549","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-c69/QIZneUqIjtbHWjTQavxbl/k2woMR7kyexzhT5+ZJJg63kVhmlqBVy3NumBXRNEb4msAlNAGvNlzTUNDSeg==","shasum":"c237022fa1e78516b0b4efe0ad96b234949ab82d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.onTransformResults.20190204225549.tgz","fileCount":734,"unpackedSize":5551766,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWMPnCRA9TVsSAnZWagAAX1QP/iM4D55Abz8Snsx2B2Cb\nfzbqYMrXKRIF4U83HyoqM4K5wfPFCzslyfInh11RRVjydN8kLTtMjVRKBofD\nI7h4XrEgvnVkeO4StzmzB6lB1+ANGYeGt7xhhdDBW8wOGxzHGEs2GDOHzCOP\n/CCn1poHvTe/WyMUpqyB4BXf03x37ALXYD9J1IB7ZLn0ixa5e3yl+FXSV6/t\nd474BbPzuYUp3Xz6Aw5W3fMORReMoTVq0LZTxWFgKiEUjdSvV+GSioBz2wDX\nt+u0TFllwfUl/4YlzXkiSm6hBYb7qBC99CvCuqDCGg5iNAYoL+GXbN/7QKOb\nExTQwRS3f4SGUxIhXjEvZDZRgGAP6bxI0UAQiGTa8DyzEMAsK6QaGeLG5nvN\nOZLx0JRGkZZUg/I23K6YWWhWyORRPRY6ANueBuoZfGw3fHPZSN2DFQTE6ogE\nfZQ0BbEnnDzWoX4bd/dOV1D0nDSD3OXc8IPXNaEjGrzj6YhjTMVPDLdqEtHt\n5Ft9p53xqF82LbJXOwvNA/krMfrj3yOcQ4JfgeNv7TCM5FNjSHyHOS7tbJFC\nVJTsN9XmFCaTf2Zh2D9Mrzt20MnzfQLxUZqUJll7u1Sziq3zJP7an7xK62dj\nqX5aJ7uDmK7V2A8ZnLYt+CM6JsAFyGkS5qzONXRbIqtC9YWlFyv6e9H6F0hD\nutw2\r\n=FDkH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDz2wsMfA2W65uVhBPHEy+NxBtoHmBDh+j1zCH8RGa9SQIhALO4LYoUSYnlz9fbDXXoLjT4OpC4cZtknm15oJfzYcLh"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.onTransformResults.20190204225549_1549321190950_0.1839979373250753"},"_hasShrinkwrap":false},"1.3.0-master.20190205121804":{"name":"@atomist/sdm","version":"1.3.0-master.20190205121804","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6820c7f1c568d444904bc651c2e4bba386c689c4","_id":"@atomist/sdm@1.3.0-master.20190205121804","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-l03St2AeTA7ceB6lwvbX/0QV9pYZHm1QUtjzlwpvVgHtJ9AtaJizayjazGYVOQBkKEj5yvJJgT5X/GTCGe+YCA==","shasum":"ad1df57b2724f4d1ec5c7a05eff3f46750e4e833","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190205121804.tgz","fileCount":731,"unpackedSize":5544379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWX+0CRA9TVsSAnZWagAANQEP/1F504qmDnJ8onx3AUm+\nQFVGxXJTxZPwR1JJN/90pYwC0qhN0+aZyS5W4U3MEQ/LXXpyc90ZeSr/GyhL\nZnf+PtdD+ThyadhjCf7HEEWe+Xzcm9t8hNEejrtafDRS0wv/WApQhpAQewuW\nVuKyU/3a4TMdrk/qKv1DzR6nbuvSKxgdSdSCti1KjPppR7mKmPnB+hEoKNa9\n4kggIdfEzW/OdEU5PjW/50hBUzccoJMVGHpGeGO7XYnGHTOJi2q4PUHiKFMr\nNuz2NO9m5SSPqfG4CYJaO5mL2KEeqZeSmfu8fDOLixO2QZkRGuBL6KSVXwrK\nJAoAQFw1OwK2OR4ZYkmP2cQL9PgiNxF1cD5agGlL19Wr5nIqTLhA9Xg+VMH1\n3PCmLrbJcrkwPSSf9HRGSq2Qq1bdttYfiwi+1xkWpua0pnbhX0Bic+pnBeYR\nOMk226+szd1x7qxpoxS7zDCJvCYgOYwXUSZn8SIlgggRf7XM8J72qkXn3GSv\nA8e/gkGO7EnEReVfvG/yf+fW0o9h+4QnH+InkVra6NO2v86OGR8sixbJn18y\nRBfBh4yZ+1ChvAfeiKV/aMoSZwoXg5A0Zb1Jae06d4ebgU7YXu8WtdRKSzYb\nfUPrxzFFARhCe0El/Irp27jfauBGGwsPl8/Fdr7d4arZ9eoK21XibLakSyLQ\n04Vn\r\n=FpQO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUj8szMRzUudSZhfuUUyG1cyBoUNE+aHctjS/B9aMV7gIgWMV9L7KVoICPjzcT8i+QhGUy1akjUyxe5VhYIWkbS1w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190205121804_1549369267331_0.2212760603137589"},"_hasShrinkwrap":false},"1.3.0-master.20190205230426":{"name":"@atomist/sdm","version":"1.3.0-master.20190205230426","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"793febf3d87800affad390d869f5380a861f95f8","_id":"@atomist/sdm@1.3.0-master.20190205230426","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-SIpSuU/zyp2lwW0e+pQdR0z5izfrx+FnZ6CQh3hdYuY1fCArhkTzHs8xLbMnhI7HNox0Ky0ZOvpM26e1Cd91ig==","shasum":"f159f8f6b968b304ac40d118a279be5d082e2a01","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190205230426.tgz","fileCount":731,"unpackedSize":5544493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWhd4CRA9TVsSAnZWagAAuPkQAI366lMoGcKhk8xA5bUR\nazocxy4nD97GBW4g7c5SYT4z6jwzB5IV8YPQThAAUXhKWwDrVjJr5pirwMQX\nItZqypy2LHUsVmx/DLt+wgut0sQN9+haaObAn4SBIFoAQJg9MI/elt/fir17\nBpBoqSLycRmmeoqg5VgUc9g8f9T3bSGpT05q9bFGDezWCXlPtPeSMws6MxeN\nQhKvwEsX3EE0YJSwrRb3RRVI06ER77DqivugqPVn8Jx50v6XFeJvDEJxiInI\nAVtA2tmUf8egbUeprwet8QOod2zBJYR89VQnrB4VGsdpWvZUhoOvSHpTTnEi\nh0WLO89hu2MItzG/MYR3D9vXFPxObVtoYaFksiqRG0MTUpi56kiaymwfJgED\ng4jfP4YU7j6MBF9Jcox1n82KZXL78P9CqTgipIw1y8SlD+XuYJ3M18ovCK65\n/hnO5zpkNSm+FOJNS+g7R4YxmnMcY9AsYdyuluzF5UCH5RwSDItI1enunQ4Y\nRJGdKhLR9Hh34YwwxYk9Rg7t8s9e6zdULRIashlPi2kIajhA5DHD4m5EDvXE\n3BgNPBsZLW+GsdyM5iYxDlPoPW2X0gU304xaYU5n7qhpnxgyIkpBM8UIPrAr\nMRJ50VjNQrd3Yf/X1hVb5n3UAHiv80mgjuegDQvo8kad59+pR6dFquryMFTn\nSvi0\r\n=dnkq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCIGmbz9aqmlVOmC4pIbX/uPCLMp4OKf+4CmEEpyHNdQgIgZYV2Gb6tN4elQr+RKhlb9jnClUvULCegKXSoxt/hj7E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190205230426_1549408119132_0.7695713268385502"},"_hasShrinkwrap":false},"1.3.0-master.20190206111710":{"name":"@atomist/sdm","version":"1.3.0-master.20190206111710","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"af78d91e2065e6ec7165519f81b6f87fb10e926b","_id":"@atomist/sdm@1.3.0-master.20190206111710","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-R1a0n4CqvwsWCS6hiwuz0oflDYnJeGnZhhM4pvTr/j8aFmarydr34zsGn1/CHGQtvIg0ZzC+0XOBs0c4UPt2Wg==","shasum":"1d9965e29cf53a547d47f7a9b96b5a9b6ca2fc4f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190206111710.tgz","fileCount":731,"unpackedSize":5544498,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWsMyCRA9TVsSAnZWagAAfM4P/iF7n0xrKh3zU2s9nV01\n3yJ9/tIArw4N5/NkztRSgxh+Kklw9iL7ZWvJWEQbVrDcLu2lxIfm44EIEOkv\nnv7lzoY2SFjsgNRCu2qowPo9PAC1onGsXugak6WcGqGe8oGSvF0Y5VnwfONZ\neyFFfARu2MCjslmde3MVBhxpx44W2LN8fSI1pyovBFdU4dJANNRkyFcjLAoP\nhiuRo93ziTCP2rM87qUqMbMUggqTRPr6wdnpPvq5WkDTgHPBY0EiRVHQwsKo\nH1OTi00rOoxxNdFOmF4rVLlocv1f1twD/SkhVSvjOoMtKmH1K6Jm7mKn4AS7\nOZGqk7CcPhK8YTqozaRkMCUcAg10puJcJT2h5PPnkYyBas7O5OhSP5zFqZ5y\nM/7D9kYaZETDcLr9oeFKRhDAzcBtUlWR99YcsgNE6v0BVsNeYIOphxjf9+zY\nVRhpV2nuhPYKINOtXp/by8xxIvBGQmngVHcj1D4MWeGf51tIANj/nfboih7x\n815L90hxEBJemLasv15LQHLPn0prAEEZ0gkQlBM7/KBlZokIY4ipAeaO+wBd\n7jPRl1Zi3AUIoTW4SGazlgQHc7lvp+7JtKmdUlM+lKRQsLreeo9YGoZVxLK/\nD9Ze5uVC1G+m/QxxrcyzSPW5AFOFegzJ+lwVsCb2of+y1BYYQPlVA3qR9fkt\nNKTd\r\n=T6oa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE+0IwbvFW7qSRzlNEHyIUdlJj9ymyDIw4LNpsyZ/M4/AiAnyKb46IgfelSRoiLqf+9dss4Eb2z6B2hqregdrLWGHg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190206111710_1549452081503_0.015924445920472596"},"_hasShrinkwrap":false},"1.3.0-master.20190206171739":{"name":"@atomist/sdm","version":"1.3.0-master.20190206171739","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"467ef4593cc94000de31be819bb6b614f90a852e","_id":"@atomist/sdm@1.3.0-master.20190206171739","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nQnuavZL6Qx3bgr1+Bi12fcQv3IW/HD8s2JHfufbxbXygxfBvk8jfxO75uMD64wxXscccprPeB1a6+tXwhkTLg==","shasum":"0cf2c2916f71f5fa73bea4966b49fad72874bb4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190206171739.tgz","fileCount":731,"unpackedSize":5545841,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcWxgWCRA9TVsSAnZWagAAE+4P/Am0rEqCp17VnTDnnefj\nvGj8MYJHfuB7mD0d2cb7Tp6B8qW2NSPcNG4oeDX4Bf+ZAuwWWmgpGEEmgQrc\n3owqx3iZw1fXhvdC+S/Y+E7C+4nIqQwA3HeXvXL85Rwyj8EFI4jkV05Efwql\nc3vEyRVQUswT6CAnzZjbiLMDELW4Sfj8xiEdb/q9HVPzvJHyVXLUfemUWUbu\nm2x5ud+b1LP0Dh54Boso2D+1PrOwQp7xIH64DIb2IhIazSWB0QXincOLVfmn\nyyv+mNxEQ1qPOkW9Lp61ETz7hhz8u5uuFRetqls79gqpkutz2I3hUa8odhLg\nZqmI0IQSibQRSJ1l5k87rrWM1rpBYe3fj1tiaANQJs5lFSy7V9cIPe/4Dkou\nwh4UsNfyVLpdo9ee9XXF7m6Npit5cdqEA0jq/jTjbhTdWFqP9ZFlNoIlkyQq\n83/+azMcll1AI443y6u9rIzhmx5x6A3qR8a4k4cePv/KaUyUV9kR0RZ45FQD\nQpLYgNe3SW6UQf7qdkxjOxuOVKqqF9q+6X9TZRZQorbSXWjiUGlSyi/A2HUV\nhhHs6y7aDa+P1pOS0whgdUMd0cPcUUaEXYhI+PAIYOeu1o7cb2cAu4mrQe9B\nAUlmKjP9MYYGt1zrSJZ8WhrZdIymfzAv/3dAkD7uIpsZ+viV0XUMyIfR0fAK\nhm6T\r\n=YhIt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDQPMUXZ2bzNS+Ns8WvdAvHgmEoVp2AbQ+RMUGhYKrOtgIgGxgD25fZSK20+aZc6Ot9HRZBVciEpMsJz9Regt/NmE8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190206171739_1549473813347_0.6947319387961031"},"_hasShrinkwrap":false},"1.3.0-master.20190206205235":{"name":"@atomist/sdm","version":"1.3.0-master.20190206205235","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a1b447be8488950bc95558f1816c6ba03cd41eb2","_id":"@atomist/sdm@1.3.0-master.20190206205235","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-w9yUgv5pP9Clwqc8nvraJydXu8be4aUuq4oj4YQLUV9vXWm6n8WETT2i6aZ5DVm3qyrs7tDSUwyhEH6i+IGbeA==","shasum":"04d9ae2994035242cb1bb8870b7ecd8f9b662049","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190206205235.tgz","fileCount":731,"unpackedSize":5546302,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcW0n8CRA9TVsSAnZWagAAUPQQAJRjXXBUmMqVfsyLEB65\nAPud4ch4f/LWZ+sHnI3JcnNiCmRVyFHE1rwpWsTrf6Pmma0OJDK8jMHcPYxw\njrn2Ex7FHBxveNeCp5XjiJO2W7N1WzF8WVjdXJyq1QQoKRrI9YZ5uWgyKKrw\nm09MefJio5F8S3B0jybrNkN/70Wtyn3B45CDe0fpIOemLOtLlDiVCCtEq5/o\nEfDqUd9/4S1o6MloMBy/iFBNG72kyeCi1HhddTXeRwgxDdM/2439WcZlLPj5\n71x4Som6velmkjLGhy5WtJEr+87wzCKEXq2HcsKdD5CVSg+pTE1NgdxF8ny+\nfHoL9HdPsTqiMKhSZJU4iBktk0Q1n76H9O5vK6uV0UMwInocUpe+4tV7btUU\nKN5O8VLIsIqWu1actW2p/7VuQO94ZI+zFfXlIntzRHgXFm6feqHqNEWtGWsL\nQPD/Ao8XtBH/tBtu8KqX8C309ATmX0xuOp/CcQ1+WRYNAdXvLB2YCsrWoS6O\n+rtZJc3QOmS4BWib0kIqJAWtrUkzjU1JeePHsfUQ+NK9BA0TRy08XS+iRwT/\nuNpTJ8kcXfMHQVS7d/8P3bRuHcLuDqNZvgzGAXnEk6HWGiHCjS8WxLAYdksJ\n683N4vJiHkM7Vv/9BaJ+qewugXaY9u8eE3gjUWTQGeWiFOlmk6NqQ/W4ge7x\n/NfR\r\n=8a00\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID0dP4rkZF+c+87DWcNszlwu5tmognVd53eEttojWr+vAiAcBoMgq/f5CQ9y7CSHebKpzhNDvxhYVbEJndyy4YKjmQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190206205235_1549486587429_0.21099382326780658"},"_hasShrinkwrap":false},"1.3.0-master.20190207212851":{"name":"@atomist/sdm","version":"1.3.0-master.20190207212851","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7c57918797bd4aadff6ac70655b2c40390aba4c1","_id":"@atomist/sdm@1.3.0-master.20190207212851","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-1ykLRbfLbDK/BdVQUT0qXpRQxMZva1UIg493wl+fiCrNumgHDSI5fzuxd0agQ+YT7CShbmPf9SxSkoiMGD3MjQ==","shasum":"cc60ff98e7ea2c74972783856d95e329225fe2de","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190207212851.tgz","fileCount":731,"unpackedSize":5549396,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcXKPbCRA9TVsSAnZWagAA5rEP/iVW/SdvHazHduGULqjY\nSblHBDdTnlj/7erJALh1VX9Hehe8kMCDOCZ+ZSI/V5z0HBm77vmRZ2cUur4z\nii40muIN7VF1Lqovw/8mZN7uZu52AYgH5tPXWjBKup5+gjbk61Q9q4xWLBSN\nIT8VxIyejauSlFWOmYrShrcO4Ic0nq+dbeMFzlIvl9AET28qbWzVx1MaeKK/\njuuxt6Mmo51QidmGBkTWhLdM0srFlIlP651oNC1t/K08xE94Xvno8qc/uVD0\ndX13iRBhgDdeN0VO8jo/OYH2KvCPfxObnvWBEbpXdhQdOtWykGDrRk02DlT5\nCjlxJ2y0fyIsG82ernklv2lgjBAy4y4ZJ8a5IHsNXIWm0T3v7Y4zTnWwTUBQ\nwqlKbIkpyheBP8OCtRVyveQUXghHmN9AWgk0anOg5PZViCQ9/DRFP3OnMuJ+\ne1H1Lp8fn5lzXHFTDnjAz/utXNNGMjgh6ZFDGCqZm1dnXw+UnQYWFmaBwFbQ\nXvnmEeH38kDIfBL1KM3fFdNldH9lFaM433cn6sSjRARg0cZck+bB4c6hGO/o\nnyIJ8/X6RXamSf166ink02Ae2x4IuRspl2JjE9X5snHNujdqWU5JM7vTnYqd\n/e+cLi4kPNH2DsS0OgWi2Hgilkbm7YW3aWK/YvsAGgQFVC9WV/kiX2D7Wf8e\n9xsZ\r\n=7bAm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHOp0C7vIRuDu9NprYTxdb6noo0s82E2CLkJNzJGa0ZcAiEAoEhTVKyWIYIeBhSPmaa3fAQFpngsRDZrBFw8a/zRK9I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190207212851_1549575130981_0.20403797118390155"},"_hasShrinkwrap":false},"1.3.0-master.20190213001109":{"name":"@atomist/sdm","version":"1.3.0-master.20190213001109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"deb9d8aea69db1ad2444cc9c30b5c2b49bd4faa1","_id":"@atomist/sdm@1.3.0-master.20190213001109","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-tJcfXRBFOfKzOM5pJefVkmckm9tXdu+9H8SwaEn36OQBTXkJYrbVT1ly630KJAb8CjEVEG2SzbmTkbuH9oDCrA==","shasum":"ed288df370755de6f6ef2e0587ac0947072ebcc1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190213001109.tgz","fileCount":734,"unpackedSize":5556935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcY2GCCRA9TVsSAnZWagAAWh0P/3DD/S7jCFQ0eZXoLMH8\n6wIAP7K+KGnHq9+7F/En0JYzsl30MBk4ZxfyShKZds0WWeHfPNyPQNHPsafo\nEstOly6gXg1OIXuAgDHCsYLh/KsVgAe9Id3LuOkPT3rsjYDoFsyzqL3jjflY\nqd9ZZt7VlX/lEhhOcqm3Yt1sly998NM5a4hC++7rsi61FVcuADFK5hPt/sBj\nQrxo0+jSzVQLb7ck3g4x309BrituRuOa9LhqyH3qGn650a67Sa9OC4hKK2Q2\nNizhQvkMosDuIt9243PMnfpClY9JrLVqeJucOa6UuD8NnCVz05GIGuVoDUo0\nWIeyZRWLKA9k1vlb1H0/7dQ8lblmt91RwqUhRrfhW9GmBYkKCyMfBkPHaEVg\n8SZjRXZFqBW1UmmUNWygXEAIIu6Rey/pmGkMGQsr6VGIuyqXTAjQul3YBMrA\n05ULGUN53tDfgwB6mr3nq2ZQodJRYJ6JDLnGwkfJavnzaR92aNBn3S0o/EVs\ngi6zVVtmEf3rUr+05zSc0FgLxWVtgg/AGomk9cgm4li32jdgE2BAGXRP1l9w\nA4u63PgGE34mQXwBSWoAhZ9MvS9d3Hni5YVoPeR3jLy2aDrJsf0mJNO55ogU\nQwcQiQ+dLU1MdcP01dU+qGHudPJWYf/Rk4ET/tEkJ1vIQBkFZShdbuaDwMcm\nu33o\r\n=pQKx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCiO9nbcOhkYoLjP8P6PTtCambRieE8l2wlBDLGr5pbBQIgYGGRxd1WJzsDn4WE76sBJUjFT4wuQ2j7Wpw6sgUmbF4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190213001109_1550016897881_0.8162634811264404"},"_hasShrinkwrap":false},"1.3.0-master.20190213150905":{"name":"@atomist/sdm","version":"1.3.0-master.20190213150905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2f94c8a6c3b64a8e255404e436bdb48fced1a31e","_id":"@atomist/sdm@1.3.0-master.20190213150905","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZsZgf16Y1HtlJQwb2OMEir0qS+ZGFnO3ymTPGlLF9EQGyLWrTozllz0nR18GbxrwreEf4NGd4b5d7lrJ9GTvOg==","shasum":"5eafd28ef141dfc558e57a9de55809cbb167b07b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190213150905.tgz","fileCount":734,"unpackedSize":5556947,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZDQKCRA9TVsSAnZWagAAo88P/j08RntG0YlYjjr4iWsh\n13PC1gZ+Nwjri7eHQYoNtbFwOsfw8uBqH6qAmELLPL05jGRhrYo7tKZKrDJC\nq3Zc30AyJIzxZB08GVrkIjL49O4XKbBZXxBU8lQ/8/+HQB2U7dUjvkjyimWe\n5RS03BZWZd+ClMKHwfLpP4/iLckoRYAvU5nv209kNWaiRAnvdidEPuQU0MzX\nORBMGGBkQg5PqpJHWMiKIxCyjn08w6+ofLBDfvMwHudYZxGhGsul+wVHZkrS\nWtluFL1o7ZPZmrE8d8ObhYWpRsaOHltYLTD0HGqcaVrf7XE1cEd2LHsUdxmx\nw83LQH+NKZETQyKWUP3dEt0lbFMfcNSq9BLgkRLEKTE0IAv1SeYsLVd5M0Ed\nw+9ZxbGnGTaNc4QrvRGnmtZH0qwhzZVW4PGMk/2vdBbBjcLu5zg5TCTBPr8o\nJM3gsqD7HfssbeMpBsrk8ThY0jpZeKxC20Z39EU7DWFPRpV3aniMhM7EdWwA\nl9IIrlLrYpgYAwUq6FZP7Z9D9P1NFxfuP6Ezqqfxor9GsbLTU+Vn7myCLnkL\ntWrLtoSUGthJW598IwcYcjwucb1PxIBpoY7KPaNOddWsn6T/fr08rIVC4MSX\nCyZJbmf6To+1uw+Jdwvks7KqVZlHdP6sRn2Um6JBZNc4Cb/OwdYg5GQWjO2m\nJN4d\r\n=ZPpr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBnDyJXat9RY44dWNLVDyV/bhycNOelXSjOcdq0lJgTwIhAN1w2/lNqw96Ts9HrjdD+3MRe2QzLvMglCGdUd0FTogb"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190213150905_1550070792946_0.5390395417513658"},"_hasShrinkwrap":false},"1.3.0-master.20190213165837":{"name":"@atomist/sdm","version":"1.3.0-master.20190213165837","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d1dd1d69be97a638a175257437ca6a92038f31a3","_id":"@atomist/sdm@1.3.0-master.20190213165837","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-rFMEFkQExsDbwmLfysAJS7arNAYY3ZMuk37E57TpgLepT6g6yaxyaKim7Zsk1h6BOZnqCaFUZzHw3OjuHU8TAw==","shasum":"888254d66c025cc9379e4ab5d77e497a7cbf2e94","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190213165837.tgz","fileCount":734,"unpackedSize":5557025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZE29CRA9TVsSAnZWagAAG4IP/0VF/GdAuAWQS/dUlGWM\n5tEthgjYMF0jCGau+wQivvMcPJoDmy2Zud5nfPJWo3L5TmYqYO/L2LWE3jwY\n2JYMmi0NcP3nt1ioBoOBROVQgM+rONyDzppnwJwVhfjEnRC5SprUWAtnG81i\nsnM+N9T/VytdFNIr+cyCJS6UovQobsV0Vkhhegx1HSHYlrLGy+P4OWlDGj7H\nkleQ7rhN5iROwGGANe/LH1WzUkbM6h7wBIuQ6u8rwvKZqoE/KJe8CjUyYRMx\ny6/lC+v4jkInx3Pf3US75uPu0f3gaH0U2Cx7NEq/4f30PjqAUPg/X6KE3juY\ndcXO+duETW2Y1U7SWfXhY4iURlg5KcuXxEz8iW4jWykr3e+qZpLVGbgzTCQc\nvva0fAvl+v6bQQXYRlAj//R3XJPcbCyglZmyhH3jv4mpaXA08FQ6+P/Mz0Zl\nuKJgUy76ahsjYNF1SE8qlwDqMVTLBimbEdXcQdbwFDuq4/Ro+9mXYKJ1ssN5\ncX6bxDoe2JMxhRiwjaqztZ15XRRO3G7d4aBFf5lz/qMugK7wT1Re6e/nJ7H0\nQD+TNI7HDXOkmOWhag0iOdpg//8Ar+1u2KI3lSQx/QNYlq8CPliXWIgHDmPN\nylpMXhymg4hv2yFMkos56HAyi37m2rgMSt13AgemaGh0LlZFmkl1c2C9kMPz\nBwhe\r\n=0zrz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEy8ttB2YkPuCyEZl1t6DABUsxagY00avRiM3nKE2g3wIhAJDiTKnt71i2Am1WWR1gCh3nN/MV/rA9Pv2s3myEX0lq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190213165837_1550077372885_0.858171998263253"},"_hasShrinkwrap":false},"1.3.0-master.20190213165808":{"name":"@atomist/sdm","version":"1.3.0-master.20190213165808","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d1dd1d69be97a638a175257437ca6a92038f31a3","_id":"@atomist/sdm@1.3.0-master.20190213165808","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-++jTL4nTwGMRxdsUKI09x0FBChjp0Bt6bL3Ggl80Cm8jYBwvPrYwJR7NSIiR0AQNgu787VUkLDZc5PfIdGhNaA==","shasum":"7a272d83609b54afc13ebb08d5b890202d612cc9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190213165808.tgz","fileCount":734,"unpackedSize":5557025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZE3mCRA9TVsSAnZWagAASf8P/31QWZpZwTAWqwIxcHDq\njHA8Gx6vTXZC9mL9nNxzKSMvzuzB7ldTZV75UKsh/W2agCXoOIjoVkV8DZw6\nHUeiOADpsu400YOeYlZTf9+Pp0PeA2cQkpZ7RUp5DvGbO8agciq5FeCknJzT\nGMhX77Sg1NvoZmszoEerKjNu+rgOzvhimuheWpCcepdEu2q0waqQx9BKpKU3\ni/ZI5Wt9TAGhNBIDTq6MzkABKvfr+vI0I6UEl7r9C+fJhsnN97trcCg5pElk\nc3YEFkhvF9aws1KmEh669EyZnM/cPsiO7HhCy61bV+x7aeSqvsHe99iLKfvS\n/O4EXMgWH27ESkYViK4DBmN0SDOxKWHRFX9CnzTdV60Ek6mqXEBGQwNCnLhw\nyS8Y0i3IuQTWoLPo/mhFJ12qGfzhNXvNnYWxBtOiYdFMYnNpRklC706xZCi8\n5OtpYJAQM9hnTs1V8rQB7Qpb3Lq2jipn+Q89k8QQG6fmSZ7/pEopC0/J0THE\nljsGG0MBKHLfEkPcBcZBKG/HOu4+lPLdqbg96VplzqMkzYUrVTCUl0BMUCXd\nnsTOedwqk/vHuTjT7+brlVMlQkl0OO15/TrZm/3zHnXIecWDtxtK1drhyPl6\nsVaTKVf472A4BJTtSeP7gtTjWVLCBxwR5bn2nKoIkBe4auczN1ZCFJn4EDBs\nGv5n\r\n=V9a2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDrrOphIQMnutCZrZzLoGbk4R9lWiABkZBjNwka5xXf0AIgJLS8JDsGsLmdvuWOWYh0J75PAxD6BrIgygmClqFb5BE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190213165808_1550077414124_0.40600065528537166"},"_hasShrinkwrap":false},"1.3.0-master.20190213170255":{"name":"@atomist/sdm","version":"1.3.0-master.20190213170255","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d1dd1d69be97a638a175257437ca6a92038f31a3","_id":"@atomist/sdm@1.3.0-master.20190213170255","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qrCh0ZSH96ewOE3mtZTjOB86QZmAKqclWHHtxHLPoqvkviAv58k53ZKCs9PwtevMtu6+03o+K8VZCB+Kx8nigA==","shasum":"7edde7090c9ff6fcf8c027780dcbad8ff40fd8ad","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190213170255.tgz","fileCount":734,"unpackedSize":5557025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZE68CRA9TVsSAnZWagAAgtAP/R5Ea+YlcCEoCYVPSCfT\nRlkYCUk4Uc8AjuxL3OYUnLpemoR5tC8DVGKrIzkE/yFpprrmDPu3nGQBhryN\n9tx9dWDjrNFgilwacA970QIbfhnDCIncEE9WPovQVbkeYHDt1Kdc9SLljf7b\npZbVks73Qy6gdhHpvUzwgg4zhtBCuDsz6g3xmbDz24NsdPKPY60qhtBuUE/D\nhoHiWjR+xXpZQo8B8qZb8uk9t5e3v3pIfbKRsvQxHh8y5F5ocFYUk8an+vqw\nKRPoPtqk0dtEqW0gUYuy8FNoOg5OvZ2y1YTbKdqilVw7PlvbWNXvgVqYIG07\nY4Ko8+v7np4IsPRojAbmlCWWsZTIKddBTsMwhjS4meCGJCXEtW/4W2vQj+kY\nFq8oRiggl9rO26wzr1/0YLxT4c1bfZzei6iIe3i6RVrqDTAb++Owd2EX+g3P\ncXtdloQvH3MI1X/cJ6UZOXS2dYnNnB2eM0k62enAsXDnc7xvGywWOEi7pByk\nRmbniz8P2EmqhpqYtFn+b8tpn2RJcm+x+u2MHFd1enOW26qkotZ237Kzx6Dn\np2QuVu1XAPBI5sZGhzhV3fiNRN4cUaEUrtM5q+3xYNsz5N5panoU50+I7z/0\nzEKoWgvzF0JM5tANunO11NeuaHCaecwlUP72joux/NjVZwv3B12sAJteLbH8\nyD4R\r\n=Y5v+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDeD5y7ZDZV3ZekuLkgmNG8MA0S3JOUuoNa/b9h8KRjuAiByBYZSuO0UX0VfRkoUK/3ohjLYpmtHa3wFR2dW+kPHxQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190213170255_1550077627274_0.7234455863718967"},"_hasShrinkwrap":false},"1.3.0-master.20190214083859":{"name":"@atomist/sdm","version":"1.3.0-master.20190214083859","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a99b5a5d033a786a5db0409dbb108aa05606493f","_id":"@atomist/sdm@1.3.0-master.20190214083859","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3BeUWHKc3K8y0ecr4DnwtLGhfJYVfatef0WRSqYhzarkW7ZrJASp+gT9dFdeeRL0GBJ40SQ7GP08e5DuUGVG5g==","shasum":"a1f346c6e3f173e112b3b7058614b66d38865061","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190214083859.tgz","fileCount":734,"unpackedSize":5556970,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZSoZCRA9TVsSAnZWagAAotcP/1IoM+CSKjO1ftLzPmA9\nJ3nx+iIQ2o9ggZsJhxsdlxKHFZHkTY5ejwbkiU840nT3vyYpT8vxXiH+1/3s\nZlOt54TKhAPbmDox0inJ3TlZEvA14izxfVBSI4jerrmsz017UxP3fjZQO+6N\nozdYjFRDIGWNI30SJUPxu2YBjrzFF0O9HdEgm1d6E94pXWacUPHj5ryj98+q\n4bRfGF7usLVgoYlFRmbb7uj0wcbiZhTDHqZg3hDyOQw2pB2G5YHHRPVKCmwK\ncJuNKK4z8YNiBf30mhyApeghkrUXaH9o6c22AfBFTL4enZJtNcY5MGOBo7Rm\naCT0IL7qILVDjldXTBSoqrrkLTeTL669isg1fXumwFPptwh4L0wyPB4LUAZa\nYOLNgs/HUjmPLooMcTlh3V3ErE6QDSAdDdjbDdKZDkBoSXG7fPwfLUt7lRKY\nsXatKAfcp5/rZKE5KIgTQNCevmOlBNhFsbLpfeHZVzj1aj07AiKEukfvMtVt\nlhXsySuUQkUDjv2Eic/1UzsNqmam9QSRSCYQxtcUmZBwtygyeNOXkp1MlewT\nQm/i64IrTCZPKDog1fbkmQ0rAyZElUAio5wl+m0FAm5kGxiBgquBKsjmI+H4\nvwln/o9zh9YuIX0rAoZJINFiQGFZ7Sjidz+qEjSBn+w4D3x3bRPp9edRgazp\nnb5u\r\n=wuBk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCt0TsbgestsVo/eWtCDqbGmy2vOzp069QVZ6E8/BpaawIhAKXQkZ8g0BMcT4ue9ODIsbiK/VtsuVQrzCy46ZPLm4XE"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190214083859_1550133784543_0.9924692363395282"},"_hasShrinkwrap":false},"1.3.0-master.20190214094113":{"name":"@atomist/sdm","version":"1.3.0-master.20190214094113","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"75e6d3979faa073dd934396b221a8eaaeb8fac29","_id":"@atomist/sdm@1.3.0-master.20190214094113","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Im858lZaPY22hzUWgA4GgDZA5Mb+hQ2pEM0dVEaeN2r5xZlfAcY+B5vDhHkQd3+7a2Vz0cNS8/rln+iUin/Nxg==","shasum":"aa95b5c62ab51b16c437371914494a0069a5178e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190214094113.tgz","fileCount":734,"unpackedSize":5557311,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZTiHCRA9TVsSAnZWagAAVo8P/2CY9pyvPAVCyzKPEYYc\n/A1meM6+l6qvZTumGW4KC97nOkb0ixld/36/xz7IFg0BClaJ6mUnpVusTPiE\nbxPN1lIGfP5kQHQ21F1ElXT2FjwAaaccs6KG6jbY/Vy1OwmlRTU1Kkjuy0gI\nwDwqFinbxtSagypjoksuC1GVafArao+Bv1s7T/YIyUBesrwkkqzVPYLqVw5J\nGcWTDwHeex2x2bPPIO+P1oHE5AqHua+MyVsaNjNiBmUXRn5FY9ZdrZwUgNxY\n3jBCLnqFSmEzh75YNTTvKoPw2hnTE07LuIgDeR1qUc4svkgxr5FvUUR5c12E\nCHEprcdprRlJPZZ8wFh2/DrcV4rZIwhfmrUkM8EPknjZE2WQUBtDuVqFe8ov\nUleGKKCB4pACHu6PS5t7GFaKBNBoi/gB9jfGQxfOS7GwuyDVnqTlk9PODhSt\nMsX2zKRLtR5CRzMNhQJehRVA/GMqcZzC3YsLX70lf+fyhCCZHYXpd9pDJmQN\nIjVnNEPBvIts3Tnt+FtoaDQ1ApqpZMUsRG6nMylDiMma8hZdZzOtOyJC1o1I\nN9AkuCMjsiweQq0m8g8OcXmZ5ccuySuzhJir0hlO31qoQ8WSmOpf33BDhxVp\n2+8aScApEkySu1mNdAruB3nR7cCAytd+LC+5TDfi+2Cwu2NXSW3l3TV9xsh8\n9pt8\r\n=TR1C\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAmJ5Kha+c09cdmEo14iX+2LzRZNAkcEuCOlqVBEKTJ8AiA7OafHgoJjObocXaADV7z2uRsV+LGZvDamjxJu+7DMCg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190214094113_1550137479080_0.26042482858571625"},"_hasShrinkwrap":false},"1.3.0-master.20190214121328":{"name":"@atomist/sdm","version":"1.3.0-master.20190214121328","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4f2b0c62c5912063614ea152eca8544ce3733ca0","_id":"@atomist/sdm@1.3.0-master.20190214121328","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mhkulTxNAZR8TQMreETaP+3hGpVJ5iOpMiy6DoO4f4k95QVoeqslTYC3RD9ujJ+n/431/8QAjUuCnEyFhcpUbg==","shasum":"f7a3076918903f0e6165f7e3a5353b7bb752f5df","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190214121328.tgz","fileCount":734,"unpackedSize":5558960,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZVwuCRA9TVsSAnZWagAAOxAP/035igqk11Ck1n/6ThyM\nJRjsvP+kkrlFfFQrA/iIwL7D+90udcLp2yJhIrlg+XjcvpkVTCaXtAtNJs62\n+wBcQ2iCM3dF7XF13p+g2BMFJwbB7cDFAJFduLg84NEW412DOspyJktqCrbQ\nIPu+F6d/uClnhoEqNx43wgz2OePO2NQNQZ0AeYHY7EPMu5dtJKzoTbULx/E3\nKgq+YJAPoBnXHRR7DTrkJdJ87vrlsykMMNzQFTeWRd5sM8H+L7d3HucRwCXb\n8PLbtAGNL9IcrvdGuffNIMQuE+vsW0WiXovSocCRE571xVAu81RijI5Zwua+\nJF8TTmdS5sAHFshrhMPRAlbXH6HSBVKf5jyiGPw/bnddOtEK52f8eTUK3aaZ\n30CiVxCbSQZlZpl1uRj1ZVyetEW4+eyyDZ3MI+IRt7e1+Zn/3GeQhthIbi5R\n/QrtsPRYPSuMWQUjKSqdlBlb4h1dMo6ykfSv6XVImbb1Y4/O/enYAdr+/zuT\npWIusf6a0kG/djKEclvLK2uWZ838EOAHI2cNtGWKhDwl4WhIUODuLhzjDUV+\n5EaOqt9mA8gEYe12JnB86s0KN+DYjOD7C+EwnWEXa6IC0NjJGdhQrfHompgw\nbLrM4NTuIp+M8Rd8+QkRUeEkLY80Fj7DBD5rtVx024XYSGgB/UKIm2rSMdrC\n7+Gw\r\n=7Fc9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCbys132ovYzvcSbGInkvzUuopg5t3pAPq267y4aylDagIhAJENrgDwsyZiiqwDEDLkBmGfNSIBnPD4VOi89yiE6R96"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190214121328_1550146605912_0.14048325659601746"},"_hasShrinkwrap":false},"1.3.0-master.20190214192224":{"name":"@atomist/sdm","version":"1.3.0-master.20190214192224","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f7474f9397daa6f5c2d002a407d790310a2e9516","_id":"@atomist/sdm@1.3.0-master.20190214192224","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-b7Dng09n5mUc2BEko14W4I9HMgpGs3lJEo5e7YUA9JGs8Hor9AserQkR6weEQ8lzm9fo8yFUTYsCCBD/ijUqtg==","shasum":"6dde45ee80d58f669215e3a5f07b38efc2615e7a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190214192224.tgz","fileCount":734,"unpackedSize":5558920,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZcDbCRA9TVsSAnZWagAAcHwQAIZcVAVkwL44TBV4YiOv\n7tXVbJzMe6xXIoEpk4PJYcMGl0R4Cvh2RUtI9P6yp6aSi+VBOdxbIxhVVLu0\nQmPOvlZuxnxsQ+1k3XKRDZ4u9g+UaV2OtqOe/R/J/QcEstxgObOPDL/yv80S\nAAr1YxJjFhShybtsa5yqS9Peat1abGludVndaE0K+nOdqTATQwGgi8+T6/u2\nn5P12kKZyD8fzooGfyA+sxCO/ZhGcHP6C8MHK/BWgVjUaNfq8631G05zc+VP\nA7EuzlsFPiZqCrYc2soBNHN8tHLAqZjHLMFS802QDLfwliO1ujM3GHiBGmuQ\nQa9p5QzSBpQE14iArzw4PJ1VBDe9egI5VDukEFN3Z0FgJXXyyzQi431cdSmK\nuSoAWmr+vFiBuSKwuA5PDfH4i65/TsxOMenrVi6BW9u+DiDazxGQoNvtADXG\nnmJZZHriRwvOnFF+MTlvdqktshGVbFw26z7eGFuNr5M58/itxNrYLl9CtIQB\nKcQmG//hrJA8x8dlnQHXDeszpSfY5DEDcpNhXrwO9OJGArzdOOc9VafA6cTS\nyD3cozufWRugFisTMdGVk0xEXGWl1PXfelGi4vMnyLc8ISKTnss+m8BEqW/b\nnOKaqzStvNNNFl7GbEAz5pG2gnFVUq1h965M0RgIRmfd8IdcKOJkJGPZXLEG\nDJEB\r\n=3DY5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDYRA9qyzsvpwrYsPKatMxjRh0zbzTjChlNkVt5qtw6VQIhALsMxvSC6te14v2VngrHoiOjxfVUK2c5bSBxWLCwhMxg"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190214192224_1550172378554_0.35553852539993613"},"_hasShrinkwrap":false},"1.3.0-master.20190214193633":{"name":"@atomist/sdm","version":"1.3.0-master.20190214193633","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.3.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.4","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.119","@types/minimatch":"^3.0.3","@types/node":"^10.12.18","@types/sprintf-js":"^1.1.1","@types/stack-trace":"0.0.29","axios":"^0.18.0","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.10","minimatch":"^3.0.4","sha-regex":"^1.0.2","sprintf-js":"^1.1.2","stack-trace":"0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190203162633","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.5","@types/power-assert":"^1.4.29","barrelsby":"1.0.2","espower-typescript":"^9.0.0","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.0","prettier":"^1.15.1","rimraf":"^2.6.2","supervisor":"^0.12.0","ts-node":"^7.0.1","tslint":"^5.12.0","typedoc":"^0.13.0","typescript":"^3.2.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f556265daba5787cac4beb10d00be8c8c89718bb","_id":"@atomist/sdm@1.3.0-master.20190214193633","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Zbf22q+Tm4YNoW5UCiIfXKzvogH3xAPJH5j4XtctczGzjQV/Q10WpNTBkJyeapZYHIdPc4QtHorg27OQWC4DRw==","shasum":"4dd8c9a0c118065e913fdeec6da9924bda9902dc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190214193633.tgz","fileCount":734,"unpackedSize":5558877,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZcQWCRA9TVsSAnZWagAALG0QAIyW4HCy7idYgUACF6kR\n/a3/p0Uc221wV6nbLqBtBWlG8j7/UMU8K5QuP4dw7l9B2lV8rXSy1W3LImz8\n2MTcRCD5kdFlk7vastsHysMaMhFOyTobVULGWfFT60/M2c4/dGRq7G6lVgis\n0GlRj/bz0uXdo4jeKY/rn4BO68/ll6QG6wOrdNBlbAy9n0QpUAVO8XNKhsU2\nQew7F1dGdoXwTLdiusR1EQbY5/+Wtcz5h8GKXI/tE2LdsciGldtfTkx9CBww\nYxaYnT86jDMT44BIZAOIh+pxPcMGPWQIY/WAi7V8JedxDnbuYjLFFPnxbAkr\nkqfmQc2iL4zuVUvHqU1UMBwgqy2l9lCIESYg/+Dxx/X1nshnA344vEOSGTb+\n3fENFWIQUZae3zj1B1gSg0AK6JIzrsVaS3uid/QK5Y8t92z/tFpcsBkRcGcs\nOBgK9hWSap6nx1+4f3yU3zcKC6/9XH+KG9m+4bLatV8fTH2FSYazu5pNf5lE\nmzr71PwzI1hz7JFHp+X2mmbjgD4UaiIxugv1luZ9pYMmWRyw/wU7ryTTZ4Q9\n816zsN0qzPulPW7Hfx3dA7FgxhKdvXFoDbe7NUUYie2jurVMDYpBMSBLikay\ngb38jeq4or/QlRBX+QvFyred0pBgEyTEwL0PamTfnNN5yzs4NUkCr4PH8Qjb\nSJnX\r\n=4U4l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDi6NvWrCVYmv8slEvOpRgJDwXu8WPRLuq/0Qn4UclIQAIgcKath7qpJjsgTwC4Bv3C8/6NOJwgUHhCYMZSrNrpMCQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190214193633_1550173205484_0.22886560144555967"},"_hasShrinkwrap":false},"1.3.0-master.20190215112213":{"name":"@atomist/sdm","version":"1.3.0-master.20190215112213","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"98beb6085cd276be99468364e7ca89ec84fcba61","_id":"@atomist/sdm@1.3.0-master.20190215112213","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0cuZ+eLiEwu6FXnBviI0gvAqobt0fFbf4EtYzecAiOFbLqZisY+Fkmtqgmynh49Jy/CSrPoXZuaKwHY5IjEGTA==","shasum":"d99ad27cbf159524bdd233122aa6d62ab6323a04","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190215112213.tgz","fileCount":734,"unpackedSize":5559201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZqHyCRA9TVsSAnZWagAA77oP/0e6mnQByrd5awbHeWq9\npPvf41lqBq+ltCRqUuEz1I3uD+4xH6yvbQ6rzrsg3PeBrylWRSbkWN4lAQbO\n7qRnyNLoBV+kNUamBRir83y7SgbN+9L8bpUWCeo3zN4mvDlu+/toSdRoGiEk\nFYqHq/N7b5NDx3XhUVWKjKyC1YiLX9Oa2trN6Y98Rcic+peGP/9Cpv/BJtjN\n3U8tj8uNwmJ/Gq+96ST/TQeFjqf4X4/ZNbLjYxtxphk+ugm+CJihQOL5VN24\nJFeBTfLk3BKJddcITWRb4EHoXd2FPNNDqU5CX8NmRtx9EvQQiZHKsFylr/Os\n4sZgHVCLyTBwY/U19OhT8SjVP8iwA4HjUBXwpFqbjpd3HtDhHgIiDTlOCRV5\nRljVsWXLeckzcdUBvmIKL/PqjV3Jm/n1WJ805qdY+JRm9jTUgIJakxtO/vZp\nla1nzbkUh6SN7tJ15CF9BGSqUUVzQhwPc0XXkTQS6r4TNAbsvEmz+IhJAmK9\n7b2AYjNnKfcMG1Y2w8raoBwcZdEoFkycreov+eo+umkeZI9HpqfNykPmUX1S\nuBDhZQd+idmCEkfZeSZLQhX7FbReNTPxuUhlag2QOg4hjXIN8tgEmNqd/E1h\njAtN15nZJkRX9+1FHb+MhFN/AIbJLYfV722yUDBCeCQX+vH1tt+yZGrG1rOd\nigsx\r\n=QNGO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCHXaSYgq34yn83oM3k3mnMb2HsfxrTON15UzqPS8VVsQIhAMlWwurNHVbjFAdEuihNYoBn3QEKXb+TZnGIVxkP2r3W"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190215112213_1550230002179_0.8378821501263769"},"_hasShrinkwrap":false},"1.3.0-new-code-gen.20190215113440":{"name":"@atomist/sdm","version":"1.3.0-new-code-gen.20190215113440","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-update-deps.20190215100321","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"60630a952656f0d035e241076a334f635fa86a47","_id":"@atomist/sdm@1.3.0-new-code-gen.20190215113440","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2EI3Gonhmj+9cRRTHa5aM5yzLlw5P8FOBOLdmbDGi95DosjS/U+dBkKs+1b4+N6SJTOGgemmqJqdRhWJPJTYWQ==","shasum":"90fae51784e97c2084edf236770c70715edf90fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-new-code-gen.20190215113440.tgz","fileCount":733,"unpackedSize":5554824,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZqSmCRA9TVsSAnZWagAApvEP+gOQAU7J54u88UV577eF\nkIPZgwBaW/cZFaKbFChMcp9WdhTPBYj5CPuS08d7BYfFRxAnuoYcOP51u2bi\n3Jh/LJ2RVxIX92OTWrxFFrhTz5wJrC1Z+aSwlzKtkzY6QBS/cNholkvJEDJ5\nGC7V6++69xwYIuJRc8d+2atj5W0O8hjMK7a16K08dkKAxkgoeHdtjp89fCu5\nx/nBTdrjkwRg8NsOhqb3pUDocu/Aa4IPHS4se5tR8ytqy9kQhCjFGbuKiO8Q\nzM3z5UNIiQvfqFwg0ASewo31jBJIGXNttoaHUznpa/C3cAF67E8xWbE8f47d\nr3mELpyYWBgLVn3UQeukGovRafjqUIyzPBKZB+/AakwzIGtp+X6YEbRqxQc5\nol5Op7kfL9qul0pZSt6xon6C74db1sUidZz7LDQ1drFxBc8rzaWCGHUhdVu8\nEZR2GSyDcA0F+5WeO8gjPMTZ2hQ6/PPeu4mWixBcop2jQQjd8M1+DSmAbRx1\ng4JJq+cV8YQskJnqVP6mGGGWDxLdlz7/rStoLxe97rVloEpoxX/HjagrHTws\nBP3RBCNMofYFSZ/N2QLUvW5WMxvGR8IUvlTDv34GB8Evs2BfCxUDM4HYwBD5\nPoxhCJLjbNf9zbv2AG//eGZQQ0zqBK9/qA6J34xnZMHhP4R4KVBGTVz/NfCA\nNFaO\r\n=n9JN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDO7lpyGqj66nPZz3XP/5+xI8JECuzMltNGefTbPy+lowIgX1DxAGK0pcLzEPMrPGFC/kkzDmlwszF/17kXupQJfco="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-new-code-gen.20190215113440_1550230694224_0.537933895409344"},"_hasShrinkwrap":false},"1.3.0-new-code-gen.20190215122905":{"name":"@atomist/sdm","version":"1.3.0-new-code-gen.20190215122905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-update-deps.20190215100321","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9ebd54f95ce0d67badb48e76a1c140a30578ad1a","_id":"@atomist/sdm@1.3.0-new-code-gen.20190215122905","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-S01i3p8Y5UwQANUr9wQlA5GGTM1OJx5dtAWsAG+E05Jh5Uz+fm0vQ2CDrn8nnTyjQ4eGH5XN5Cg+eFOj3nU3Qw==","shasum":"31491acf08cfb00f17cba80306b99bdf0f858155","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-new-code-gen.20190215122905.tgz","fileCount":733,"unpackedSize":5554743,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcZrFrCRA9TVsSAnZWagAA0foP/iRer2DpjbRlfVqu2Fhk\nbV7PqLWXKwQ7HwGMApNaZYrbi4F2/YOD04MyWR4WAP6NQ57td/sgN/HPDnI6\nH3Lzqe1E0CKz3JWmy6Sd+LbdQt1zND3ED4rliM6AuATlWsMa/i+xPXT1psVM\nkku3I/TxfvCItfLFzuL1UxBFyif6Z5Yz9OzANrlhNq0dx8sJF5aKlE+OXAcv\n5cyqzN+z6ngwr2pQGgEzObHEDrVe7ffRChJBAqbQjMoBn3KvyRC96/PTIpBL\nCGIJr1Za9owOBZtwpVhIGGPyWmarSdpRX/HqJ3377+27sZcouKSc70uHImgr\nwcO6Sc8BrQ5ldaDUprI086IaW3zfW4BiuiCm2YhcDZ9qbUJMvuhM0t11od+M\niT1Tcrkmz3UNDxyT1jr8gDgWpuX7tttdWGmZmNarKoH0Yl0NkvU+jIvVSL8d\nY0qG/wn7TiB2v/Mu4VuXoXk+Xys2bq3hrYy+knNLcuTfCnywB1v/vmdmI0nn\nRxp07BQ634Czn57YJrPx1M9IFNzO9zVBNwjLr60hTsW6nUqSLefpdyVYhxwe\nqbB1/AzvkyCIRX8QlGosiWy+7zdGVueYHYLpNuQKZ87cKyzbHUNpy1Vbjehd\nCGdPXUXDjNYP+IqU4uALS/K5Voz65Ok/EeRte5fqN+Cho35Uto1ZY4zDe2la\n9cFf\r\n=6Juf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCIVlHKPV7AZ7qunPBtveYe3tAIYWJVFfeGNp1L41He4wIgD/JiMu5uMoKJHVfgVBBuwQAYWKkQgbF4thKs1pVUzZs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-new-code-gen.20190215122905_1550233962462_0.8393297384959508"},"_hasShrinkwrap":false},"1.3.0-master.20190218084049":{"name":"@atomist/sdm","version":"1.3.0-master.20190218084049","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2d23086621e95ba1dc8f3f42fd88cdda54df85e1","_id":"@atomist/sdm@1.3.0-master.20190218084049","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7ntcCwoA34ySnNWegSZgkQSWwpL34foq1vRaYx6lAUtOIfT0T6Ysa4yvzUrN1PhvlUMHFZzAy11O+R1K+vQjcQ==","shasum":"d41abb346a75fdf4aac8e9b33644f4e388be1d06","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190218084049.tgz","fileCount":738,"unpackedSize":5563426,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcanBbCRA9TVsSAnZWagAAnKUQAIJcyUVd3lBMlu+i8PyW\nd25UufA/TZgV7ocekxwjO4CiLCtCbok1agEb5n/sNpaGf3x7NP7zPPKUbIJn\nMXtXbjtqBvfwmKkHYtdINY+v7hjYlCM4k9B41eqAV6Auu8s9A8QmWPSMqvzQ\nzrDEK3MuXrrATmQKxvC0kkAUV9sSBYAOkZsq/cljli5srDURp7GEYr0VVVKZ\nIhKuYrGBSrK35ULUgKBkRteaJgJfTBbtXw4zCjOrAkdL7vQL2L49I2PexEiG\nbQYjxBoAvpHe/ok7mgzt14n7LMjdc1n6hR8gq0pMP6bcMGFakI2Zbygx0TJq\nQrhYWVupbCQVL/yDlnRqPWK6RfV82KRcCF2xS187GI7G2hoCB1jSHSosqrvi\nWrFFciHxEpIji3ja5aLDQxbIEKPXa0sInZilcpN/fHh/pavAsM006pfL1mlh\na3xs1Tbv9JPmQ2uzp/mvc45avpu5ndwWqQaBAZT4Hj6sJDZNB+KEFjLjAW0q\nXi9+/hcRAy5zSFtafjUPDXQPTWFJSKCrzshalYx3HlFosjPY7/GCnm8xL4mT\n+0Cfns2wfoTq073Lv6DeeCj6wfW3ltRho7G55wOUgvYVD92CFR/FRzULG7ib\n29OulaKUxZmg2QJeP47WZrvF/zA7Dlxyb7IW8KKc5LhsP2iyLOSbYKXjb456\nhkr8\r\n=4Jkl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCm8gPjgRcTLL+c2SgQ374ThdADevZxqO8C2rX5096JhQIgfzirOPXlKG5lLk/My9wU02RshJtus6dwQyB3m0sX45A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190218084049_1550479450545_0.49254591858811825"},"_hasShrinkwrap":false},"1.3.0-master.20190218090039":{"name":"@atomist/sdm","version":"1.3.0-master.20190218090039","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a800356761cf5f2cd3f3099219a70c5e52f4af45","_id":"@atomist/sdm@1.3.0-master.20190218090039","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KO9rSs2xn3Qz9C/ax9h2qFAWI6ubIx5CMyqDGryv0DmL5r/DBpNqyomR9F+7aNmzPZ5gkWBIfzlVAtlvBQgwWQ==","shasum":"04ede9e6baae9db07572d419af61fa984d01d443","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190218090039.tgz","fileCount":738,"unpackedSize":5563421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcanUhCRA9TVsSAnZWagAAryUQAIHxKt4dfMpSk3dpkk+9\nlYyL/Y8LFCbEHD43s1ePA1Ow/MCoEiRcb0Y2utlbWmAgLxjZKiqxBcmcH64N\nc3AtgmJ3OTHUwUkKAvCdtitfr7m6YwB/9q7vdjHv4qR2pPVTkQ/zbpHbsWOn\njaIUjccuc6VMRuaRePpDWLF/rbd0/a3G4EsnIziSZo4lwxeokpb5rJWDPwAW\nsL6kA/8vG9mh56a8r/ZNbKHZFNopjF2RJJrOJk3SJYw0LhrUNFzvdKdvJ0Cm\n+292iYyb5FhYwlTp4vuN4LNzbu3Tdboz9+hd6Jo065gMWbP22V9BP5zhdMub\n04pqdlkZnMSv/BDRF7mfAFlFZ7hgWeeJzVzge4Wq3YbsnBZ9S46Yp7NXteCV\ncEGWGDHXUUbadGQlxPhY5+hi1U0yzwrf6cebMqoSEYyj45l8xi7i4zB5Oupw\nVPORzr/TMcFY+UzIAJtDrmzEZ02dOHADNFODV/w5OTthsI093tCe58nh6pck\nLuNowci+670h2xkynrjUwm+tJ9au8gEU7VjxTAw/kLZj0VQ/SZX/0yM9SRu1\njMTqa+hZynSOkIavEx7nSVfl+B3Qan5ehnEbZI+W7WGWjpwFJ/+nOnFVALCX\nr2COMVzz+MhXZUr72YSLOSGABPV/MfM93tzt3bkZhLoPWcuQgj/0LSzKveQP\nVHfz\r\n=krDk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAQ9Yd8InyVgRG1VX71iGMQRCG0zap+12Cg9I1kuNQ+kAiEAzp9oupeystgQ6fYdKWcdjRb/jM2Lk3DveUaTJ+MEL+0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190218090039_1550480672527_0.22563998778047734"},"_hasShrinkwrap":false},"1.3.0-nortissej.accept-dot-extensions.20190219003021":{"name":"@atomist/sdm","version":"1.3.0-nortissej.accept-dot-extensions.20190219003021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"397e482e35e40732f239d5e9f4cb5b307372f072","_id":"@atomist/sdm@1.3.0-nortissej.accept-dot-extensions.20190219003021","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MwiF0IYykEnLg3uOIWSyJ3fcpo+Edi0GwftbZUQdkqmtrXrgaMCxIwC/9CFbo8eyq2h32GDuH6mT1zfWhdXlmA==","shasum":"555a1f2c3e1b1ee38a418ef16b8333f1b2a1e144","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.accept-dot-extensions.20190219003021.tgz","fileCount":738,"unpackedSize":5563674,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJca082CRA9TVsSAnZWagAAWjkP+gKvvw52CLG7wSYJ5/16\nMbo4k7KQRI5HI5lneZqjqdINw/bthFZkCnyAro3WJLkxX2HCKZlnD17ou19e\nMb1RnUf0NakEzx6EbyC4BRB36y2DuQ97YaWhuF3xxfua6duqd8mZRHXJRa2A\nM7J4691qFRmocLzSaV0pwSh+ymbhauxfVfE7GoDKs5RjP8AP8jnb+Fdvfsq2\ndYzz0ZympGvl8CQPu3RbJH0OI/6sVPEdh4S7uUVTiUAjUp2xl1kV1pijbuHB\npGMKYGFpZV6kG1x9BLjrKZ5kRNrCUUI/eyDeM1Ytnj0WTxrjS1osvezWKUED\nqrDYloYFVmPV+0TaZTrv5FB73oBY25SrHSjkWhEfbKtMUvm7XR80aORGqCTM\n/4OwLORLIxHddIuK5q3GRr7sot8wDpPvahdVaDHWYbY/4GthIozJHCcC/eHp\ntWI5f4apo2JtYmxJNvqMiHh4tvz8m0Qw6RCTrQMRJtLbn+hwd0yfTRO/tkmh\nAguM4vwr+kULM99WAgi5YII6b3gOylNfcgxpsRWoemKf2WDgrXUNk/Ft3++9\n9XB8TyFboOA0zMTOCbgrq8c4RvNZQ7HNpLwV4SfzZIBXb8pVhiZGrpeY1D0A\nXQRLRvHmXHd2gfG3LqASTMF3N9ZPJnu/+cYJfc7OJAM+Fk37mN31fixHKCFd\nqBZV\r\n=miqR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4d4SuXjCKE4hfYZ8nZcYxHwRsKhFkBH+bHzYHuY+MEgIhAOPxd0ULUn8LuJnJ5yqJAIvYlkEhx18C3cZY2GUbbJA6"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.accept-dot-extensions.20190219003021_1550536501587_0.9621096779931213"},"_hasShrinkwrap":false},"1.3.0-master.20190219054656":{"name":"@atomist/sdm","version":"1.3.0-master.20190219054656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c8952cab9d6522a07de16d06b2a510b646892500","_id":"@atomist/sdm@1.3.0-master.20190219054656","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-x+oKgtc4QbDrCvvZnXuyF1XZWLFs0aEdF28HdVLsTZwp4wRbDmkd8YXBoFKlIjCAnqjD47pPTxh6BkDJAjGvjw==","shasum":"37edeec3e48da00c62df74b84726643a6f0380ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190219054656.tgz","fileCount":738,"unpackedSize":5563758,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJca5kjCRA9TVsSAnZWagAARKYP/2b9B46WL1d8UMwg1ZeS\n/ANY73Ov2anc9VnEfi1E0JqEhZuDazbSHitJ1OWAeLe3thbM2P5pd8Mao55L\npvehOljMJ80JCdh6deVehgwZ5sKOVUCpmYt2fRIFZXB4HWqmyI22xuf7ck0U\nebkcIPkKkXoUttHsOdUsugSWPIrv2WIC1i4cd+lTl9Y/7W0gjyTqi5jAuu3G\nPnJJ28JD9qGWuESx7gplVEXMnKaPwxu5rnmUDH5M+eBLYOb8Y8voz6W/l2lE\nCAyeakRi71/T5zyGZa8Nhvs5M6XeF+9LrLm41ByQEIPJmP7wxV0z0d1peiIf\nOMSeVoApkdN3pXPZxFD1ANA/s+qFjOFbqWAz/Rju4BjiIiqffBUa0ONh04dT\nGpanZLj3oZVbwKTanBwVkseycBiM/ftBnqPuw8bqfJHlXrVKq/3qeaeggrV1\nxeFgwb63IWhluR7Daa7WijMiQCqUCRXIXF41ai05kYYA0YHdmKBLFyjKyZRq\nqGi3aP80N72Vl10MEAaXjYD+aAjYaKvANsZO3QSa4NcltNDGKVZ/cCcO193r\nnUd8plPel4ZvNC2ZfLpMF4DndO72ZVY0IMAzoUzfekOOK+JMgJeMx0M23May\nn2Pr1gUzPd2wtWw+1bHkrBO4XpheJkU6d6tCDfQfSU49JQ4BwOfffr0LwMAm\nxNU2\r\n=hV0Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDfgoWqE0FpjBRnc3eqiiyQoeA7a+kD9Vq21z4/sVNBZAiBwB9eUJyW4t6C9kCHpzDTH/V+j4CSW8d65T4P2AlWFgg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190219054656_1550555426764_0.09955040855921582"},"_hasShrinkwrap":false},"1.3.0-master.20190219122245":{"name":"@atomist/sdm","version":"1.3.0-master.20190219122245","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7b211cc07e360628d93d2c7e994722f15ffd3628","_id":"@atomist/sdm@1.3.0-master.20190219122245","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IKHnNXa1DezkxpArQIU0wdFPVbrXAvLpKGogsU3NUVPpJTy8/y7Ojj6qF3rVjFOHk9GRkJidxQVuSSBZbGATUQ==","shasum":"46e35a74f6040cebe67b79a46986ab1b9f047800","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190219122245.tgz","fileCount":735,"unpackedSize":5564142,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJca/YuCRA9TVsSAnZWagAA0bkP/RgZfsXHQFyRCGxCZ8EV\nmkxJ9dMeV0kyqAO3nE0oaihygJAsb/48AxNnDkpJQxSXvwiww51uy3VGBj0U\nhGjydRmuw+uEoFJAfMp9DjD/Sjdl8MmHCKNFPEQAHozXUjrh+HRZdC7dKolT\nsQS/ZzNh4UU8bJtV4Q0FVTg7N9RsY4HutI51oue66mph2004DE3L328eXBKi\n2QBesysxJIABfnGECx9UA8NA4Dk1mdL70Ajot76jbT/7fn702HHnQ7R/CFPI\nLA+Etg3pXSjqJhjicULqxQ7DZonqNsN59Bb+hr4pSphER92FYWZluOUP9Ou+\nCsMGrDROCKMd+0aip10/a4eGcKpqGxSzHyftG1+EaLbl7Jo9EjIQb3oYnhNk\n4tiFgX9StiNEmivsZIMv6+0vV1qGjkbW90pg+U9ypKWj2ZvlPbqWcODkxWkE\n6h2w+A3eC9hx8k08pnvOUyIwf7UR73QXxAFaaANaXUuteZhuGcsZYXsAfSUP\nsBVIOGScnLRSTO9wW13cRd1m/elbRFYqaJuSXh6sXkEStV2gOSD8BCxI5h4g\nDOr0dWu66uQqovmvzkPPF0+zUAyfRp85lsVwL9X/Dypi6+5hgNdf3ut4WVyI\nCL2vlyfwfDL89AGd2O1IJiBp3mnyKYuGZQPm27YyYeANmVfm/V/z4BXPbX8v\n09YH\r\n=15X2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCcygAipros99JGd9nCFjZtHDH1tKaTg9c1GGBMqLGayAIhAPL9C4DQuBjowQIkCXWQUIL1gMa8wndJ96NoCNH/ac6k"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190219122245_1550579245149_0.6739045246218995"},"_hasShrinkwrap":false},"1.3.0-ff.20190220085712":{"name":"@atomist/sdm","version":"1.3.0-ff.20190220085712","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ee131d093e4fca4f94dec5da302272eeb3a4b4ae","_id":"@atomist/sdm@1.3.0-ff.20190220085712","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-d2TH77kv6hACkYIDTez62ETQkCninI+Ty3QjXcBRV6BmMXUCLaBhR8a5DuFWwARBHxov7rQkuc8dPnG8nQvFCg==","shasum":"814c9b0076061021a827311b73ad4a3dbf698e7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-ff.20190220085712.tgz","fileCount":735,"unpackedSize":5565563,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcbRdMCRA9TVsSAnZWagAAZGAP/1nTqmOav9+zoRpVhVmI\n284F4rzghNypkiFmEzjhR4mcufCXUs3jV9/TNiPinyTzVoGFAD+2u8ezEfmH\nRU2CiXwUZVgofo07YtCNsG00TCmEo4rARdQPi9ItHkuHYzKKVznNerfQQZs7\nEL3lXfJJAeSXxh9cmYhFVG1fZr14a7kwWoKIzf3PVqqE0J/gfcn1J8UaZ51z\nUQAzqYtZjMtcJANVEWKdcMJFO2vihK/RjYXvbwxH52lWSRwsWjXliaf4lMSS\nDWaoWvtZ6F4253HxOn2YgGhyiYrMUS/YIdoaG4yY/3J2EQAsdQM5GA0rhwKo\n5kzYqS3y93C3l//hocZOEE6fCDC+2abqqPTU6NgJgoZIEsdKXEVsAqazBbKI\nDgj62tkRococKdAtkeEVDJrBO4mU0K3N1dHzhPp7JQk7d+7CQRpwhja67h3e\nuO1ps6afEM7iiP0DisHj8eWvic2FipyrDH+y7+/+WaI/MM4HElPu/jQId7Wz\nz47FRna1Uo0vPEKYowr1PWAXhqoaboujzR8oRsGVN8GJSUfOVvbkYiaW1Dg6\nRNDVf/sZV+OR0AsPUfRfVr/Pi3WUEJssEMNoKmXy/PSiefEWCtBJoXf40sBJ\n+UO0cbwCfAsO2WB4rAtVzJKl8Ei22qDs/AW1x0MGN4puAWqLs7IilPl1DC1H\nA4r7\r\n=STzS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKndOwPjCzGnSz5l9+KbaT9m7eV5V7fTuEX0vXKYJ9IgIhAPoalmzZ8kHGI0thd4DyQ8Sp4kWdpgRD0PTczdSsUS1P"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-ff.20190220085712_1550653259861_0.9374043339992317"},"_hasShrinkwrap":false},"1.3.0-master.20190221190336":{"name":"@atomist/sdm","version":"1.3.0-master.20190221190336","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c3c048b0791df653348e5ce3b876e734a055f505","_id":"@atomist/sdm@1.3.0-master.20190221190336","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-2DJ4eN6fAhNPHCNOfVNGr7gSt1gyVzDBcTLg1qK+Nj32ALNkA+HzJ8Ww9u//bTAkvjGIuRN0jbDIxylmPGj7GA==","shasum":"79f32969c880601f4c7230a43117c27dea52b0cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190221190336.tgz","fileCount":735,"unpackedSize":5565659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcbvbtCRA9TVsSAnZWagAA2NAP/jdLwSbKjqQC/7QDPJEq\n80MYjg9zUDp3Mq3gotRTGPfuhsaOn/ZxIVu+kkUeeMk3rjANUlLEPxPc/7MZ\ntH/RDM4nbfsoMLvBqbu1uoeAR95M0wS+2+MaTJXsBO8wpruauq5C4pzBi2GH\nglavVA89PQAekLcE3/vPxaeHn4k/D78yzQBPmkIOypcSW1mkANOpXLU++1Bb\nJ/cGtYypx7FQWaAalMOKIOs8SSoypbRo+hwqjo2fKaLUk6hzcXARRKA4Amj6\nlO/aCC6xmB2xnkU7zvzJKMJW8F3cPpXUQ6zFUAIWtJkdICQFVWhyE9gu7eKf\nO/q+uuWg9DU8BaIWbbl9XNbQ+x2htkWy4SjunM67Gm4t2a85lcvsD5JB3TBh\ndxz+J9tlpc5xI4OPVxRiSwWhYBadBxkLomL3Ig2xvl6GdjZo5L4lv3YqdHYE\n06yyEAZ4BnknKKDEQ9dHVajctfcjGxDEkLjdPS79+KkAZvDgMugQ2fL5POA+\nLso4xAdnKJk9jso9citwucF5fPOJu9t3OOHIN8Nbkoap9Cs+MFG9G8asc4RK\ndsSsa+YIMwMkohsw0jhUwnl8ExXRcMLnDYXb5a0QQS8DXC/rhn+qh6dTfxwR\nvPYB4zV1KFm5oTweTau7rPm/dwf/OReIyW74IG+cZCnjkDgYk3Zfrzm4km0b\nGt/3\r\n=uFQ8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE6jS9y31XyQ1iyksmsa1odVRYTM1K/yRtdo6jsK6gr8AiALgclZtxARF5rCqheZ3RXaGwUakjmn2xeF9RKWdi15PA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190221190336_1550776045059_0.8133659742507844"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190221190343":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190221190343","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"adb1037b2b20850e2022bca39aa8b718cf55acdd","_id":"@atomist/sdm@1.3.0-goal-signing.20190221190343","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-HD9tkZ8gfwwuzpimOm+DUc7/y2jzYoQP96GuUCPC9Hm5aO7F1H3frW5tGeqx4w5jyhrAcKVF338sSjhr9B0RtA==","shasum":"0c08ee2e0e6fe7c1c3946f92ebd875fa6210c507","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190221190343.tgz","fileCount":738,"unpackedSize":3065256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcbvcCCRA9TVsSAnZWagAAJ8MP/0+cNLNwcA+wPJy6A1bw\nWwx5/oj/B4KjCR78TVRDkqSV7LPPKlOXd9N/Lh/3G22s5B6XD6eYtgaQBC0I\nniDVB9ZwHJVwe+gZvC6DQ1cqHSISK3dY+K5doWe13rMaHnoxde+KthvBkN7y\nHGt+PmQk+5PUHwNu+hFL+eea4SaVa/sqgpbMmWn39g0bgZYXSEMGxOnSje4G\nhma14om08MDvrnRsoT6w6JTU/ZN2MI+Ob2JcI+aT/o5vIr4FtPH0hAt1vdhP\n2FM6Rf2uxk6Qwyv+nFP3tZwz6ljjLWnUeb1lvGErBxbWpkQ1o4aeVFJhpqiP\n+R56g+zquRBn8CJUrubvJW0CT1hfZXth0ACqnO9KAF+IXp54SHXcCM4CVFtm\nMV18bX2TTSwk4Na2U9/S5Lcbu7gfD5KprTPNBpl4RU4i7sxp4L7jOF9ivA9H\nEOr+hXTE9/7qkLKVFCLA9MKyehINMXcGk7j6HR1vP4r6YnDZjnSLia3osV11\nB9vZ1P4ARh9Pr2jVYQP8X5YSgEvj8wL6ph5ko1JftrF/2hC1IgXftjcgsiOB\n65+w1xQqyKqTsYAn/8MT1NHnyMv28Z6uJdTDIKVEDZ9nL1aX44I0OlPj9GMt\naKKViRc3DNGXXF7VPLTT5LaPOH4XWMZ7/SDttaLrpewoBPm6J2lo0yE8XMPL\nnO7P\r\n=7PK6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBpB4mwtLSPcMksUXRtBpWK4F+5DNeTtnCYN7IC5E6ybAiB6i58y6YgZgQD/wjmv+K+p0K3uQvSgNYCv+lLnWSETCA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190221190343_1550776065682_0.6107559784083807"},"_hasShrinkwrap":false},"1.3.0-master.20190221190606":{"name":"@atomist/sdm","version":"1.3.0-master.20190221190606","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c3c048b0791df653348e5ce3b876e734a055f505","_id":"@atomist/sdm@1.3.0-master.20190221190606","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ITXd7sLjX8ZBFMjMX21BxQ4N85rCZEbQ0msRZD0YiWMiAhdPc+tEtI+OVDpRNkDRV1JhHREGwIIq/leV46BR7A==","shasum":"6d40fa0ccd7916027f0a0c768eee08a5d845b07b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190221190606.tgz","fileCount":735,"unpackedSize":5565659,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcbvd7CRA9TVsSAnZWagAAej0P/0vzs+oxjpD1qHWKL3LD\np9Wn1qzyyUJg4JIGA5C2t57SFu+aQ/6AfR4FCcW1eMDKXmOemvrKCRTxYxMh\nUBUbvIIdKalab5DiiqFBN0bP3yRaBNrAlcux9foL0GvgzU2USJrjPx/5jMSY\nOeSPCul4tQbrc9LWU19iFZtAztFa3LZFLj8RoRyEIDtNfCGRjix+C7qWXOtz\nfcvToMAnV5WxLqN7TjGTLpHA5i1bQcMOcg77CBbT9zKrXWS7P25fPC+uoInb\nclqRocYXyMSFDxk9FSrZ2sERR0GPIwDWmts+X8Nj6AwgjU+BmTv/pju0/6xY\nQkV+l9gH8JIsWEryl/+5yQDMPjgQ1vRJLq4JA2qgfoG61dCUBa+ObzduYOBw\nlAhxWlK3MYCdcLtDxepMF5ObcZ+2IzsVRvw+GbsFMmhw31K8ep4FaKImeMdb\n08QPNui6h4f2c0+L2H1Hwt/V/hGMlTRP/mglR2AsUkdDLCMclFvTABrs37uE\n0HpYp5YdxizoV506ly26NlVkAFw4JfQc6CWYI09zrsthELAIKMX0n5P6pAyh\njKsA04JIVwid0tK0OjBfO+3XTJbgklu3xpUinS3nKJpwxOpW1uA266AMkAtw\nVEO9xLpx9i3z9TXPBRV9pELl3ah91/U/mqEIk8mYebTdVAuZUP4KE13mC62u\nZuZT\r\n=rh3d\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC5W8rk1LgCqX0KIjoHWdeaLvlcMH/KKCbDsVC9ddlP+AiEAlhB76ZOv4qkEuT1dcZJKQvoANyyR0q1AhHaBxwp00lw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190221190606_1550776186194_0.15118166402663324"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190221193315":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190221193315","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6489590f9d87f684027cb9e43471a55b2bcedbfa","_id":"@atomist/sdm@1.3.0-goal-signing.20190221193315","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-KzwGGKqLZ6N/hQE9lDRFV2IiLQ8f/8nTK6O59xUeokxImGCsXiLQo6lO0SeEsh4CjVH3IlqrMXBwikezbMu+2g==","shasum":"7a186e12dca4281273234c006f7aefeb3bd5ac4f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190221193315.tgz","fileCount":738,"unpackedSize":3065250,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcbv3KCRA9TVsSAnZWagAADaEP/RQLPGlza2j98QWg7dzL\nPnRf6J4PMJqsg+PP03vu1v/WiHFHSSnm8ws0Qaw+X205dX9n8wvEhPfK8EuL\nhjwj0hnta0SbMlTvdDpjvn4Xcce+TR8agJWzWRWnZG2Ijd9k0wmueo8rWaSH\nbfXASrO+MbnDKSlemdAeq195ttmo7d0Fb+E0X2V+YE6qoJ3vCIuy4vXkv/JX\nY+MJNenbvcOiy55hnjiSjw+JoM7GhbOvK9xsPC34R+t24ycK/vEMIQ0j18Yj\nGvuCXuCal1h+732Nh0OIw+PXiJQQPCL7rSFNpQjeNB+fS3OHWRre8nR0C5t4\n6AezL9XrpB7l/TB+tvLZaek3SpLctqQuEe6iXu/G1tpbr7jmk8V30NQvJVnM\nWqxpQFzCpHex70xlE9AmMXh7htM+GYiOMcV2o9dUmU4cW4pYm79YtuyjuGRM\niExonL0emITOSGob+U7cRKpZgJo+0ogvi1dwDJ+vBvyyxNHAf2HZTtw3nfh7\ntb18n7tAw+rY27gLtm9HLu2Y3oLaqlvvZQLOAd4r4PWYWkB0MAphVOsy1efX\npy8jctdERSDbn21Ne0bAbBjZBWK460fRCrbYFPwUpPudVI24K5FQfeHITM8p\nuwPNKK/a0bUH6ReFNv7XD9kWUyYN5tZO7f1leP1SWC2ey5POT4j7ZXeGQLn5\nsuvE\r\n=W45p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQObX34ev6i3wgb7YXU5ibRwEO7HOljvoxMmzvl7RxeQIhAJqgQ2vNzHGMJ57yGyoNoU6vAoKGXPTxAGKrw0rBBktU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190221193315_1550777801301_0.5136149805167487"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190222073550":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190222073550","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a3e24e2bd78e57663b7f35869f66183833393f36","_id":"@atomist/sdm@1.3.0-goal-signing.20190222073550","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-7t7GFZGuKJIxoQHo9GFrwmBOnq0YnLK1SAWYD5hP2RY+Uz4tsDPhFxk5jcKkXYhcQDrsrTMU00w/P49A8Ch8jQ==","shasum":"496f66b082710b2fd02885da04200f37137076b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190222073550.tgz","fileCount":738,"unpackedSize":3066206,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcb6cPCRA9TVsSAnZWagAACbcQAJRHt1CkhjdULRGmsZXv\naECz9s4xocy9TWGnCXG0B8JW0tWCSpOWXO/4MNG/QSv+HQVT3NcTOh+dRMbq\n9V7Jhk9/5MaNz/WT2z+WfPf5n/23qequfv9iIsoMwWe60xr3y9MFIPTZDMqV\nzA0ie6JVYPvDhn8zXTbOzOmMA+gPC1tzsSbacFLQpfM4H31Iv0JHBdNI0/j1\nRqvtDipypEtpfCAke58BeOSG5aFsXl03n48vnAxA3+SC9ySTgWCRLIYNBL6C\nD60eaPaonTOA9w/PLQtQuYBJ9y5P3qs8cnNSO3rtFwiuYsXTfX9T1CPMuJ7X\nwJm4ILtVXl25aQOb1cnuEMG2F/clNh2kCHNmsgxVepr61zlweLTaAJ/aFkOB\n9p3aiCdtxirNPh3/aHuF5jhXhq9ef95mzELcebrFIxEz7SOCBQIqyk2ECIFa\nUxhUdvPGTAZz8jbtQm12+Y0vH1wml0ynA7+vIww7tQHAbPqzCFjuoM8oHSjU\nHumE5f9VR2p5vXD1nJxIwfp6CC9mVpA8C1LG9SZKsfJcY29eKOFIVh3qddB0\nHtw5h3CldUxMGEFgQGEd87zTnEE82wTRzibWTvMx1yIBR5LoFIFyJrGjkWJS\nmJnIw5tJvu6l6bb+PvTqqchubsprdgeOOLZH3kaP2popVxAAcI8y15moyk0O\n3Yha\r\n=7HwC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLMfHicjeZJpOfwOiwuxwKV5yx7ysN/fkZ6eAzS73afwIhAOd8XOZ6uXnSFtYCgeEVAdtm9HGPbfPEafeurE5OmGxY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190222073550_1550821134773_0.12264737977783247"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190222131507":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190222131507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2f10630d6dfc0f60d64f8a40c5810938c61bf063","_id":"@atomist/sdm@1.3.0-goal-signing.20190222131507","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-nOQp/D5fpyZBg4/ZvPB/GZocIeQnlm4SMCSPMhjRAXoaby9GpzHDsdvTyAZliICPB21G1oy+MBG/mpiaqeonmg==","shasum":"414133371f6c289a997359f06f05e5c20ce458e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190222131507.tgz","fileCount":738,"unpackedSize":3066785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcb/anCRA9TVsSAnZWagAAGdIP/0nE6Lu4QdhBBW18tJYv\nTxS6BSvWRvuknGztiOgh38iU4ko5Q+p8Tp53iIbxuIXf7cdBeXwMcmQQuR5o\nwUtbPYmPo5y/lb0TtqYQZvbp7GbXjwg0sje7TdaRAKXJ31AWtUKjlBGoHUJi\nOGLcG8yC9vk18m3BuOWdrQCeJYZnW0QJXYCBId+x8yp2Ur5gLtYA7/jop1+e\nVGRg0cw9pwU3jMNuqQcmmxm8EWpwkdvsLendiu4R64A6NmbByESvHO3DpsJl\ngzembOcoLCOCZ2WNdqVNjdr57c4BxoYugcBKc/qouGV5jqpyk2mo53cOKVLP\nYTHPCtcy4OiqDCbFQTqWNzrkM+TPXyS84wIvF/oBAc0FPgXgJF1McJ+K5RYz\nQaDyhqm14wJnYddg8ohqo6pz36ZvaTgUn9EMyKmaH6RbIpVCPyhRMxaGlpLB\n/fdzg7ZZX7kVaivQqGJjL7u2J3ZzIY0yYv4/EfD4YPrAEqUHQ8d9PyVcrk1Z\nWymvD8DK1iCT0/24HULTEAgovCtPj+jbhJ3ZfGEX1ZL1ZgCJdf7YsP9ag0Tv\nUlM1atdBlpIDNynYWuYAfnPitY51s+F+++FO8BQFBdtZWF+cetsdzl3nh23e\nRgwSPkJDN1Jq5lukrM06YcEf6B7USKlK+U6mf4ziR5cpWmindjRBAfzcppbt\n6/la\r\n=QfJx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfCTQ7jFDrwkZESZ7Qxlq5aBu7yBOglq5d4UojJcfv5wIgXQYvCkwYcDO/pzO0PZ52UfdGSS3HEj9xYBwi6HTv8eM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190222131507_1550841510013_0.9852656114926814"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190223211723":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190223211723","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a7a81f346b4aa8fae42c7fb4dc8cdd88c1d8bfc7","_id":"@atomist/sdm@1.3.0-goal-signing.20190223211723","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-vZ85ZoMo6dXcCOHzl4MGCk7YaKszQQlGlRL4DTND42piy+n8Kal/IQQK5a+PMxE04cCl7nfbcWMcI2R+Hs3+TA==","shasum":"1d96275b485a6ebaeaa023ab2fb5e35250a68808","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190223211723.tgz","fileCount":738,"unpackedSize":3066785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJccbkhCRA9TVsSAnZWagAAQT4QAJWea0pm5QsKEpgyq/R+\nBnsPUffaUBmB2MHbMkQplJh0kop44oZWyrOtsJj/CD86bEsmrKp7QB0BG66o\n3aXtWmGEaQRsOI7V12yoIpuaX04cNH+rP8XOvb37L4lXp68ul/ZtnxYn0l5X\nw38ZuF6bSyJkXFlTFsSkacAKglDM2qzmIIOOcPx4S4jvk82WhU8GpkK4LP/1\n6/QUiVtIl5DTDjzzKQahAQx+WloVozvahhhgk7v5Z8rhfq7KHR/QEb0xVcln\nwU98bi+FVxlcj2GH/6i4bHp7zL4RmC3zHeF/Csmri5ajBxI80eKziranLDkR\n3ZBqqFbzP7QkcgQUSFAqXfXVz2WvZt26i+IhpuS37iu7sCU19AHZBh1gmcw8\nrbS9Ku1N7HwhM21FBTPLZp7lQNAoXpIVC7GWSETPJhEkegw1ev1K2Cy6Mw2T\nDkISUPLt5JObWcfBI8wORZmcg2w1q1gzkUwK+6D1mN8oV8JAK6DDMej3A861\n008HmPpC15ESokHwDoJ+tweTujyTX79VxzXcOoHJhWPgG/UElmfS1EC85pBK\nPUIhQehHH3gPoAU6Bc3uUBpfOSHXSOgkhpk449bt9f6NWsUrS9GB8MAzuIMp\nC/KvGSELhx4qKL3jduZ5Vbibx6emoNMJPPVCTyvbiHXxZaowQ2ERu36toNpF\nqU4A\r\n=Fwxz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDvaj01cKTPdZa45qN+K0SFIJG8prox7DiQzEhtfXqrgIhAL0DxWnxPI2BIzBCmiIjnodbQylMSudbQfD1me62WKjY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190223211723_1550956832936_0.4937518646383605"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190225074725":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190225074725","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ef320ae378d50815ca5524639d784cab5a591ede","_id":"@atomist/sdm@1.3.0-goal-signing.20190225074725","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-E81ZAQOeCwJ8Sp93Zw0Lo9iVhAr8Q6OnNlQCUXi3C7MFcBObne4iUdGyB82+6khnyRn+df8pKoSbDhPh04xcvw==","shasum":"eb5f06a8988f41191eadac7715e26f5d94b9b97e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190225074725.tgz","fileCount":738,"unpackedSize":3066863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc55LCRA9TVsSAnZWagAA7dYQAKLoBy13fmOmoaWaitoY\nQYPwwJCROUlofBfQaSRlkZLsNlNDZvc51azTOFHNGfQA+Nk6/xV4N0SYLRBk\nmymmA7l/LU8N3q3OK4FtBWWI8e9kzspIXfQwWBjO+M6e07AGT4rB8YOBNYmq\nHiaExa+D1TIoqLPgcKnJeWqXapBlqFwPkJK21XMhv8splho5qJ7T4YdPndQb\nPOF1AOUeuaPmaddWi5l8IeTLs5Y6JkQDCDBP529pITTxnCr6kcctwoMk9LLU\nlEgeUfJV7nxhPhf9hSU+oSt9IxUQ4ikNr85Fha0BWo3xNdKyOcFGEfli0/3J\nCx+LPeBXDR7BWO6swtlMste3pO08+okz6SHHIwV65CofhXEZSwGkcEgt1WAl\nnrLIn0effEjffgoUucpaXZDvR+qR9a4hrGOLkFSwU728QXSipPl/IIscoVJS\nT53aO3CD6HsBjktguImrSkuxW64W9Hy9SAjCQgFExLqLp7iN5zqJlJrwUvNU\nF/lurh/4gRxjTKNOSMU4k4nI58WVHVU6H/Wc13hD7Nb8aeaH+O9SugdQptm0\nnX11G5M8Ooy9qkcsZ9ufYiS6w1Dyo2+yTR0iE5xSwLXMGmqlUlz6qxjemnU/\nQQBus0V2ApkavhTGvhX/dtHrNuCoUJ/A2SRd967w43DUlUifHAqAYNNHtStK\nONcd\r\n=iuH5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDWMcQZO9UP/seqm+dvrI3ucl5vSFtsdOfGkehBO067MwIgdS7hD2WMVboroBtRXkzmy62sEZMFSBNi254ieP0KQkY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190225074725_1551081034437_0.21805880793435284"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190225081105":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190225081105","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"56310b32e00a524e1609fa673657c7ed0b43e089","_id":"@atomist/sdm@1.3.0-goal-signing.20190225081105","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-x5wbRQd+5QzZEiEIxtlc7igB5C06XEN1RRqUK53jRw/kyfC7C59Nlurk4EEQABiC0dft/RaE/mVXaFzuufJGnA==","shasum":"fb38c2d0d6ff35c3d4c21b0a3e1beb5e0830ffe0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190225081105.tgz","fileCount":738,"unpackedSize":3066824,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc6PVCRA9TVsSAnZWagAAlUgP/R1TDU5mkngLvMkh5/jk\ny6hnRyvIWMY97nE0AG+o3r471i5sZCZokslmJgAbe2cp5X5AUMHjobptFjz+\nOeu/JRCGHwRYyHAuoKDy/ijXObKmGZjnVm0/g51Qzovmi+XRzOQLXVB2/1VA\nQr89xUj7XJY1GgSrN1PsqPWnJHtFhRQCoxE79zpitFDPq+45ipy0eXectE6g\nPJ/184L/qgHLIZWveOBJ/ghUWBgkd488oQDpgV2Ym9An8J/XjMXKB8qxh8fs\nsNqg1gRIRVGJMKjTNBtAkx3P1Erv4q9kLo6WzOKPf5lCacKiXGHYyUtL8ru6\npFByEnPaGq+DZvj2VA4yshwcwXf0bDt9KxHewvQyPFraoSMkwx3fZrgt7Blg\nKgz3Gutfj9oOClF2QP26OpbRxO0Hb6VJSFinaY0Ny3MLDIW+VfpU8/MtZMNM\nj2b40MgYl2QSKbvXQ+LIfdU9t/Lm56HTO2M5Nfko3RkH4Z9TxYj2nTt3Aq8L\nqdsY8jR2LILKH60UoHuqHOLpVoH2ZO3uAE631RJGIZyGKxOFFcU2W1VlfZiX\nV0pmQ9YMlmrXtmn32+nw0YlatMqDxxTEdU141HAUMRaPZ7QmeaN+U1nnlwX4\n7ErXQ7Z07EZjiEuPNJLolsOGT1+IqA7GJIOkR7CpvSBNpgpHgjagTLRhIiIM\nD5Y6\r\n=TgP5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAdCnDEuEUKn3W4KgDnzfaUUsvQZYa5Ena5bUjNxB6x9AiEA9T+o1HOoV2GGHZV79pL1pKfudeTI8Ib0s3ZhHElymYY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190225081105_1551082452745_0.3623722052580578"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190225082334":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190225082334","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6b5f3fe592c8f96b9e66c4427f577450b57683b8","_id":"@atomist/sdm@1.3.0-goal-signing.20190225082334","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-t7OFhHzjW9SLncDpzBmr8AaMYEejx30HpV+hBkUe2D7RCfzzhpUmNjsiJmfKIxmkRHpdmyaW/82E+JKT3mrN+A==","shasum":"3ce82288fa0ded7bcd353ad301efb473a3ad9644","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190225082334.tgz","fileCount":738,"unpackedSize":3068279,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc6bKCRA9TVsSAnZWagAArEgQAIDTU+6EqCXo7YBnpRyB\nYcXKRfIldMU2vmUNFkrV60ulzIT5ULF7zyWNaU9vFdYOl1UcheviO05kgiLk\nf914WF7Lyshk38zeqyDhZQhkQSgXt37UA0Y5+q791SKezn6kuP2vxx+DhFgA\n5Yc46gn81CpwqAiVD7oINVms35MFZWQzuYXucU9blcritbL5ATEjwIasV5Xj\nQKLhoIxMYF/CM5QacycoGt8u0vr8DKSjN1XEPb4XGyCxOUv+o6CaqQmcgQ2/\noXVMvwzLJPp0TmYhdWbeuaLa+dOWBB1MtelThMEbdI+buLBOQyTMe4YJmnYL\ni9B1J1aFJ/e1topHgmu4PPoin2Nlx298PxXtfGw9ln8QnSa2KboiTUBjGZuR\nJk0aHzrpwFuyjS3nCwWxOYIrX8iaG/6hSgWbuIPMXLS4u2w7WjZAxS0K3KRv\nBSvJtiIzDUsjZ7IkwNbn4V7MwO0kbLs7SKv7IFapJPzwMCPkw3muI8w4AXYp\nXZ0TeSczQeIM3eOmyAcemmi2au05o1gbF1pQA0eoMMLYC3HiErRbEKovvqzV\nuiXlKQsJ0aGwqL6yuidyAatNd+16PPdhEm6xwymI2uVrgHi8Mu62vWb/oPHW\n+2kOMc6HI2CKy3wDKWAbens5KDe6/Ac65CqE3k5cJex6TcOWUjl4mY/GuKZQ\nePkg\r\n=N6Jb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC4tG4obaabNycBA2Nigi/lzQJs1RZ4FPhMmDb3yKqYqgIhAMtpVYXveYQMb3hS5qeMkkaw6fIIZfHpX+Bw93EGqaDX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190225082334_1551083209629_0.48977769624065415"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190225083806":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190225083806","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3514dce31e40a0d27dcf17c5140232de097ddd87","_id":"@atomist/sdm@1.3.0-goal-signing.20190225083806","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-yYZpa+Uqb65EagVaTUrwM+P6bwtIntaPjIqiwYOUHjnpWRYLULodX/X8IP2UVLKYjs5L3r3fApaqL10s/5QTXQ==","shasum":"6e5194a0034f994845e6b3eb71abc80421cc768c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190225083806.tgz","fileCount":738,"unpackedSize":3068237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc6pBCRA9TVsSAnZWagAAuk0P/17rQgYNzP7fnWIJ9Uq6\n1pvi/W/i/19wiN920L4t/wpJUbeC4P+Dlzaqe01nbPAYGJ7pDjRq3pxoycwe\nAFgSfZIYe/9rXhJxi/Kyk34kkMH6JqFQlQsrjlXUf94mJdhNMqUYQBwGTbA+\nVTSCRxXGLR99JSgTQMhy1Pe138Q5514/pcqAjvTwCC/9BstCAbAXmMVgf1In\ndwgdYKUCCYeYjZq3os1f6nTifUa64JFwVJTLlHav9IsLj1EUC1qPGhjTuUsd\naB/0iE7/7jPbEK37Mw2WS03b6HovUVaV1QiQDc9ncMCTTmMf5w778zjKnahG\nud6SFPFWeylILS7FYkQy02lIKeE61ixHGTO5cXVBDAY7uSLVf+U9NUJCoYh6\nPzA+66jVEtt0Tjx98SDnr7sp9isHTlBZuJ3nRdQs+vzv6XYZnnIV+9wWMdtj\nrTagKQOxcNJSHTFBv1Prd0KfqxFCyL/+d4e/0FdvF/JUD+TYkfIQxQHlOBWl\n+vXllQJqqh01jG001hATbsS+RpefXbZhiNtLArEW1rE/LC80+U2yMxfJE+FJ\nhaomlxwK8fOMe49KHsZHNichBw2Fs/prxefjgzTQWvDjq+s2vsblyT6CFNX9\nfZ+hNfIT4ulWSmqz5pt820UFsuiHYVuqrEf+c8fltSBr9rpj8F9W6Fe0r7Js\n5wgk\r\n=1Ssb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICPI6/lIAOQbov6L5x4Uzky0+lPxK/48PfrfSd+FRGk3AiEAkt2BI0GU9HKwoLENzdTJiJih33feUDB1M8vWFZjW8Hw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190225083806_1551084096346_0.7455947204694546"},"_hasShrinkwrap":false},"1.3.0-goal-signing.20190225114107":{"name":"@atomist/sdm","version":"1.3.0-goal-signing.20190225114107","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e38d40bd39600b5f43a711369d788aa3f33b8237","_id":"@atomist/sdm@1.3.0-goal-signing.20190225114107","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+2uQbySbbojAX4AXGWmTSs8dQ0RmnCTCH6TIRzrbc+/i2mYnLHLR2LFMsOveFpiaYBKE2RtYYiKIn1B3Dhc5Iw==","shasum":"990956f7eb9dbf537c91febb580a9243081c14fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-goal-signing.20190225114107.tgz","fileCount":738,"unpackedSize":3068237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc9UdCRA9TVsSAnZWagAAN6YP/ixGXsfLhNdOTgGt/m+I\nlXcrr8myT39pignvpqoyTVQX9h+GA7q6WaAdfBXyO3HsIVvAcIQoCgQ2/xdV\ner0qqybPywGsg4DeUKRT4Kq5pDpkEhAfSDEGvcwbNEgjUJDWtcSeQ5HEQ8qc\nahIeJsAMjsYoZFjvAZbzMhmjVaFTVZcYRxiLrJ6pZA3ouZknxti71Jl92crC\nYKFMLe9N4d8Pe4/S0BlQEgXh3itdiL39i4OJPPWc4rc/+DIap2JjmFiKTJcf\nv3DX/qRhBohBGbRxur9ek8GjHXyxYhHRu/QmP+i30BdJHOJ91d2wrLQVlXLF\nfzPv6BG9IptqxBA6lCRvl9gGIdD2WNQ+yLta9LjhOzpruh6PbMc3m5tUWJdv\nhMhQnkoMFRER3iYUDQZb++g511/rltAtyVRl8taFUFeLNH+EXlnaikqOlUpg\nL7swfHkVYNLovO7Aazy1P7fCvXUeI/j/nC5UQ6/A8fTRuox0QM5RHbJ68gZx\ncz/J35OaYn4jlU7G44EXYhMuRhF53EWYD4OVx8RrWR2KnKs+2gjBLSkWhrSl\n0yms8Ayh0hPI7biorp6pnDmzTM8ztTdbjwkP0u24NV+eVv32lOET6BwI5Zi3\nWCItuHVGC9L34fgUecaJVMRR+9jkPEjIyWmD9ZkxQX3BWpyx/gJ0ghSip21C\nKBIN\r\n=JMXa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8rPbATo5Lp059J1QY2950gH/bn1aWesySYftws36VIQIgMmdJBTJkfuw2O3VIgUWLvXESiCZDnrIPg0z1NpwPKSc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-goal-signing.20190225114107_1551095068514_0.8526564827378198"},"_hasShrinkwrap":false},"1.3.0-master.20190225134151":{"name":"@atomist/sdm","version":"1.3.0-master.20190225134151","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"39d88c077215b2f8afdeccbde26ea7b11c8d04b9","_id":"@atomist/sdm@1.3.0-master.20190225134151","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Vj2jYkOMLj5Btnqc+TVpy+BvtrmAZiyHVU+b57wBX9KafoJqw23RD5rm68UarSotCbT+wK55b6/20WeLjEJJdQ==","shasum":"97fea9b5082ed8950d09b7d0d3c25c47f9dce056","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190225134151.tgz","fileCount":738,"unpackedSize":3068322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc/HHCRA9TVsSAnZWagAAdI0P/iWSJV6cd5RwDp5JrvfF\nliiu/7+T7jOanPG49S5qPBy07MdTqq4+soWgIp/oWQtIkeeGd8h+Y+FpOTyt\nMjp7aUpgDEj9FCphSkG+/4HKDg093QsOyYqzWaAZxBmd77CEfPpjnnxElLwk\nRIsZlbL6HOfdgc59tJYR3vf1ftZ5966SDAcyMaudsxLSOkszIwIshJHPgDSP\ntqR41G/rAilocUNWQK4fBvHIhbgLkkXt5cBcgPCLLGIQ+W6cgMrb7B4YhBNR\noyT4zVo3YPcopqS3Irw9nojMvizmGXWVi2Xa1ynskGxQG1thZ55WndCf8hjZ\nAMGo5mIhYhDRtKI04UWwbUXGGpWiZDQFahCAXsPrHnrgcCGngjATscNAnhAI\nsh8axa4YnPsnNom2TNnK+qcxKKOwIyjIwscSdcuQl7xyk6Qw7xVrsOchpjkH\nzrw7RJO/RzKtBTyIq200h34aKVfMEw+yFI/almD6h7SimaaCEWE90XvRx3WS\ngMJ1Y3BPm0GW/9Px91mKEsF9J3akVPfgv6+m1fV/e5ohElY116YBC0em+csn\nKWPSJCeogV/zjPNEW/LEEVKmZecLUhphMk1dwPqZOqMKakTFDydh6XOkhOHB\nUNtF6w7gC8WFJ9HcgzaIz74NPOU5nyMDjToAjP0vqlSnwATUW8n4ycThtfKh\nJSxi\r\n=nuSQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICuONaZ7uvy+o7SgRTu3Ok9K/vYDuEDWrGYupeKlROQ+AiAnrrxp81zsgW4PurMfWM6NZcqR3xejlGqE5S+2UwIBAA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190225134151_1551102406880_0.3088229773952751"},"_hasShrinkwrap":false},"1.3.0-master.20190225134646":{"name":"@atomist/sdm","version":"1.3.0-master.20190225134646","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"39d88c077215b2f8afdeccbde26ea7b11c8d04b9","_id":"@atomist/sdm@1.3.0-master.20190225134646","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-miIsTskZDtRJW1QW05SqapWkxNwMhsmQMfnlNIR6v28d2n8wp9G/JN7rilWFHKrC4AibthiHni92MZMUl4VrmA==","shasum":"661d29460374513dcb6fbd3629e6bd3948c645f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190225134646.tgz","fileCount":738,"unpackedSize":3068322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcc/KXCRA9TVsSAnZWagAA3IEP/26tDXptSpKvJb6uaFPz\n5Y7VERn3PNJwL87/UVJf9HejuhL4aSEL3Gjvpn9cY9D4gBsJh59kIKpk2elq\n0HpHmI96hDgENIoObyMFszbcsOJVk7dJwEKpZ3FNyjvSVXwRLO99ENiNDCTa\nKXxbEO37gFUlbVtS5x9AB9plztdqjEpvorqetRboQDiiTr4wjqlUAJdLVYI4\n8Mpxi0XwcTc4zQprtyYQ8cwEgGW2uSDtc7gS0kwQLMIVr0EgRc6YcWverqDC\nF+aCk5fDeseB+gFeLwS+pf9bqURmMUZ4FS7vlcDiFTw7aJWFSzHmrNoEdOhB\ngr+XEwNRU0MltTr6y4rEHhND1T93TGqZcqC2N6lXG7JT78zy5f6MWQeu5Whc\nG8VxGT17/pV2DFDwBre2LtWPfIWBLwmi7vROkdiTLn9m9rOoEynIFF7Qtlrl\nHBztRwKytXTwCJlqKu4Y6YvgMhGaDBK98MbGF3HoMctX5REr8Il3Qx/Ro1Fi\nF8SZo6GohgJLy80b9ajEiZ4BkfMyzQt0gj++HeEvIBPHBNcFjyudXYc34VQ1\noFTFzjzCYfw5EqaVDnqcvwJB+bcQDWGTqXazgKCyaUjLv5e+8L5Pbp5XduFv\noeOs1mtBO9anrYF99sSw25x9eWmmrD8557URubxlhnxTAbcZeaQrihXIM36g\nkXLe\r\n=sg1K\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvUTmDIQ+/ntc818g7VDCtIXtlyk3sySvXkTgMoWbp3gIgTMSOO4NeEMtKNi+Y922zdlUkArkDWvCxEMYTMNV01uc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190225134646_1551102614846_0.4879785770260374"},"_hasShrinkwrap":false},"1.3.0-master.20190226083351":{"name":"@atomist/sdm","version":"1.3.0-master.20190226083351","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1ae5b9ef5720a5bd0041353169e639bea3df6c3d","_id":"@atomist/sdm@1.3.0-master.20190226083351","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MLzfmlTmV/IGdL8b+rDkPhqfCimV9xyChTkPzVMhpnLf6E3YIfEf2n5fzlwjCCY+cHUsVVXVZqURjw2F3X8esA==","shasum":"a45ec0460f1e4ffbef8febd5ae2db7f69b8dc1f5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190226083351.tgz","fileCount":738,"unpackedSize":3069760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdPrKCRA9TVsSAnZWagAAq7wQAIipnx1WP17gzASuHFMw\nvOo8pzHFrVrnO0sjhInfSO/aFDbxuVpRooW/WEx5bHTxd04R7dBSOhKXOZEo\n/cvgXJ8USZiPB7Gwm9odNvRE/v056edeHhc+uPPI917LeenUYkELRJIjMbOB\nqtImomyHRaiFFyXHLjSxDsk9h9F41dRJdKTvOhZrl0BZpcg6QFj6VqaBt7Ha\nzoNr37krKHeXu8pSGnNiHurGD2DHs6UFwBGaaudPEOpaOd02cybIkqebatVI\nTSLJS94x8bfghpuztnMteWdw8jyQPAxKovn6i9lwU4pVqkiP1No5/7vzG1Xe\n+x+soMVwf/YpnZdV5SGWfrEuScLTT5O9cluoP0F9lTogjVbMYBqV1SupXLpN\nwEjkxK3FPTZ3i5r3xI0cGiJ7XCAzj5ltUQhEVk3uJjpJv4shrWRjy/t1R6oG\ne60dM7+62juDh3JHrESCVZDa/aaK5C8XBEHDtdkEObgKTByFZ8URLU46Gc/G\nTcezUY7vsrljTtsLcES5IPWxVCCWT1FlUNMYL6ALJgXF/985YkiQA/ZHLmFW\nmsjHkwf5zN3HxbVRaeA3doUj6Zfw04RN13VA/FD1QXVOdIe9WMeWv77OUOBb\n5erZJt4w9HZcIuRl8ymIoZt9jxKsbJEm7rRPLDAN3bcqAoCXPefmFBGjOSzs\necuN\r\n=j2Gn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA3TlM4jbKk0fVkUnSv5W9h082s/hb6McW1309+08op3AiA7XvMGXqkd0I8tA1bfp/CjGqdEMmzPW3I2ZqXL2KKo8A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190226083351_1551170249478_0.6474410852868222"},"_hasShrinkwrap":false},"1.3.0-master.20190226090409":{"name":"@atomist/sdm","version":"1.3.0-master.20190226090409","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e9e74b05c46730cb1d11ee116ac2c27f767684fd","_id":"@atomist/sdm@1.3.0-master.20190226090409","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-Uh9gIKot4GpRE3tltIbmXWVa9HlpW5VH3ccvZHYezWcc8BGmGgMvJgWFzq4jjFJtuaEDFtWLzMvzSIDhtxAjww==","shasum":"5cee1d48639fd3a2e6cbd0a5f54677077e9a3a1b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190226090409.tgz","fileCount":738,"unpackedSize":3069762,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdQHbCRA9TVsSAnZWagAAOoEP/R57qqa2MrCg6NJuU5J3\nU2beJ19tXaUS9vNM22vhYKaGj+0oFmi3iUcClcz73Ki3sB72Uo8alG/Yv7Rd\ngWmp/ZNJ6judZZtfTBus+oH9T9tI5aFuCqm3MEP4DVw6+V+WkQg1pZS+YzhM\nMxh+5NQ0iWzfcyf7Mbj1uB3FlocVQbTVbMVGGgrtycw27GWJ5cP4VEPgkgkK\necT1SLhDdvj5CxWSX5xEX9Fk8IPaG+rHjXGlSPgKMGP7cd7QuGsYfWG5nLaz\n+cfYJVi54+PHJcOdYIYixxOE3aoOihiLi5nPy92kHNUmaLHYIZlXzfReV/kz\nSpe0EoYaNSkrDkkZIhKn1x0Yk8g9H6LyA8OyHvpk24S59juVnT1Dygb0cB3O\nHRjPOumsa872vYBj6nFG23L4KuVJEPtWE/Fz4Hr6JE9G7NtXM9v3uLkNHApm\n/FWsHSVytalhu6KuuPr0pFyMFtJN5km01maE2Cl9MvGrt6XOCaxerI1kIXv+\nAAHHu96yhbqGC8YU9lRGMnTEgBXCIJPOwYMKxQv2p1WgjWAB79gUNzoRGewR\ncAhQw9CUbi86QlC9OaNL5dSHvrMR7MueUpHtrqXqPMAE2POvh8Kgg2v9taQT\nNArLAiqFvCj1hp1M2wLcJSoHxDuZU0BDgy7T7AEWkyGdXks2jfcs77RjwmgL\n94gK\r\n=Hvsj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHdYN+z7n815ErNr0PGnsPfZZSV4Er0P394xIjqRYmkUAiEA2XttsNqI1rO1ZYLBVRd9wOfFFbfbF1eJcQk0k41JHps="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190226090409_1551172059331_0.0952014414047393"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190226180726":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190226180726","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9993c1ea54c99ad062010a3d148b721e4f7bc4b1","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190226180726","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-u8EGGvyiVjn8+0GR71osjA/9I6e80bt2GI7va2tTpcDzT9scQcvtqiq1LWWzI81gqUBCS2jtzemsHOS7w4KCQQ==","shasum":"d885b5a3f3fd575dee25a27f95aafb734a0a0860","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190226180726.tgz","fileCount":741,"unpackedSize":3070859,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdYEaCRA9TVsSAnZWagAA/mEP+gKid2XrQw8T6fjbxrmH\ny8wMWIVT7LCiTb0bm0teEASPp8V1Tg9D+gQnkMtchEZdkEDXEEjdu7uSNfjs\nMFPQDU2WKRf4igAzvySUZXAvYLIGyCSXFDSdjykkCaSHKJDnRZAFegGTdCaZ\nvS1woDiKRq6OHNpCAptACPLnaX9OX7Y+9mGrD3MLnLFFsVxUQQdT++HHkTPU\nb7BOvlG+uVB5PIPJiVABHj6PmSxjvPMdBsHn4I42i0FYvLO3eqYvyWR7JcK9\nt/UFtMJWWP7PKTba0DCiSPSaXhWJjeHx7PMBhUXZXkdmS6GEbTQ6RxC8fjHl\nvHn4Hn1a7DxZMJvCjnTFmyeVWhpbyjwKGaFX7bQ5pBYFMm9Ypk1/7WGyYy5Z\nxcCdDZXg+F2aeWFLZWkOYJ9vIB8tFXS2c9xz1VjrrPGCBQCU27IwqRhNwL1f\n1+EmdF+UmPyZoeTk2uFzjab1IUEgniNG38aHxl+y3tcIU8EomqxEu2OIyD+F\n/IIjq3wn/lz5X3/GxLH3CtN2yK3eXUPcn43AZ8cxw3tjmjHx5cEAx6wXTIPk\n6zNvVD3m2Uimqp3jpCvRwcQPUUEv07Huf7XDa6+UpM9plt2hp393inV478s6\nM0Y+DojXXAuf7r1AqqT21eV0DMXNFETOjqw4Gz6uczarYHa5vdMDdQgi+9Nj\nBoI3\r\n=hDHo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNa31ZQuIlgd2sz3YFr/n2g8sePocyBI387VP+4gtYZgIhAIlisgc4kfz8Vl1yScV4ysJZE6pRbYgJyPmuIq4JU6PE"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190226180726_1551204633753_0.2910574108405388"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190226182659":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190226182659","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b472283e02365bd761f0a47d7de4ad4901f9d060","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190226182659","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zNca2WjueRcGCCI2NsPaxg6abWKg/cltIQtufDJEpko/Mx+KH0iBR1tH803nVq4OfUs6yXgnhPlyUoV49eY9zg==","shasum":"e9423725f1c4e18460a9e6b14676d89d55ba19c1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190226182659.tgz","fileCount":741,"unpackedSize":3070887,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdYWsCRA9TVsSAnZWagAAB94P/2jl4pV8wXqYH3pqU+V7\noaENnvyZVYlUloa0biKHeQFaRF5K6MZUOeLk+osJACy2QAK8kvKj33D+Tdmi\nF0ZO1QcScXW0QUFsCijFM79ERheLfC5lt7Pw87zxuHFcVEDe3Saz0jFjdRDE\np9casegW61+233BDrCOuy+jgDCkYj8bxjxsztnQr4MNzNOXNLaegmlCtezLM\n3pMv+WXONCoel1vjrZARemlEbv2o8J6E7A3i7f2tQJEKxOC1sFk5Va+eHBux\n6lhiIWRKvv1wCuYbfgdTL97AnL6BrmSHprmFZMyIPkDxo9bTcqfD0ghLi+qf\n0GXMoaWskSgaGED/CJf7la8L/OkPzzCCt7D7LjunheK7LzX+5vMdu8tRhE8p\n6mOiMFCWhOXn99f2goyxztgMw+dQTiqCsh1m8kiXiFqF7LKkMIeRrMT9gWaI\nfUhfUnuqIMPC3yR02LqwlHTfrciRXMtK7NJUc99ak3wiG1d18X2MsK41i5Jo\ncmas1G3YoilaGzdvJUbDm2cI9hYE3nvRQhHzPk6ebA/48kv7zmzgPNpuXFhq\nj1C4aCj26ZKdEppGE2MxRejF7Ya2w3gr+WxnTTD1drd5LLvnRAeloC3F+7OH\n4bLKsyDAlaNMnk+lf+a7ZH2fr+/BNHP2u0ZX03hjQdVB9rshOP/mhghmo07T\nuPaZ\r\n=nEcT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID3WTMVlGga/HEJfHEnvRqxLEdYZFlOMjNyIFU4LgkRKAiEAmwfzOnLevrH1N7d5agbEuKSPmkRdVz6ZfgOZafryuXU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190226182659_1551205804255_0.4626362219621174"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190226201908":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190226201908","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6ebe1c88a87d0c6725834837ca1380bf9e86274c","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190226201908","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-q3qXkWD/m1ZI3P86uXtyyAgo2YnkZhbJzm8WSCM1vpKSyqoSXPCKtNEGOqrzg1sWisB81TsNAO4B5cvp1Dz4/A==","shasum":"648fbfc6321636fec33e292d2384f95b29412637","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190226201908.tgz","fileCount":741,"unpackedSize":3070911,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdaANCRA9TVsSAnZWagAAOTAP/1hOTEBr1ni/GHn3Xn8Z\nJx0kHLLnt/Z7toqyHaQYkwFRgMNyx5JUAo62JimaCV2PZQvtk1hFsgHJe5RJ\nX4FPr7zehnyE4ZORP8M+uaBV1ub+7NQmMQnzatzId6n6okHF0xb5tg9FbGuU\nzmhjSHltkYZ+iZVZPe+UO8WYtuL/+v6H1pth5va7+06oRprUkOoEO/yXz10+\nXXq4U/zneOfBoViTqd+VbZq4KV/RTlAAs/yzwdW8hF1hTL1IHeH/tgaJ/UT4\na2Ulp8tOYiDcT1V8ve1Cjj9F80WX2vN5IUZVBhGfxWTawsflcMCl+E8dN9BP\nbkO695OSmeet6eZKLkeRHKK6ZIli2TpiDFy4tqTpEKK6BJ9sEBpfbrDZ7sKI\nGsIgKCdxfFzA3+XnRuv1k92X8YI2q4VTX1pwhsZgYN+pIvEU01fhSMc7b6zD\nZDnKpOucoAQ13bFNz57Hat3zJMf+U+o0mOQ4Y70cS9zb2lNCx0zrqfxab+d0\n95xrfRZbXhWAbH/SJSkZCLFgTSnM0vnU3huJ234PVs9c8kn9dXCoUMCkap/p\nDgbyrylVmJ6bQxNos+ICobHenA3G/BAJxYmkgD2X+q+h693QZgpGNGQV2d05\n2+0jLJ8vhqTUqcGcEtm3xOAZML5tcNK9U3v7GT3MZ77phAFvsBTNkD5i/Bot\nbg7f\r\n=acTt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBQ1c1L3fbAvccc4sawciU1Uz+uAJPSq2EIafk0FmKNNAiEAhXu3E6/nnDASdj0WD/bpqWSTdBds+irIDzFMGpbRTEk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190226201908_1551212556782_0.7604215402178078"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190226220012":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190226220012","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0556cf7077ac95c42fa05f84e7af705cfb86aec7","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190226220012","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3M/fdUhCMDB31wraSBtKrZ8nFLMKrvjVUTt6Upmpwrs6fjWtl5U6q23lcooPovZWcBPYA9cOdV1+ALJ1kO3M4w==","shasum":"105028b9d42b2d435fe1445b6471ff66cf0eef3a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190226220012.tgz","fileCount":741,"unpackedSize":3071818,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdbeeCRA9TVsSAnZWagAAAQUP+QGmC4+iuDiFpNt3A7Tj\nnsC2CH4u3nM2ur/9tu4eRabSBuwqTDN34lJvMH2NjJLprxbtFC4D9xPfgJ4X\nO3d9Jb/yxzxLCYcAgjTjytPpE3pt1EsNQm4oIaIYz5+cS33qvRWHENGFMuRC\nqMwyRPtjkvuwPcrfAWHDtfNItLnDcpUir6/nahdsluCsjwYr8TcPTxCyWqLp\nRy1HsQsrp6Nvj2w2bgzgPl4y++wPVy2VtAx1YwxrofXFsQ/T4loz8oVUM8eF\nTZjKQG/eGUraD7Cgi12FCINHxYeqmf6UgYq34XU5aUf7FTv27wrxTRP5RB96\n22jwPOVujH0K3/uHNIX9RGVjjFVfh62m84o4qqa0dHVEBNxCcF/UR8V3rJkb\ngWGQQaJc/VoarVvFQHvWU4DfVOwldrQgNQIdLBJoeCR0RPb5LXHNrxHv4wuS\nE/mRFLk+GKSHP5exLsvy5AnIpN3ta5XT0rznm4mabYAxpNOx038ATjQPSsLR\nOkdZ6GrEJo2bGLSFc7EXcNmnUwHD/Qb+smveZb9d+y0LVrWPD0GbJ+z6x9eF\nekYdAh0cuTAxQOMXt5UfWGq/VDaHNGGiCgJD2WXf5mzkAeqtQhVlIgr6E7Nn\nq40Oy8opvDlXVORH71n43jKRupi2YJBgL/Gi8wn+5Zd6tIZGp4FQl4ixSXBv\nWmIC\r\n=xD5/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQx50NtJ3hbdPoNUr8p4PEhdOczSzmvatD2qeZzd10sAIhAIKnhNRb+z3h03WPWg/+5oYd8NidCZ+fbKm4nl5H1qij"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190226220012_1551218589665_0.7452452414552859"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190226220907":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190226220907","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9e2109ac090894db288d1001252c8fb378206dd0","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190226220907","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-wA3qQ+4pjOkAx+iK5MVu9iyO5kNroKuZc0xgJ81VkKlHQi20jycqIqorWPc1DBJC3+JHtrij+TKDXHe2Q7eR8Q==","shasum":"462aed2bceb2781e9a2be1a67ce5827d4da7210d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190226220907.tgz","fileCount":741,"unpackedSize":3071700,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdbnFCRA9TVsSAnZWagAA1UIQAJMRLf2eAULOG8WGAGcN\nZwywi1iiIyYBDLRPVVNts0yjfifSclVtVmmi6M5evXD6TZRZigoYP0su/UBe\nO/952qnN0ilH8ltwjiU4RWQmuHSugU7pDlpQDSOwqGTRgbxeB4tN7MdX6vjg\n7l2rkMBbX8wFRLoG+tX/g5jCH3LndnKHPTxo+D+srQhoBQ9PgoGEOJg66OKS\nKiuQQMe7pBjFq7kwIepaHhF7dABnKuSb5zyZEkpT7vYHQFiUlPAKHGr9mzti\nssN//U7HIh153UbFLvcmex+GCUI/mPt8SV0RAfwKU5hP7mgOE469Oa9z+tfO\nuHV6hoStpEYPxHfuw9vZTf5He/BygTCHMy3Rt2ashx+cctfMdxF7mV7zSYY4\n2ybBjgivMEOR9+vJTD5t01AbFJSZNI+/CLCW7ChueQ4boq4nnXeMJGa+m1QS\nRr3X5WluAAjJsK/eUvmJCK9wdLhjWp8MzAH+nWdU2bJrG4tAljUGUtE0kJV5\nF0UFYVMPB6BlffIICmP3I4oyMs3L4mViZiDQ7Rq6a7UQDfikvT+gV4J2Z6Hc\nHkh9WbPrRfLSSZmP7rZRermozF4TkzZpRM0+0TRfmtJqvMNqTzHoMGJ6mccw\ndZvY94hS9a91T/jbQeDZP/V18IEViRdlPMg8h0X1Gt2h31u8bWSlDZbryMB0\n5i8K\r\n=P3m6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHBIDf+kPcXMIGXETjgDTdAhyORLZo3ni/pPuEq/rl5CAiBqM9dHrItZlsgsirxzBjyZheEcsWo2+PEvQYFNLCrB1w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190226220907_1551219140652_0.7131980417696611"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190227162629":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190227162629","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f42f47660a4091ee72d7abdbcd1d475f904cdb30","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190227162629","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-qNN4uWHbwIfjHtB7GkecGCpigZhOyPnV2Q963p+71/4AY2aJkFyjbjAQPn4FI0/wHZiJ8JP75pIIC1A04rGsjQ==","shasum":"f5b9bab74a0399eb41ce9d93c1e7788f28e8fd68","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190227162629.tgz","fileCount":741,"unpackedSize":3072255,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdrskCRA9TVsSAnZWagAA+1wP/3NFXb2QkB3T/lvEeywQ\nzDurB7PI76x5+BV+URcYVZL8hj08BXK4CXzGcvACm+lkvEg4SJJ/vCW0EsKX\nOfCLCmFEem+74IoEHGmVXScRn2rd/p59rCnNGyuqnDWv/muERaXNtTeTJ1pT\nPNpthEpDTGFHqKuGcV4DDBWDrmBaDaotYM1TyIkMGCcl1gURbAYYuhh13Crb\nowOUMfjO0kOEPla8zg427ByQBfv0Dc3+PaEOsHodcf6tdiQul2MmNl4O+qrg\nTAw2skdhFPKWVOqEFqG8Pj6zxpqrtVK+S+YBPSvFG8+nkmxvHivkOdF8diN2\nkHVasuv4SQfKNfP66l+2+hMgZ2u9BkY/lCVDxbnFKDbcFmyQ+Z46yHP2eXfx\nqXvbJR5fyfgh6VclF+/N9K3I7ARJ7yurAMHuVlCbHQw1G/m2iuME3kh+5mOB\ns9ca0aRJGnd2hWFbI8AP8xaWpaTzGWjMW/gOtCcldkQ6v58hdH6CzJ48NL3v\n7IDiWmFVom2yLb1VyB7Eq4njkMymezTMJgco1+JR0YsdZDUV6vWOf9C3YhLj\nyrQhkBixySnvhFRwq1eCIRmsYnpsehAnCLW03GD2MaG5VjFcDtHVaEIFpTnX\nDHdXHAmN4mN7yp20+UiWvhU6TJTb3ByrwMynKGeYvHwUEOWEaeBPP72g9Tzc\nP+G2\r\n=CHUC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEA2G3ocpMs3KYIQUoe1UMgaHERkX3vwiJqwlnX6CZWzAiEA4hCoqImxWPCu9zUj9b2QKoGDCb7Wy+v32GErv7jgIbk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190227162629_1551285028070_0.2274199622433497"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190227202820":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190227202820","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b5d2c8cec87a6282aa21bbcfcaed0286011f762e","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190227202820","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-sS8F3cEho4LtpmoeLgPsLftcriXJgb8eLYrgJhjqCUiRkvxQLDxPvUjRRMd5Jc0avhtUIQfzHKFzDc93wVOhjQ==","shasum":"a611aaadc9efd303403746ce005083dc3e01a602","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190227202820.tgz","fileCount":738,"unpackedSize":3070947,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdvOiCRA9TVsSAnZWagAAP6IP/03ZCiIFwgVf0UzMcF5M\nHeWOhwWrEw6syxao0FZmmED4q0diUwI5ohqmvpv+6Ad4aHHhwLIdxO+C+BXm\nlyWODE9VFua9Keld5gsx2Ywj5cTfwJmruBOatV9LlhhGgNozoAviG9cT8Oij\n3YqSSDqTm3IXDncY34d72SqGjBrwzEzUHGOzjoTVXmKEXdWaxiD+s9j2rzjq\nSdSqra25soZr0xWxZykeR4wxGcdRuNbssX/wRWdR+fCQmuqrf5xHrAHtSS6Q\nwS1S8mSEE5Fh65qB0YhqTTsuDDI3Pg9jOiAOKZyBfa3EyKRoENq1a6K1KGg3\nsXiP9/TLvSD95gX/j+nb4XfUwH54ChK36FhfCRnrnGyphRldbBfPg5HEkVXD\nt2d9mgAmZCH3SVDbtGkxpv6cEdUTod4qsrfXIoY939vUW8XljVNOI3QKmpax\nQIR+P179S/G543C7bwo9+chitTsR6jVRtiVFf9VEbENXGumpFJjIPaipboNm\n9ts6ehOsDj8nty2XTdOlDzFNaHh0vrfVsU67CH6FwMh77NzG5lngV4nSWQ3O\n2bSlFNUEGaqZ3n6taOe0eTzRS7U2qznk9cruaF2CHUw/foVaJem6d+ErXZ/k\nOEjoCZ45WQH32sXV+a6S+yZuaoprW8uhO3SX2NflsD/KEtyIriJ3nEgkQZ60\nMy0H\r\n=QLbt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICs4w/PSIxMnAVwYUxTRyNvTXoEmgkdauXZNYZywPdjjAiEAvLN8ciPWrafhws1qcpzFGYw1uB63JJmZC9+QXtW6e04="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190227202820_1551299489558_0.9265663402805604"},"_hasShrinkwrap":false},"1.3.0-nortissej.addOptions.20190227210207":{"name":"@atomist/sdm","version":"1.3.0-nortissej.addOptions.20190227210207","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dbf818476ff772576058574dc2c3e67b1209116f","_id":"@atomist/sdm@1.3.0-nortissej.addOptions.20190227210207","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-IDNJ1yKlkRSWBm1Xn1O9g/CwFfYBgI+Iw1w2d5GW5w4i4JUdYYyVn4AJtCRpq7p0kkrLJrkTYM3mSTq6gekJoQ==","shasum":"f1a3a14a27310fab20ad0eb04581ac9d6069168a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.addOptions.20190227210207.tgz","fileCount":738,"unpackedSize":3070949,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdvuVCRA9TVsSAnZWagAAqO4QAIwmrHGK6ndhj1WFwppz\nBzvz6l/C7Ql90YyLvzUi0je83xIN9gTM+nTfbiOoc1OnctJqvPU4FSfw5CoO\nU+iO8m6C9H5RUeoUAc0/Ks/COTEjqlHo06ZFfSv3dNeTczD4IDv4OMPTLZWe\n2zgGza4HweTfUxB7OYlCfAk8vLFvq41nmm4TVHWLwIP+DaqOXYU6QzqQPb8h\n8YdWdywn4KFs9ftSYDcsaiHyGLTCMhTpg9IFNsrQ3DzOan23N9WYQ/+L4JdI\nIZjL73wESE8AUhjGEEoSP46j+PjzYPLobGalgVOX3LxSNVblYNqeFnlbhGj/\n3Gp5d7h8iApEPxRW7wsGaUYvZ0/aPL9RO279r7K9cioEAtqQSXUHNxEtKInO\nEfmjzCYSpuVn8mtgpUmtDMCxwWL3CeHP8un7JGdd5zz8M6Cib82FBA5/K0s/\nzJfirRfAfPDHah826ofSzUVV0F6KxsIG0ZRl68L389d0YARAHuJAmy2QwoJH\nLALVFmFCWVgcTI+RQyqtCeBDKOIQGRCvdFdAxGKNOLTX5xY9cSNCzs7zwwmm\n2DFmDkD8E3Lbyx5XdNFn+oF2PpWNGh3uNpE7MO3F3cjzheKrbNCmB/GW26ux\n6rtwmwciSMLjWmRDH558N0opqQP6aNVls+J6kxxAuTtyJSzc8CVJyHqOkxGl\nTXI2\r\n=70xn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID0fD6b2boKZyvXL7DchQF5DWOIT3d0fkmCTJhR/eENrAiEA5vFY9dHvZwjauBHVxoh4Gznh+8VMNrC8yTHtkfjDgHY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.addOptions.20190227210207_1551301524604_0.6285162074125286"},"_hasShrinkwrap":false},"1.3.0-master.20190227211200":{"name":"@atomist/sdm","version":"1.3.0-master.20190227211200","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"71a5a745ab1ddf95d5db0efffb32258690c68663","_id":"@atomist/sdm@1.3.0-master.20190227211200","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-oklaPv4MNdOm/Kiwu5C/SzK0BGETnQUD4vFAZueHK3haIk9wrrS9GxTK923MDax56H8SNQOhX6yB6WTOX9ImHQ==","shasum":"6b1dd92889a1a13144e5cbc1151a7d7cc5c64a43","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190227211200.tgz","fileCount":738,"unpackedSize":3070935,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdv4+CRA9TVsSAnZWagAAz68QAIeeBPF2V7jwiZX32m1i\nY3gvTgLSK5wHbO3TorRpeKSPyH2lUkRDcpDt/EJQoE23tOEXQiOfJZnKdvHX\nh2bECfal/AXwvcm3jHCZrjIei5NXe6jbjqwn9XKieizS+FnsGzE9BZrdFpJO\n2vOgvIkymdec17LBJC9KvrPmzCgeRt6seh9X5vgn9v+spELo3lw6TgESOURF\nUgTD4eVl7jKlJ7SIHc8bALZY7F2VTSDr7g1gjhWZCggDZI7L5AxWAQMrZJ/V\n/MMgpFydsx6WhohdyNvt8FhEUX+8sZvF+H2LMuEIWvzOdRT6RnKmQ2bvVDE6\nVEbjIE2rkDWIXDUvRS7QQV4rZ0znac131xGN+L7cDx3sFh+sozS26cpq8ExV\nTiUg62pM04rR1jxt9M9S5QAqgRC4/NJi4UYREzbA9MWcIdpu3jJzHg7F/Ddy\ncNZYsJgzfmtiklVOvlT3kuQPvrpiA1ZTllk8edrSe8QwPfi9lsAFJsTmNH1g\n8ili6Lt3dNPaqbzySeFXxRpbkSaN33JTMT5dSF5xB08YtoGOh7jQFmN7olgr\ndNy87Idbkz572yUAAADzpjWZa2BcFhllLZ5y8gvWQSmb/5xb4rdq0D0H2RpF\nWLoA/SvKZ4b5pfNU6a0lEbSKMU1ZSvlwh4sPdLJfV/TB+7lLPNENamk7BO1a\nnv6l\r\n=C+LK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCWNG/uQl3wLGfnzBdOznGGqQn1i4BFAJh9Sb227WR8sQIgdn4ixoWrqbsUqxx3/tIjmfHVt6MzRa/ZiN1mddnq+9w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190227211200_1551302205569_0.8102341905908845"},"_hasShrinkwrap":false},"1.3.0-master.20190227213422":{"name":"@atomist/sdm","version":"1.3.0-master.20190227213422","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9b1d6b4b0c5575e8f0b50b98b34cc600ac412ab9","_id":"@atomist/sdm@1.3.0-master.20190227213422","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-zL1D/KY8LpixehWX/BXzT+nqAkZCdTR9T6YQZsNlH2UcwpQbSj8PQ1MtEQkayWEI5jcYx9kJuA95Godoaf29EQ==","shasum":"aae2d90f203998483d2ddbe8d020578e42c30e2d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190227213422.tgz","fileCount":738,"unpackedSize":3071249,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcdwNmCRA9TVsSAnZWagAARI4P+wWsaQfyfMds3bA6nh7p\nWoZW0q5PrQH+wM4T4sM7TKVl/+bf5y5BB1aBRIhcw8X2SPh3szHumR/CRQaZ\noA3ZVtRS2Ja8q/xksQI6ue64ktKj3fj5UY1fcfuaGEfbgh3JjHS3XUcqwqj2\nhuAkF+qZuU05DRCyjmZYUY659rUvGytmc1KFkXg/z3udXR3HrajQyNBIp7Sq\nKubqg13RW6DMSspf4hL45rQJxFrw4wy6B3paIoHToUZNJkGUBVqwSV2hZWGW\nARruL4okLiu3AJmUbRJRvYhrs8XyBt3BdkqwR9Et1/39bqg8rnHuCzOkQQ5U\nhG5wEP4tbwX72ElRUQ6lvCG4up3XGiR/E5sWV1/iIdHoTkpaJZNkCLlfm8x6\n9na4sEUyDnXhnxadintg91XIXc3gEguAmrXC3J2lI3hct/bFvxsPseM3TzsA\nC845ythB6SD3NNjXdJv+aKqCt3wngTmZhQ9Wdbfx9GSJULRvpxS6pmQzgYsl\nxy8fSatg3/rdQqM3iIT6yh4SX2PVxs1orHsR9sYmVIDNZ3LSinmz34nifyTk\n+jQ/hw4p172z2nQjWZkw4amACnhuDcaVxUPW6aCpV+JEkas+UGO8m8dXHyb+\nAAamULCqwlYRgXTynJNLoV1FREkJMYsdC+0ERQTeOmBp4wvz4rZYuii5CBVO\naCRw\r\n=1pgI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUh5pRjfHPRmXkONK6igE83NdtnfGLuYEEx8oeqI+Q7QIgJK1l0ZbclJVax2/7a4d6Owe5hoAWMp5SakWzKgjZfZs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190227213422_1551303525306_0.9100765256130401"},"_hasShrinkwrap":false},"1.3.0-issue-682.20190228124705":{"name":"@atomist/sdm","version":"1.3.0-issue-682.20190228124705","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0e7a1a2d2479ec7d3f3a87b69aaf6ed790723ba3","_id":"@atomist/sdm@1.3.0-issue-682.20190228124705","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ZHxY9NdFnZdt7UmkZ/Aim+XphT6aoka5K8RAD/JxnqRIWSLKbg0DgzYjuR9dEFT00UGYmS5dDV+JxZildqlicw==","shasum":"0d5f72c94cd4171c9fed45947287b56ca2a1be18","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-issue-682.20190228124705.tgz","fileCount":741,"unpackedSize":3078443,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd9keCRA9TVsSAnZWagAA0WEP/iSc8GJBdEWyIkTvOyE6\nKnIp8r5sOrzbVkwC22yZEhePMs784KruUtcolqvjREVdLLBAVNj822ioCwm5\ncNyzUfKNnWW1w5svInT25xDsngsHG6lSzd/4srvqwxWrBXxafOx0rPigtfsw\nmunfgXGuTwonjB6DCD+pm5m8G88ktEJ1AFcGpXAdH5gMPyAG8JvVupS/FeyG\nU0Ive6hvAl8KTCnxilRAjmYp8Q2OUvfQdD/vc53cz4C4xNKpQLlHtwOhL+hW\nMSFWbg6cT2P0ShYyJ/uiurp0HxkKS3SuIUjE8phMQ8aoI/74RbMGD6ek4WYD\nYp7iFRiGfY3v8p9UDOGyTeRcmD5JCRVeRPfeZ1rhwnQJiGhwetFTih5SAiLj\nlXvITIuAlr03jAUkIX61hJtKtC9UoYYlYGNwt1/BXZ3E3xJB/9RbMNtNnfRX\nbSyaEWKd9yE2Ns0mImT2OkSJ31wdimYSi7OWOEHBb5vgdQy2/AHGzz1F9cWK\n4ZCF7SG/3SW8ZrZ/NEm8YcgKjIot5f/FRAJzYibFTxiLGEW4KZAsP0FXQmPD\nn4AWTu2DCMxz7BTqimNW+5cqwvCYgklUekbAFT06Q77aCCWj0lz8fof/mwsS\nHezmMgvtoQqHHgc37bmq0sMgPuwLTZBNSpOIsouN1/ZbKM7o3wikLAEmNfEI\n0vJo\r\n=qnHG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIERPMo8Tq40MpAzSKPp42usdhxJZNRWm3pM6SosT6E1TAiEA95+gjXR2tCqEDgRS7ltYa8YWVBKJE/UaFzsp7M0d+ds="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-issue-682.20190228124705_1551358238121_0.8089848724395377"},"_hasShrinkwrap":false},"1.3.0-master.20190228135053":{"name":"@atomist/sdm","version":"1.3.0-master.20190228135053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9f528434b718412b4c8165eb893fb8212f64a177","_id":"@atomist/sdm@1.3.0-master.20190228135053","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-/NE+wEYCZFEGE8C1dSBHM1IIICXr8zy4Kb2Lc79+Fcj65cg+FHQ3f0nmlhSxbu1zea4EQ3ToflNQoNOMWgOREA==","shasum":"46cfd886740c3c47118c1b59dfcb08841df4beff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228135053.tgz","fileCount":741,"unpackedSize":3078589,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd+hACRA9TVsSAnZWagAA7DsP/01xN0K3Glt31PSvVi5V\n46X4xErj5VdcXI1R1Vt1RMNoyzdEJOsnLPxJ4/xjUu2XWx2EVJzdJjHeKG6l\nTPXe5adjvvOABiXNxYBPa5iVP6ZUh1oFXH/f7AUviM19U+V2/GQcGGI7290k\nWmbXeurv6l87WdYVYwD5Ew/OvEKR4tIKGIbMPeV0ufOSAAJkgsFZcP2Hk5o1\nCcKllgiwRHx5CdhUgyIxFj0WY/LlamOp0ikf7DUK3k3LSfmVU34onX/Ts2PQ\nkgrCa0ykVa72JiqjpVyVdGQl+T+hhgv+jrgrzPrrzhcEEy6QabKdGWl9CWSw\ni2XkIFYw7wscCdI5aS8K3J41ea/C9tkynEx3ZmTN7R+1Mg6VfwFAIG/OhIZ+\nj92Glg1DqGOf6P5wf9F36pS8AJfEN9xSEYzI8iO0OsxfonEtT8qgXsgbh3is\nFrRy5Fo2aytqypqlkqVq1/MoesptGlRHLjwIIUWZBRaKR1JMscc1ylILvteO\nORfl9KHJahdozuwyWoGOU2T4Q7dpufAiSQY1++tR2RXctaFDscQiAJiZDu/i\nm0mUdwU/bBDuTR+lp9L+ll+Y7vlfG5YrnfRDp74InNfp6tmNLQoDaCeLWiX+\nUZi0DprL97dDbh6pUB8OtNpcshrGQPotHwmfWBmH8pK7W5joblMCD6qjbh5y\nxdlA\r\n=k3fe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHuxwTKsq7D6DrDgLZuRi0N3UUVNGBO8g7VFU2JM+jDwAiBmHdDYrpv1tZE2ozevHAi2lvBphLKqmcoefG/tV7QAEg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228135053_1551362111672_0.5850440284854919"},"_hasShrinkwrap":false},"1.3.0-master.20190228135408":{"name":"@atomist/sdm","version":"1.3.0-master.20190228135408","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190214172210","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"25745ceda82aa18115cb10c104fe3b681ec19e41","_id":"@atomist/sdm@1.3.0-master.20190228135408","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-MADP+ePtrttNyo3BMarNB3UcvNCqDS7TcWmLFPWUa/X5qOsOshZNCzD00F3ZQ6oqGbzo2Y10B2QNEfBLFp6Qwg==","shasum":"fb996976962c90c6d63686c10b9044453d671855","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228135408.tgz","fileCount":741,"unpackedSize":3080701,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd+jDCRA9TVsSAnZWagAAn6QP/RFSxgFdE7uQUfUQMm7E\n1sOj8YE5u+GwG9S+bhnOKvX6CI4otFlhzoHaTJcOO+G1bAjRmdauVCSmRXHU\nse0CatfA8tX0/CxVWp4pW0ivFh5YQo1bKl+R/toFwo0+8sw30Fz+cNycvfa2\n62jLvbhJ1ET0wKC54/y2TaOTV6RzydPyGVev06rwFfx+IYezoKCehuYztwwN\nghynexaOMt/hKgEdLFL3MO1gnDHRb+9gaPWJMy4UQtwF7ZKBL51Qke7oN9OR\nN55R69r4NNNqDeQCNq7OS3XldGkGG+47DiLybbPOumilT9FlRZgt+wSbnyFi\nUb5n+yiBv+8cxP6Nh7yNQkITC6/oDjAWQPM+VLGeRt+AjzQdSNdhSMHn/ZMf\n4Z2XcIYE23CSGhO/wYn6dmwpGE1orlzG+hRuHLxSw/Xq4/B37G995idlJTc3\nxVmM1fvD6Ssg4SHC0t4WWuHnMZukap2aogA55i6ktMSYm5q9Dygh8amz2xW4\nSbY2lXRheM/xMhCzmR2UKbTzA3ykYrjp/gdK286KURM2embnzZSwdpLho9Yx\nkAi46GnkyPqbhPAGdfSNlkiNi3mflGv2keU0C5OY9tyBOv7Zy2wjW5dz6ywf\nMjMbtysJXidT0PQ448pK2bCBd47YpFr9o5d69vgC61w9Rbpeqs7WT+y2DEeR\nL0+i\r\n=qhAB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDXmTVv0QmfMGUGtdKpTm5PALF/JE06J2PjVocujkGnAIhAMzb43PaJzsxGFJTCJ/4RS8oDEfBDc7LxDTCRAWg2NNY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228135408_1551362242617_0.7943472577717368"},"_hasShrinkwrap":false},"1.3.0-atomist-update-branch-master-20190228135129.20190228135314":{"name":"@atomist/sdm","version":"1.3.0-atomist-update-branch-master-20190228135129.20190228135314","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"36f4da195aa77a5e0c32f157a3dfd1740ad490a6","_id":"@atomist/sdm@1.3.0-atomist-update-branch-master-20190228135129.20190228135314","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-6DuVKr/2X1QhGirS4ia0D6Tm7oiLixLI8Av52WqpBSLXRwJGDxYT8neFCbl6ZOCfE6k86xDx6yL7ar+F8ffXOA==","shasum":"57525896ce733df8a2d97e55028d12d9b94b6916","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-atomist-update-branch-master-20190228135129.20190228135314.tgz","fileCount":741,"unpackedSize":3078446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd+jhCRA9TVsSAnZWagAAnwUQAIuaRsNixcr7C0jS/YzX\nU+ddn6n0WSzRjasqZr5jli7igv7zMeZPOYbblPyh2G+Szd1AsFq7zblXRtoQ\n99RJs9EatRbMEyaGBT4PL4SkjK8WV90Av8gBe4iMN+XkDqm8oDH1uYGkOVSa\n9ZzVfGP4RN3d4nq61KP12Lk6H0bbHfyMmnJ2+0v62QnJmSar0NzD17rfaAsd\nWMbJhU0B+O+NnZE6yXezR3eI4srJ0unckY+B6xcI/NALz840C5QYwaL/VmTA\nsRC1l5l7vWG47us6hvoRlwngycX51M6KW3AdUnYDkaCKJbT+be1FfLiXR8FQ\nLhMMIyc1KGUBU0jFYZiyCbq5mRG7iRNTNu/IJD8rD6n2GjzAfuvLcgB3n4vX\ngTjn5AFuyyrBwmS+8fk8yAETAYfsrcBbDBSwuek9UQozCJrssDweB9o3w7iv\nFBHE5s8Tytjd4w1afkhPM74UUgkjamQaw3o6riG81mM674TPxjYSWWb5pLay\n5NgBnEkTWjt5SOB9m7KJChNJ2ZV/ohPTxbA78e3g2DYIOyrcscwSQYUA8hCw\naFVXX7/NWC0RQPQl0t8KGBa8hzzYKBNl+0DnyVt0fIo9TJGrke4dhUR58dNA\nnHXmvT3gdGP4kJqB/uwc5PKZ2Jqs7FxM+55NDav5UGbf3EV3Q9L/cXTCerG8\nsquV\r\n=GIxB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRe+LpWKT9hvcbFtH0GnUi07ij9P2akRi3FREJUFIHEQIhAMHUgDaflSkXsXSWESsB/kilE8CpO2YYVswQ/rlfY6u1"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-atomist-update-branch-master-20190228135129.20190228135314_1551362272522_0.9379512577098048"},"_hasShrinkwrap":false},"1.3.0-master.20190228135710":{"name":"@atomist/sdm","version":"1.3.0-master.20190228135710","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3cca73f6062dbac5d40d23c518dfb797ed571a5b","_id":"@atomist/sdm@1.3.0-master.20190228135710","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-uaPRq/4W9DUX7IzWGDvR9+a7ingDKbDVYlRBVT+LjBFWKGc5BhAhxtuQDQumq4BzkkzsSu9N6Cws6NS56KTGgQ==","shasum":"d3d46f273f41880bc5f1b4a980f5194a43bc67fa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228135710.tgz","fileCount":741,"unpackedSize":3096494,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd+mWCRA9TVsSAnZWagAADPkP+gNuKM3DF+LQi3lQ8OZH\nsbuVvflGNCB19ip1JNIC6prhc8WFWL8zwbILHOJX8fFszaXtPvQElaIrLRCv\n51xDLvJ0M1LBKaDlYAusiS3j5gqGgOaqbgLY+oyuzFjET301Y9BD2awUcn9g\nERY2qjTM2Gvw0PGCHR0Sr8M7Nibs1cxmkdlPXFfri0KZSfLa7rcvL9KCJoQD\nygA21fk/JJ3c+6pJTqT16SEAhnFq54guXfTeG//N0UutI1qlalaGva9+LrLB\nRuXStqY6c4pm8QUKlUp8g2uJIdbdGSgNV02e1IMkoT+lu39cHi4cO6tDMF0w\nVQwFale4sfDkRjAnuUEr6rvGYqh3c0Wa5ivg70O6oEKAaEsJBjuq+oj3YdFS\n8Lz11lQt+STixT4nAM0x1Bgj9K8tqO9BblQ+TASjso3/eloGqoKnR9zXRJEA\n74ihQTugFfjMHfydQYYgtkMHjQOpdEPxWXQ9ibhxdnhbjKP/+hpmecoJrTXv\nP8Qp0aIibJwAYjaj009f7mnqatRKPq8wX/0fEIfR9/Ax/E0hJwK8fMJBFTEX\nOw3gqwRDw3kODsmk9Ws1FLzHORiSD8a7XfzkVKRxyb5fMzgPPu+CwCJe2Ttr\nC2Dwj50LJmwr0pvaa9VcHOfw6fhUrxNGCauEeKhFMV7rFC/mbrcdD9d4Ax7y\n6dA+\r\n=qTfC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3Has3YyJJ99h43m3g1lm9U680wTOU6FObqKaBn/acrwIhAPy34MKJ7CLTwPa9pJ6pDuKFDD/lLm8SVYfy9tMUNM+e"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228135710_1551362454178_0.021010398932550967"},"_hasShrinkwrap":false},"1.3.0-master.20190228140124":{"name":"@atomist/sdm","version":"1.3.0-master.20190228140124","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3cca73f6062dbac5d40d23c518dfb797ed571a5b","_id":"@atomist/sdm@1.3.0-master.20190228140124","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-n7kjza8WGbzz5c+LOIDnhBcZS6ofsicoJ/4JHr+JstSkU77Y21xqjPTUKCrQuAs/IkeS2ZZpQkZ9AvD9MSbk1Q==","shasum":"bf866ac48e1dfb19d22b522401bffd2f024cb7f3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228140124.tgz","fileCount":741,"unpackedSize":3096494,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcd+qXCRA9TVsSAnZWagAAfqQP/A2v1FHbnWD8R2TMFGNP\nVtEY6BR21pk7EZKvG6bmhxVWK8g1SG39Of5LCaUyZ8klUtvnEKeCR07b0i7v\no6WzhOcjy9o5/zRK9r7e2Xo7KUGn9J1gMYiNnjGKpK1Pw6EtkXIaRy5uNDlk\nz5RMMXDuUT01zoTlGt26PcTQBzvV6+O848LBewTV/S5tdMj6HYQoa+hITCPN\npq548RACr1uB8wJBYfwSsF4WAZApF6oMRhGgzBksnwMHp4NRZ0P3XPwEPhwE\nM++2Y3FJw6akQWuLFvUvaYBQN7Xvfspj6OJGZywla5egxdHM259UOtE7gWp3\nyneiktp9sQC0iNWPN+GSCDXVYf/r3Z0aSgmiU7C25uc/0jtTElSeqNf4DzA1\nTyGKqyqALvT/GYz6rSUw9+Uk5Mq2ppkWRKTSgtMJ/LO0IHBwW3HvO1awGTdC\nrxG7jskUSUD6o4pVyIOdXRk7B2uKL9JQbUcEt2f4n8i/tM6Q9BScbR5xPNXR\nWHIBsYTEuSFPouwEG9Pjnzv3I7LAcVX7OCDom144C3XSNseUgo13+G1ANlRN\nTSdy+/HBcoRmDvVTWyMwf9tEkcC6iR61ucwTjTC+EHhJKZ5FdGIpXWct0xq+\nYsa7BQKIP49RwnDiV2DSpp2saEGa4a0ZRydpzgH3K7RCFaHocwFnYGoLW5bs\nCAXU\r\n=y79l\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCOSR921hhZx4EHMKg0IysJxTRZ2ZAaosSob8zYBuh39gIhAKuVOsmWoGfcPdVIPX6koRRYyK3nmZEnySzIUUAqagIR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228140124_1551362709957_0.9901910665325913"},"_hasShrinkwrap":false},"1.3.0-master.20190228212004":{"name":"@atomist/sdm","version":"1.3.0-master.20190228212004","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5239735155b76a9a82101e2defc0574004a7c1e6","_id":"@atomist/sdm@1.3.0-master.20190228212004","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-k+gyiGkcnjlemHQB4tdY/0RVffKlDxMjfbQRIrUaukEKkvu5XFFTKqGVCrFV3RhxzcCGLPUb1yNDDhNTaVflEw==","shasum":"add91a442a9ecb021b51e5aaae18ccff55db32f4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228212004.tgz","fileCount":741,"unpackedSize":3096631,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceFFjCRA9TVsSAnZWagAALRsQAIn57L8lGUJJKdpa6pvs\n34+5hm2ukkP3LtAH7rDAl6WCIYrYQgDP366maF2ZDjdBIWAS/nJPyE57YLCY\nZ9JIX1/gYLlyDoE0ZOOBrofGYLGwAjZ4eW3DEqihCexEq97HjPeTNbnIfPoI\nXKXs24iu89Qd0m56gLS1Q0niLR7rm9ToEOgaGeQryebuN7PgGz2aj2XPthes\njZUfHTfOG5p+ezVH1o0sLq0PRn7ChcQZgzkNF6gY9uUn5RkSQOHprhK5XzoA\nKOtFJQHB/1MkhpYvqCjHPLxEgoxAaGaC/7OiGrdcXzqeVEwhfn1xOYNlcVj6\nIx7wnsgDF3m8Dipgt/oaf7Afa1IU2kyFFzudO2ZPPBfqPrEcGY4zxpAcgKWB\nhaqTrnwx8QkHEBx/9nuPQuF7IwB+V/j90H970/uzLGkdYW4k3Rv8K/Kf0I8D\nhllMn1sCOA3RpwZgT3jbd35LlvQCPCjeB+FN9UsptpaGvLvfp9yk6JxYls+l\nrWZ1YldLc2Kh8GXzqrViT+flJ++P2eHYPwsqbKgK9IpwlFuaoyhDIyGOKwPi\naHz00GsRL29wxb9DNRWx8LRLIUFlaHvMAlLRSaMwOv6zSU5ODoCB4tQ5nTqw\nG577d6HalnKLLNcyfl4no7JsqFJF14ZPcEVHywR4oy4k13J1GybktjHPJHfF\nuhXL\r\n=I/YS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBifS7b9ywK6vxXMcCpcHIxE0WuvrynvvMfOtNNb6cXiAiAk1lcbSdnrZfW8aUn8WKe2CskpaEzbORIYlK9YAIyU9Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228212004_1551389027070_0.7360846013362987"},"_hasShrinkwrap":false},"1.3.0-master.20190228213223":{"name":"@atomist/sdm","version":"1.3.0-master.20190228213223","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"29be18b11326af97d22b8b8cc551685796d30ab3","_id":"@atomist/sdm@1.3.0-master.20190228213223","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-P+4gEjFQrgo561XGGSg1MT8mwnkoKsYlGqXQP+6jxMHynKU+J5hia+wytJ9m3DBVXEH3uynGUjgqsQChjVjIfg==","shasum":"84f2bae6af2f4c8c2b86083db61beeee461d5f1c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228213223.tgz","fileCount":741,"unpackedSize":3096599,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceFRFCRA9TVsSAnZWagAAYVQP/2RGLrSgy0FURtVo8/Cs\nopm0KGyBuocSxRova57pfu7Vj9BnjD23GC3+z0Wn6Ebh7k+mqCd8Eod1IbO6\nsivp9KXsLgxzAbBaclNihEghkXZOYvcJKPuLnpeUYRTgrWPrU77EDW+jFvjB\nWmgGQksKiqOiieFDdYK2Kj3EhhIjkk+urw29OBkd946dcXcmck6399M7ugCc\n7u8B4NhEi5DTNai/8CA4ut1SFuHQpHltZ6rpB5u89WekBwS4ckATy+WPOdKT\n/z+EoNZsxsOFEAD6fdj0V9+Gwpw0ongW8Hsh9Oo8PjtJpyrObRH3f6i4VwdF\nr15W+uzudLKYIKGN6y7Q6ejRJI3bWsvEzoYlxFb0aZwFGAVdMvHGa1gMksVs\nrXioRXZxeEZmMn1Dja13u51D6Tzpq55YMVM2tBRz5Qq/UrKFJA8oenScZSzF\n/WhOZJndJ1KAcNsL8NAvStyWLsmufMPLlRgCKTfvT9sZOmvRGIkTlg6qLODI\nM3CK2qZ3E8GFcS5duvh9hcQXqrlZ4esgbSxSo5sHAP93YNRBY9tWwueuWT5u\ngzJqg/sALT/DWiEU6FX/hcN3KwCTNXspEHsH9XvDTPEHvQpU3pAzvGkKkuwL\nm00nj8td5MbfwSW5aewB4uUl7yQM9I3KU16sMFme9hWbfl4wpfU3eQDxJuT3\nsq3z\r\n=QEpU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDaeULOOuSjpyYWLRCqviJFIFm0VJA+v6EVWmVZqgFvEgIgRze6O6nPFBTlfQgAU+X3k05WbZ5Ubg36U0CnhCXDJGw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228213223_1551389765272_0.08595906272915088"},"_hasShrinkwrap":false},"1.3.0-master.20190228214652":{"name":"@atomist/sdm","version":"1.3.0-master.20190228214652","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"50844d41e6b2047e403c6d3bb9430af55580ae63","_id":"@atomist/sdm@1.3.0-master.20190228214652","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-xVwnh2yjyfAZ2QD1DAQkTpLMoj+gOtc1TtMX94FH7fbbDkhiODx+PsVKAd+vZsloFrGqqqi3nT7JNuc4NFFuIA==","shasum":"8b4742c70ee39f50bfd83cffddb94d15097a38c3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228214652.tgz","fileCount":741,"unpackedSize":3096608,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceFemCRA9TVsSAnZWagAA2Q0P/2iNliZlwCfGXGlzzhvY\nA4o0RdD3q8KUCySSnTG8wkHN1OCu+oTDF8+eafpC9QKeaRxDL5JaghluxVAD\nx+uh4Q7LGw/ltbRIkacFGoYil0vcgBpUGnIFYsDqj85ALGcj0J1QjMnBzId8\nr3QkvzJu17yAzYo+aBroTCUO4bxU+1AHCWQhBtFq2Bog13NcDxAhEKe4/HVC\n2kcH4GlJDABnoo8gKSIP4q2Bfhgr2zcSjK1yiBYF/M3mvNw171P3fN8SCtUV\nr6cXZAcKQhSHNSQ1eiE32tNgePZLTLZWxp1TRKl6Sd1S57iwiGSXBGv58dNz\nYf9wOIA6n2WMgC40ZJxkkEhaK6UnEeZWNPq1l4KszTE2bKlqv77+lh6XOtTx\nomKxVdNoETOOagsbWVtevF8cwyoqKQHGP2J1toBk5kM5q8BFMxd4z/o6HgFN\nKEfVWPlKPv8Yw9VUMzJUwHhv0Td5de0Rx7eo3PogkXLR8iEzWycJYq8wWl2J\nG0f63HYywFtBHUVSOX6WnNI3PysOx1HOnD7sR1fQbBCG+HPItr/BPo5K3Ciu\ncNeSC3aqArpXAv/aGVBB+LxOZSK7LshwjvTnyNpW3fTXHOSzzsp31fqj/MH3\nBjQn0Tb/fXcOjbwwhy6RfWULXF7hZpMQ1U8vOFO/IRX5avKQp95YpiV1B19w\ntRwW\r\n=XqEk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBP2ry5oT5k0RaoGU3ml3Lk194K2jgPHVDePAlWQVJQyAiBDIfEmaAYDMvj1EfaDktxyXKORhjhgd3rm54ByT6uhPg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228214652_1551390629424_0.30028709891540406"},"_hasShrinkwrap":false},"1.3.0-master.20190228215409":{"name":"@atomist/sdm","version":"1.3.0-master.20190228215409","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f6d642506b0ad4892fd0727ca04a74f71ea00bbd","_id":"@atomist/sdm@1.3.0-master.20190228215409","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-3Ka1c2qBJqZPhP1zkMaIONXuWzFm1W43/Z5vwFVIttHKCzfHJvI0XAuQttEtTOUzj2E/HHu9rXTdM5RD0+j5AA==","shasum":"d002f55af783963db21f64d539f96927a681382a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228215409.tgz","fileCount":741,"unpackedSize":3096677,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceFlcCRA9TVsSAnZWagAAckIP/j+X959xtNpQU1LE1yMb\nEsaTfVTwcLpF/phs4bFEWkUb+GjG4QvXdc1djUiCCtv7Iv2NUKjJ+ziKFe1F\nayp1cZMRj5sfK6IodldWOqmXaHdF8J5QJ6PwdWEMZsW8/MKwC2ERxe5Rvqcn\nill8diJQMocg8Yg/T2vKb2dkcWOHjoej5BDYh+j98ECohkspJ/gQEsUwFU7x\nc4P8KRvF1IjYUhGLexyGzKoFXsheQroVgMbO3u2XtMJz7feXyA9VPCYHD/G+\nKxAmtyK67olpX2GPq6kJfBzNonMsuSeQp/oq1xNCVJmXPs4xoPAWHMmxXptr\nKyIW2hmCcXTfRb/BduuxWP2fb+HajIPdT4DJYatmKNfQ/OdG/5BxcQZ3v3X1\niUw7aT9CFAvVzcOzM7AGOmaPEIOPfAJopQKLiKMu7ndAuplBW/74mjagqhTD\nDGvijlR2QIBhmy8BoWhuZVbJw+/Ap2VSHVbj4ozH+2aW0mcTL9Cf3UyXgkJF\nHDlJ2G60r0mxMpGp0Bg+L3wi3uTqoH5as7lAj6HkDxMQTsb1ShmZx9wux02p\nv1u6BVI4Qx62jLFpbs40EJ4IoR+BmGPk31XvPWSYuMrtTjhTJUw4wY9WB2Fm\nA7a5L0CXo6dYXLXWt3S+JSaTdHy6Kwp7iNcoB4BKSmBUMg/VV1OrU8s6DeNn\nCiK6\r\n=ItP4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDG+m8J5YbMruO/L4k/f7EwUK7HXQAl1k7463AKcyyTPwIgK7NwRmvDMXHi/S6kv/fZ8AqXJq33lur3xSceHYJhT40="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228215409_1551391067761_0.09971998782275149"},"_hasShrinkwrap":false},"1.3.0-master.20190228220157":{"name":"@atomist/sdm","version":"1.3.0-master.20190228220157","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"701defc4a6f9e1053c4a459672adee9572d59c3e","_id":"@atomist/sdm@1.3.0-master.20190228220157","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-fpVdDsjZVoX0zjAV+yezOqBP9fu8zOWc/qpp/kR8yBTOwgEZpJAQXabUuW3JpSF4sJbu31bzSFcYPnkNo7qFsg==","shasum":"480f8d435f6edf8292b2b8adda8049c433dbcc10","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190228220157.tgz","fileCount":741,"unpackedSize":3096999,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceFtSCRA9TVsSAnZWagAA9fkP/1scrJSN12AZkTz0MBuG\nD5QqMcrmkA4h3R8ai4uIl4uhXtcy2+0KPHeL/sZqbj2Q7YUYleT8DEsr0mRP\nEZ6+izjCAJ9cI5+rrXXX8Z8g9VozSbnGIohzgDj9FsMLCMdhd6jpQzqRkz++\nfJyg2HJp7NR3s328+Dx0iK3h5YY9LwJdg4sIXbCYYyl1cQyWVBTJ4gfJhO7c\n+7xTtmi2aKSm2BgWU/D2J8HGjnDKtF13TwRw6luwEjqVuk7PXL0LW4I+mnSt\nDN7s9DUweEhTXhjVNxnp5YjgC+2aGTGQKee9dH6f9p87AJhSOVUPye2/ZLJi\n9tYxHlzmz4ae5aMyHyp2+Q5DUYQTuMkA0vMcWYxzX7NrzZUebOVXUlK3N/hc\ndFalRK7D25m7KvT05wwL5xv1eh1KipbURF27tU8aQJM8C5jGgsWrV5H9aGRu\n0/f4pY3O2wsrv7aKdPZgfXTf3OhzLmVRmTCJ2HVzn/nyYoPkUGE5DXUmSasA\npzxVuHzSc0/jPfREFgFCnQBSCH1WNi21Mq+0OmhwaJ97fjk0QRZuZjXK3w3g\nfv6jXEp5KVjOACvYbMYXkb6Ihc9TVzWOIWKSUXdJvCRbvpXS7mK3QtEplbVE\nKW/iMbqtzu2b4AohE8X0vNWmiNCBcQ7tlSf3RMZAis2mpiiu3v7w8lRjPRjE\n7ucf\r\n=pqTM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHKUglxPHak6tYRc/7gvgdboEmPuV22IenPo22Xo2F3BAiA3kYXnh1KN0rC5Zjj/Hb9EadG1pgU3jQuf5p2a3SCChA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190228220157_1551391569099_0.8860326377905436"},"_hasShrinkwrap":false},"1.3.0-nortissej.apidoc.20190228221827":{"name":"@atomist/sdm","version":"1.3.0-nortissej.apidoc.20190228221827","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cc0bbad88ae1b4edfe9ce5e84c7a93760e279a40","_id":"@atomist/sdm@1.3.0-nortissej.apidoc.20190228221827","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YW5pGtXJYF5dRA8I6M8awDGyqPpQ0JuNoW/1tvUvvAz1Ixumlx23K8yHKTvDmlooii/gltBlDvLS0TRiV4MPzw==","shasum":"d618419b89a93a6422153a6d5953bd23ac1209e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-nortissej.apidoc.20190228221827.tgz","fileCount":741,"unpackedSize":3097705,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceF8SCRA9TVsSAnZWagAAG7AP/1RxCtBd543ocS8SYBWi\nM2A0y8BignMS4R6+rpYyOKzI/JFiIz21vImjZSeYqFuUuCYZHaKCcOUetf7K\n/3Cz9YjwZHK5MFNxeYB/IZmweSsyDca4hDW40WYgxJjYMopK0Q3EaBo3qvaC\nc5nqauN7KRuda6+o5bEIeVthqQ1cPEbycIwSGERU6MLiAiIucd3eMRYgG+C4\nV3VDnyTBobP8MgDt28RjW73PYuN4vNtpOV02smY1yPgLzgkj3XJNX1jb2X2G\nS7LuLaFJp6nQe2CLUCP8z4YGDEAq5EN/3lba48+C2O7myDgBM9S1Gi0R84Wt\nua89g3vFFP6MLimUjLqJnN0HcB/KRR3nAI1Kg3ORIcPzThxue56NvSvaGufz\nCSbh8cS6OKWLnCpXM2+x06OPntQmYQIUezeUW0NFAGDrf67E9VT38O8LIT3f\nE4c4bvF2vEqGuxHYEq82An+cVTe7+WY5Tt4ZgInPBivsHHsPHRMOju39wWr4\n8fFqGUjemfNmWvTODD3D0x223iSD5J5hHMyRqlVsrLH54ceurf0Cm25cmQw/\nvjqtJlrRrr9RB2yC0MTcNsMmzkJY1RmqrMJyz0cn3nadB6AQF0dGk4YNX2WX\nxvF5Mj4ooaLMbObvgMu9EQjGxJo0emIqVUiLLpCRA704xLcrawVlg/fs15Qm\nlc3K\r\n=KRuW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzRQZO7KT09kRPRjKxU/8OJXROeyW76AmrBqRdRFePpAiANd5bTVtyE8mTO8l8yI4+QMb+pEplzKYjp8m6UbFkgfQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-nortissej.apidoc.20190228221827_1551392530042_0.712494052706321"},"_hasShrinkwrap":false},"1.3.0-master.20190301193744":{"name":"@atomist/sdm","version":"1.3.0-master.20190301193744","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"58598a33edd16b48e7e151a94044370b282ae071","_id":"@atomist/sdm@1.3.0-master.20190301193744","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-gCt/p1Inxf9GyNDBAGZGZZ41IEc/vctiuejnQeGcvfgnVld9hWy9aRC1xIYG+fKA/Qrw2RHlZEnz4BxWeSeITw==","shasum":"a006c0eb37da09bab1f8a603bc15272f42c42312","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190301193744.tgz","fileCount":741,"unpackedSize":3097212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJceYriCRA9TVsSAnZWagAA+/kQAJ0+CGobA0pHT9lW4xSq\nG7ET3lW/NWIqw+BtQzHTyx6whtnNcpt+LbFqmpAx3iJ3eQc3s7QukFOZwHVV\nG3eUDDIv/kUvBur7LAgDbZwt/bBAMzKBdK1dCQStD1225Fn1qKN20OosAgyJ\nd+4BS7GyGkJYzCqorK06f8yg5SNbY+XsnRlzv9IGntrug4+puQNJ//1P/edD\n2Qk7C8OdZ7gBFSGVbrQ6hCOZdtqIbkwGzd3Yg/qRIOid1NfVnMOcnZKVHByA\n4sNLp6uTpVjVoNAt+0MG/GeJxfhF8jEXNrVw7ieZVI5oaSd9iMMq0UJX2WL2\nEb2aIry6WmGtKcIiYYR169nL4hBsn4QM1CprKdD4hjfwwXWB81Orbe2sB2AH\nDMT+LF9Vbs0VACMdGbrxhiu/S6P/IEYUAz1wU56CnyZ11xPtl5MzTLCCA3kW\nnyk6LbAL/jjWWdVevAjjj4SzWzzilDoOLweg+t/g1tRKjNz+uEoGnEJHXXbo\nmaer8N7Bk5BLfaZ10JWc20RsFPVMaL7vudvvoKW0ocwKR6HSmADtSmwo8wfS\nGhdwCJBGc7ip5AiKPhfaCXsbkqTSoYR05JjCpgEfNjhZyl48Zv3nv3KBvh43\nOQCg0HTomkJdJz/fX1mU5UE3kqcgXA0C1TC8MhSNu87E7mKWGc4rMAiZQCEh\n+eAU\r\n=DF9h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICBBVaq2PclZkUdyuGrD7fgo3hizyc+a7wtd2BACBXofAiALnzNFprwnQooxtlxIYiWf1BaWGiUO+fSIJpCQEOwkKg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190301193744_1551469281070_0.8918765327739411"},"_hasShrinkwrap":false},"1.3.0-master.20190301225910":{"name":"@atomist/sdm","version":"1.3.0-master.20190301225910","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"95f38553ee98c10ed83758c2497f9757c4c1eff4","_id":"@atomist/sdm@1.3.0-master.20190301225910","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-XTBI0j7r+e/V4r2MEcbzJ/EraCmYXc5MSbopEJUYS55hLUwQuC+JL5ClVrFeHhlmIAKJdIQE5lWFzbm4ID+rag==","shasum":"41e4fdf9522a1478351585a84333a77fc4ccd57e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190301225910.tgz","fileCount":741,"unpackedSize":3097908,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcebooCRA9TVsSAnZWagAABEsP/RwWPra94BGpYyVadp28\n1oGtJFsMS6fPFIUzwyR2eNZeTNogdXyhk8s3KGD9MkwBJ1xDifcI0Xhi2A4s\nliZI8p87JRmCR0RCERMBfINvdDuxKp/jssGaJEff3k+PaPUDmmmXpdGK1/m7\n0JOtfdBc6MLoBLOMiIeSifcN5Wl/Y6PpdB3DlHuYDbKhl3RuMhEA1W35AbOd\nmkEPlTf7IlWLQuxczYftgpyxOzy8V9W+u6b506nWhIvRCwlyWr0Rsnz/V7aE\ncWHJPjc0seIBSXexQroEStTyari6oETrGR/Q7wDQd7Bb7WXk+Vo2KbRzz32i\nLCfoxSEV2Y7k4vahJk5zNYPnhEZNxQ/HkY2eavpnj0KkzgDL591RjpMh/1NU\nW9ZEvZAZNCFcgVgZi5VS8Q+sS570XL+59zha+uZg2DDLQLr+b+BnAe1x5eRe\nF+nPQWWqKn16V5zTEmPRN1YV19OFZAJwEc0I+5gRMKtOid/bhYSByPQRmBOs\noHdS2TGLvH8XOEIZXBPI1G7sX24Xpy6cHgY+8+ZFFg1IZZ3db+wW/+G5WOFa\nQplW4ZThyT4lzi+1KLoCxpFU5Kz69RQhlyXmGM+zksmtc3fv0tUYXCXzynkb\nNhchs1Q+U9vaX1+ctDlQ2lhijvZjKIv5lMQs2SRzDPPkhEkwvF2yPdqtAJNa\ndBlJ\r\n=PXJ3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFx/dQTxdlRJi6es0RymLhDn4MY0JvBhXUw0h7t1b0fBAiEA9r4xgpzYASfSelA5gjqWMz3UwMNWyapv2oQsEC0HFDg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190301225910_1551481383348_0.3416592581192206"},"_hasShrinkwrap":false},"1.3.0-master.20190305064801":{"name":"@atomist/sdm","version":"1.3.0-master.20190305064801","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b5ffda0a829fb12ec0be0281175ad82551a0ce4f","_id":"@atomist/sdm@1.3.0-master.20190305064801","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-be4PeCZcJcpSfbGk+F7cQLuwWiA9Na8v8kxYrgaSRMhcQoTBqDkDiDwqNAvpGDLf2EZIPm0FNvrBkH4Wn7S5Fw==","shasum":"1a4c3ce331cb69422e2511509427efaf2092d341","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190305064801.tgz","fileCount":741,"unpackedSize":3098662,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcfhyFCRA9TVsSAnZWagAAU8QP/3pCpGltct3ztCDZMmYi\nkTbFpSjQP42hmem1whMz+MZDptKh+kU6kQzAvrE66PT1uPMG4s3hqsGP18u0\nKLs+y5ZZQGVTmll++dqNbYZKLxOlm/CXzo0mgX1pnbHgMAK0a0TcBIz6sJ1x\ns+neDGQ3qGJl9WTHytOniArYd7F/UxnKLG26LxxJ32fkFXbyrfMcsrGYHxZk\nY7RE3F40z4POuDJAAjhAMSFH3KTVBw1x9AQQmxs/TLfrdAVEm6lof5Q30e30\nCnalWDJlbyeNGOhRrU9DbW/uo5eW6ByKcGRR1clNUuzWY5L8xP5ZoLaGl2Mr\nvgpZ/AiRSuNMjpLfdaLKbtIHclVr/Oh5Gdfeo07rbAhPv3+Ncuvea5HkBV+R\nJUEQlhz0MksPFgQMLTqKcK+HRAb6EaqPeiNKvSYrWodV9k0UepqsHHyoeLtm\n+nIUww51Lb4mKn9dKzUhQ9SUvtHcs+HJ0vqJ9rOKpoQRkd30A4NWai/ADvm7\nLbi0Mtm+isKqtSlSIOnIdv32/9F3fhnwS8WJPMGM0w6SaD1/7L4p4F+Q0OEl\nUaaNqYuxP0Tf+K7zBhKF2lCrCBfQXS9Y3eFLcHd8eMUvFkzvgXoCyc7BhM2y\ns6CDOkoEUfe960HnjW06O3Hih4OMF5LGnfLbNIzLJnTUf1rTISfma+0Puf0+\n4Gcb\r\n=JuuF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBsgaxeQTfws0y/8fFduaYrcYAEYMvwb0v1DfXZkRfcwIhALiE20KSG3KQsSjDIZtzWzRoUPFgTvUMiOr3IDdzr6+L"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190305064801_1551768708486_0.38186367250886044"},"_hasShrinkwrap":false},"1.3.0-master.20190305073353":{"name":"@atomist/sdm","version":"1.3.0-master.20190305073353","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"24af5daca68bc5a735c33e2ce7654be6b9d2da97","_id":"@atomist/sdm@1.3.0-master.20190305073353","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-ISbvl+O3BASItozFuo3t6kT0X2L6y2lArKBPyqjnHPyIWokOuP9Q375S+WF5T5rd8+K45XcEfRCcTGaPrIO3vg==","shasum":"5b7ada10eab268da87d0dd4882a8613105a7cfcf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190305073353.tgz","fileCount":741,"unpackedSize":3098778,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcficrCRA9TVsSAnZWagAADnYQAIbBFEC+SNcdRVYTR6/1\nHgWKVWzaxHZWti7tlhvdL4YQRojsNbHHQ/8CwccnAGn6GPGOv0R22eqLO64H\n9GesqEh+AtucpLZ2nMkjSLLg9alk8oX/qwgUTeR1XueLrWYq4Yxmt7w2o6fR\nahVEMG/0u0F5Hlf/uK2thCBZFGitzYEt0U3yV9L0sX/b/kiFzu62gR5vqCiN\ntwggXzeY2AcQHADeuDyXQSGnaiv192fgjf/7j69F07+o/CFWKGVJ0SgUKkY3\nGT+Aa/U9uWpSmsDYHZCNGYwOqBopcylg5RqnoxRRfZL30u/VOBg+bJFjLBjn\nmQ2xBLyBA0bMcQNSLHKLE5M9q4qqQzqwJTQCy8oVy6JnqV+yqLbp2bWYdRLg\nyUxnfPAGmGkjjXxdUuLSjWGQrdijDaWwKAZiAHaoFRCQf+fXolissvQ5I9NY\n7+vZ0Ftz59R4i7+4EjMlSaFsBqdVA2/XKn8/JuW0FARqAuGKZV9C5FQXAziv\ni8EMDjB07/VAvIAMjImco4Mcg3WMUg0PgUJlluBYXGOBmhXVZ2mkp2PpSE4n\n+YETHAY+uX3oE70ZMucHupR86DzMuSyvFp7q+C2899RSTyfuU9bBaPjzdqWa\ndyOq1rEwr6K73/ihBFcX2+G7r8dUBY7YBK0fHhSg+MDC+IO5m6NEmkjQU5Qn\nTTn9\r\n=Xb9f\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDW4+Z+V/PBzmqrFPnzWqS7mlxmP+VHq9SEdeYBX/jsWAiEA7cHKqSKxVfnvcuNshWJnihzhDIBfR+ZBkDvSpZ3vo48="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190305073353_1551771434207_0.12281552255504802"},"_hasShrinkwrap":false},"1.3.0-master.20190311140443":{"name":"@atomist/sdm","version":"1.3.0-master.20190311140443","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c958cf72d2cc88736cd68a9e64ffb52f710cc4bc","_id":"@atomist/sdm@1.3.0-master.20190311140443","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-0Bb2h8nSLKpXdzx5iJYp/PyCaYuRvQ8QFYSVtKqrN5L/JLvv3pMqgoIwlsekcuj0jI1vngiEvEo3PUvUR0lTng==","shasum":"15cc21e5465aad2b3f79c079783613dbe2116b75","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190311140443.tgz","fileCount":741,"unpackedSize":3099117,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJchmwRCRA9TVsSAnZWagAAnI4P/3NSKo3MPhNLTb9IStLi\nBBK5n52SFhrJ5pbxQ1eAMf8VJrCzuibrHkbq/+FvsBFF4sU9aONGc5z6SknC\n6bJDSSOV7j/s3Yclq+XcglbBGifQ5ETxLP2YerpojX5LDSrxOSdqRm2WuxQS\nAucmPKgtmslrqIQxUleI+LFEkFXPCOLvPzCVkDBtsly9nYIQgcvX5JEMc/24\ncSSF1U5nY9dJ0UfMrzHLkbxxPcrs68HnS6fUO4/vhYt6+E07yM1GNtVc6tV2\n1w+v1oJ0hNcrkunt587lf1t7HPSViE8PQWSlonQei+Jkv+zC3WyUyuGz+Lu+\ndnVOWOSZDA4zNCGtiS0a/J3zIy89ly9ddz2byqJeq+VGOFiJEaG9Pmzxjput\nxADbBX9veKVg5FCUM8bAqCP60iVezGhSNO2wisKjLuiO+jf46Z2dh2qukejR\ntWS7ndByjgjOh4OJkTJZUpFVDlltJhOjTCLdq7ejdl9XbSryGwjMzPICX8Gl\n7amo+j4/uVZpA0PhfcrwZLRjdD8h+cBR/V9jTC0L+LByeknU5EPqA+QYdnYt\nAgqo1Ta/qkzO6LHD5rJovg2RhCaGh60K2zsWFOE1/Yputsi7f1Px6UmKAXkO\nNkcKrSxHHaYMft8lJxuIVDxt/9VYERtGMVJS6TLqGesIQmLMRIkal38n39Qe\nkiVe\r\n=aVf+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCenM1B/zZcE/Gf1ZqkR2YpEE1SwaQdTfmffUI5ClxG+AIgV+/qdgLLSAhYbdsc9s64ne4PF/tZJ4MfU5T4EV5Lrmc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190311140443_1552313360756_0.4029535468569263"},"_hasShrinkwrap":false},"1.3.0-master.20190312121102":{"name":"@atomist/sdm","version":"1.3.0-master.20190312121102","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190228133619","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4a20265242e873ec84d58968980889775e750ce1","_id":"@atomist/sdm@1.3.0-master.20190312121102","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-+BuNIq3nls10ybGfDpxGlSX+lYW/c9AbIRbvto+n4zrIbiZbs/S2iDBwYsgb6j0mJneK+HPPuh2VTt8rGGmp9g==","shasum":"c20765643a24dd3f67ec74e614f98f061a906476","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190312121102.tgz","fileCount":741,"unpackedSize":3099942,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJch6K3CRA9TVsSAnZWagAAf7oP/jb1q4S7WGOtvDE1+Mc8\n4Tc9cD8+7DwZ+Hvc3T3zbCHG3eiBis6ncL3GulOVy25N4/gtMoVoLyblbFc4\nW/ts7pUaBNdFpI18sfzzjNy0BhHCf60IbmJr3UMvoEC/RHV/wIDeKWS2P9H3\n+eSI44pwgocr1B/51emcJhUL/FoL2ALjSE0l6Kkv2AHN8M/SIzDgodkYts5J\nP6+tY0SjdrHMGlEk3RZGSrGaFaG/W63Hyo3K4B01hZH4+v3/hNVj9NFbPokS\n6XxnxC7rCGKj2WfeoDjW1T4FxaHIbydzrAOTsvp5TFfYf3rNYDp+myo36OTa\nlgmhoe0NhFnM7GgXtYNc+/yNPQUdP+mp/xhfZpNXkaprmxuRbNK0hopDdJwC\ntB/nAM6pWg3e+8vZpgo+9FJUfsjhSKirG0U8wBElXbS1HawSb4CC+Mueec90\ne+Jj5cujoeU4xDGlPTqCtLJXuzO17N8lqCcvrkaaUDq6hASEmHLEUOgts+ki\nfT9HmpNRtCepw32Z9VwbDVR2JgHq+ElgptM8Zpph+dlC341FPHCzfY4Pksaw\nqsyhrIDuc6nXnpAmwpkGO0+piqt6qE4MIiGxu5j3zK9qTPkPFXZz7sJkK/zd\nhK4uMQtGc+VYOKjbdvmQrSdOQBYJPz/sPtclAKbkwztvFGlLP8GC53v3EzH2\nBYk4\r\n=ys7n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB1mgl9c3a40zoAvlKIlXIK1JuJ+ewILw8oSkJMlX4dHAiEAnXDJ75RavkEEL3HMwdlXLBWZhXNvsBGUlj+wh+Ll/X4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190312121102_1552392886326_0.7835240379098627"},"_hasShrinkwrap":false},"1.3.0-atomist-update-branch-master-20190313091831.20190313092013":{"name":"@atomist/sdm","version":"1.3.0-atomist-update-branch-master-20190313091831.20190313092013","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190312083947","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8dc4c230e73934b36eeb703ed807e4f0dabe4d28","_id":"@atomist/sdm@1.3.0-atomist-update-branch-master-20190313091831.20190313092013","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-YcaOZ7tx4CORk3WZfpBlE6GObDPqfXwLn3deo10ubdYedzsKadJ+Gx8AOG7mhVeFp3edQwCZR4q70ZXLr9yD+Q==","shasum":"4d28c59f7d11bd487760bc62b442a9e1d4aa0cd0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-atomist-update-branch-master-20190313091831.20190313092013.tgz","fileCount":741,"unpackedSize":3100013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciMw1CRA9TVsSAnZWagAAdE8QAICTQSW4SmB4J+Ceomwc\nDQxnKD+7xC2sYjyRNAHG/iJegw2KouRN3v8YapIFjOZVJYxIx+1FRlWrsXFX\n/QQDtTvyzfAl5JYHLGwbewwmvGCnwTYszjS9wefQKkjK6aQMAt0CuYvpbdox\n6XsV6loRYIreCQmXgkrwK8GXdrN9Q86Lv4blrWz6uYQqvx6FAsbwNFRHzWHg\n2fxk9n1mLlCwQ9NTwD4jTHNgjzPGDMOSw3UKaiUKCsUAty8IDww+h5AU+kc3\nU5RyxaaLyTbvj28gVsmhFhxb3mzynfYAJyCc0gjsDO/wjRE7l2TGe1vWsEly\nH/GU6kWQOyLxOhDsnz4H8ySnin5Cea3EJdMj+28hklbzU6c8WP8EQzloL/A1\niL0JNa6kzO5uBJHmJaezd24zUcqCyyvXrfXKAsw+/E1RBYF/Dtz+lQvTClZW\nQJ7tQosg+p9WI0RnPsWsASqRVaC6u5vAlEwcqB/7+BySvEuxsKehLBIxQ5Jv\nT6KCqAvxDLhe4vsT8KIPr1cdIbcwrMuYTUMoLyZRu1X8dac9Kx95j+dkF7uS\nSibIK3Z+yerujOxIoEjv8nZnEHa1miu3ctakpTn3a9CCpY40dMzR3axh1enl\noUdZq4842PKxKmUt/pXN8Xd5kzJb9RXk8ftWlriFQdUhjUTVLAzOzGWL4BiE\nk6OH\r\n=srN3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGDbMEhna1Lk/YCSPKS+D9mAC47KqH2VSmRh+0XvAFeQIgLbrdkK5PfO7K/MvwD2cgjKQ4ebDpXtmI5W1Yz5HD1eE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-atomist-update-branch-master-20190313091831.20190313092013_1552469044816_0.9184001190049778"},"_hasShrinkwrap":false},"1.3.0-atomist-update-branch-master-20190313114903.20190313115058":{"name":"@atomist/sdm","version":"1.3.0-atomist-update-branch-master-20190313114903.20190313115058","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190312083947","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d4c4301c6f100bfe38dbeeb0f2ba03b4f6d82394","_id":"@atomist/sdm@1.3.0-atomist-update-branch-master-20190313114903.20190313115058","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-mHVn9XZqyvT5VTJ6xkbKJ34dswm19X6D7ekd/5Ucy2tdXG4QhtauivsTkejtosGEzRA3u+IADQZWZYRkxDt5/Q==","shasum":"43199dd6c24780ea8a5d595ba4fc0af4f4315be6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-atomist-update-branch-master-20190313114903.20190313115058.tgz","fileCount":741,"unpackedSize":3100013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciO+iCRA9TVsSAnZWagAAPJYP+waHDdfwafS2lZaVvFXw\nqC6XFBRiA+SDJJa//faWkGShVjy5OIJMn614zzbEa/6c/w1FDPn1dyMtwnFH\nlebKqnJhEOhdelXZqeeojIqvKiZsamomGahIwUbP7i3OwFBbT3sieaTza4vq\nQppwoT9/DgasPvyQ4gJaaKf3tkwIk+3mleGeC2ELmjORjh2q8k1TXYMRxQpx\noOrFFRNt40ldaZmqZYazGkd4ST0O5YmA3UBBSZWUoook4dP7q/0G9GZymvx8\n0RVmqdA2Izi3wwIzxmzzsXJJFIrrncsq+g+q8eGw9/ijJiSoRbGi7SIBhooM\nP36JIA7V5D0jmKPdLp0W9VLyICOm55rjSvVnGCyGnBpp/V7AmShZfQpYt4qh\nQi3bQx7Bujg/1RhP/M8xoRbziodYnTqPe7K1e/WAXfF/Ea2o2bsYqwcTHJph\n9bYmCrr/kWVH6sDXKSIODkteZjuC06J+DHs+oNFzZymKP1tlin0Xb2q88FQA\nwxwO/D5E53ce8vae/DPpsnNFtpV0OC6aL2ix96f4h37Vc+s1kGnWPxMHvoyD\nhycvbsjCBxhXepMfiDWdSuMYe+2XPg92ScaSJ6l4ieoKVHvhbekS7LEylsQL\nJtnsgOgkYGDlEQsc7neUFymnEYQugVkDJlHKPFeg+MKgb0Stw4bOgMyj2JFi\nSIrQ\r\n=+j28\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF6q2l3YNAvN4vj3s/5bHd+9IPa4EqMvtfDDPmGIf5ZTAiEAvnl6nTYkGNT9EZFO25udeStBmlHbBzxeouKyr2yUYcM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-atomist-update-branch-master-20190313114903.20190313115058_1552478113707_0.8973909628506782"},"_hasShrinkwrap":false},"1.3.0-master.20190313115850":{"name":"@atomist/sdm","version":"1.3.0-master.20190313115850","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.0","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.121","@types/minimatch":"^3.0.3","@types/node":"^11.9.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.6.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.3.0-master.20190312083947","@atomist/slack-messages":"1.1.1-master.20190217194316","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"1.0.2","espower-typescript":"^9.0.1","mocha":"^5.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.2","tslint":"^5.12.1","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dd3aad161e2feb4638bda1659bc81b9ec6df0974","_id":"@atomist/sdm@1.3.0-master.20190313115850","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-y1qHjA5ySM2QmpV/ybz8bjoWrFFteSGfdzDGGL209sa6sdgcFLxI1Q3Xwao3vRQP/76yjkyfrpRFiMRdMcCxAA==","shasum":"4a4ef5f9706e00ac5dfeb35b97be9cd19a178353","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190313115850.tgz","fileCount":741,"unpackedSize":3100385,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciPFtCRA9TVsSAnZWagAAJPMQAJd2eKmforskqqii0QiL\nf3sE4FTeyjijLlY5Vh4tfTzF5+TmrvvZDfhY5pysSpgRpesyLJzuMnJOpvum\nkpi2UsTEquyYHBakAzaae6BbFWPSWQ943dorOBUvmoAnW8MaLRVUzi84yaDN\nmDBTWzodBXZwH6JFqmKa120XxRcFHH3hXrR0HlG8SDPYOeUwLyILjh6sgE83\nqsy/gFNet7oKMC05FjxQKi8uz7gM9BWfS52gPcswF08Cbxh72ClJfykt+EX4\nqRTWI0J1WRGePskFByH+W3SpX7AA0CphG+IRVFvm4sDXjR77oen/iCfmkUcm\nxJT733NwKy1EWbGA3Bm5HEHji52uld1+6eHu12hZgAdG0+vmbWape0V3H9GH\nPEIEZm91AZ3D5a/XEidil7vPs76Nb6b68ocjCRpSVc2fXra4TNkSCSUxmQXz\nLYoadhZNTUs6Tchd+HHn0GxjQEr03ZDMxu7VLB78gd/uaX6TMXp0qyc8a5JF\nEJqAki6xl0JK0bvs1Gp/Y0zDjdtyXAcrQx1Zq8gZHFV8LifcpZdcciJ3fBi5\naiBVBq91ZTa02TeL44goxtgX93nNQFBiF3wr0g/UY8H/UEd02xYPTYYi4Zr1\nzVOxFYtUkq6ncfoZMq7x6fl8ynQCNoh7Ad19b4zM7x6D4kqXXeFa2nz0Ja8H\nH6gq\r\n=d9eV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDw0Y7ote8A+od1+/C+6aSyO6ooDjIMNeT9Ny/gCH4vLAiEA4ytYPhgHuwPhzHx687h6rSQ3Zb6wOaghQTNE8ngfizU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190313115850_1552478572295_0.7982021462170117"},"_hasShrinkwrap":false},"1.3.0-master.20190314232345":{"name":"@atomist/sdm","version":"1.3.0-master.20190314232345","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"94a10a81a3080d73ce65b0d958b03564884d32aa","_id":"@atomist/sdm@1.3.0-master.20190314232345","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-LIW599i3ow35v0lAYX4voahdnJIkpB+xPb3Dlh0Q8IDlrSJ3jmrjYBRabX3UMwfQNOQFeqVebTjP0Oj/5TYfTA==","shasum":"50816d4998a13902ea3827b087c7542bc4581189","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0-master.20190314232345.tgz","fileCount":741,"unpackedSize":3100609,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciuPcCRA9TVsSAnZWagAApxoP/AyxjIab9tI6IeoOUvmk\nl86tuBsBTW7NFdA42xPhdxilpcDgN04ssNZHtcI94Bl6IOpT//z7yUJ/LQ3H\nz9xCSYUuK+/94WITep/9NmaYE9rjhNvPCD7Rr7K0a4P6I8KysIkN6AmlSC6r\nmM90Xl9E54TkCXyrTYiXGNPp684nO/0WXwYUzNdwB2CGUtEVj8YvAp1cAdku\nRPUS1+zJLnoaProqpSw529Cn6RgO4hysJ76GO+A7zEA+u2Qt6gGpqlRoU1X2\ntdy4gxyYOdKdjhKdX/dFKPd51m1UxR7LzgWjXbKOuhQfrKvHUExVjh4uie03\nqDq/iHd4Y22WSyWu1HcE0JSYdmGlfw2gvM93wsYGDAzH/fAUxxHWfpPY/rII\nnyUvNMC9MCEYhKKFET/hVYn6WGd5JXEVnYZCN/Po4fae+S+Ma4RFP54o7sFg\nOH/VQEBCOwV2gJu6YkK3XG7NSnmbpdM67DqhDfYkoZJUoQLcA/iVPO2Ag5PR\nOvuvKmudWvw7fXPzXoyIGO6w45iREnx4tWV6BFBOFeaBDHMX58UUdSIQJDBh\nDVCXIwSwhlYAnNk3n4GTtGqaMQt+PlmdPApKBpkDuqzyfI58U5aLHkV8WbTT\nIxNCMD5zAtvEA1urHBKyO2m4+/DewfjJ4yjEuVoDd6N1BxS4aNt+XvdSbjFd\nhoDf\r\n=RspE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHLQi7BMXGqhyzx4Fa9ybw6HF1fZs20JhBqHBG/LZtI1AiEA6IQFmVlTwWD1Pdsq46z1p6HPcw4fcUNtu0cAvPMuEck="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0-master.20190314232345_1552606172140_0.25042654807375153"},"_hasShrinkwrap":false},"1.3.0":{"name":"@atomist/sdm","version":"1.3.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"85b72fe801f299cf6949df36c375e32ed4588df8","_id":"@atomist/sdm@1.3.0","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-H8czlch61xB14X93wSiGVKx3wf90zEmYXzKwQ8yeXOXwMzIXq6ziHH6E3DMDd3HXv39nmwRAydW2pG3X/7GBGw==","shasum":"cfa6183702f10858cf780c1657abff967fdf2f06","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.0.tgz","fileCount":741,"unpackedSize":3100587,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJciuYjCRA9TVsSAnZWagAARQUP/0dqIMeNy4N2A83cn3Dt\npbc5zzenbVuQOzyjHmq2+O4M4uTD2SyM8iDBtrykRnV9OpYuZpvO9hOB7IdA\naWUog1bA0LeaCRsWwuAE4seAZkADF6RuTfrCDH0SAKswCRFZyhUg8jcVXKv8\n7Z8yjCjv4fRxI/CiihKZis0TE4ru+2+TTCU2kaLzkwaGEq9a7yAmJ41KWWit\nUiAptxOPL7042YIH8+dGjd8JzD8nySY9nxmRMVoaOz7sKLsfsqS76X/nvi77\naBTlfmJacwx2MbfLD4fEMi5aguSZu91tna83yw62U6lhFJkfHVwZXd6/hETC\nbKoUD9byZAh2ycs1xLYb+8CjeC31+Ui6TiR6VNjZXLOSRo+1zdmfW/fI3P29\nK5SfDxf6HdvV5JNBBAU7pxyzDklj9N/g0Ny3EfcV4ONebtjJWoiz5rpigtDu\nyAgJUT1PV6raeg0r7iJRl0K9tKz5nU6QJhYNxbx9D/o87CHRLV9Fk3hhfO4e\nuMilxSDmHyCT/KtKB1ohc9yc0ZidJs/JmFqBGfd9XkjJi84kg4o2aX+H0XTc\nCscY8x6oWeh9POL6GRs3M44g5nznU7Wf2np/MgdZaGghntp56XJVYjeDtZA9\nkJ4DYoZsX9dvo0ig6qGSg2fZsMK7Te1rC7YswuAI6rJdepd1KyP+jT0w+Hhz\neEx0\r\n=Ki7q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvAPglFjbAf1Trtl8rtx7O9v6i5TagXDPWFur+gnBPTQIhAJjLRmgoUp9AlQBVl7j05X9S5b5enDw9lz7UC373fEYq"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.0_1552606755209_0.28850543317423694"},"_hasShrinkwrap":false},"1.3.1-master.20190320072151":{"name":"@atomist/sdm","version":"1.3.1-master.20190320072151","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"858e07ea99e1d29cb705ba8d029cc7d9af2dce9d","_id":"@atomist/sdm@1.3.1-master.20190320072151","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-OH3iTekqv5ZNGXivTI3cvwvmDX/qbAEnExMH0wbHEsU9sCD8N2Nc7uCDonG3oPqcUScIotZyQxqOK+1a9zQkMw==","shasum":"f741c26bd21f37308dee86e76fb92636c9002a17","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-master.20190320072151.tgz","fileCount":741,"unpackedSize":3100775,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckeq4CRA9TVsSAnZWagAAL6sP/0JJUgD+IhoYJUdbyXWj\nS6I6Wdsqe20ulzE/NKG5baMIdOuQ4KMrZBTlQVCF7I6MfWZ1saEtpwSkgUJH\nVTioa5CPxav9IMvQuKv1JUVvoqttbTxl0/w7n5FD/BXy9EHCLYHkw/RtrS5m\n3yC8sAki4JiyLnz+VRQU/X0iLFF4kv47MlWPFjm0QfCikUFKdSo93HCtN5m1\n5yYT5GLrHUfSshqVwVGPAY7vkSIDBC+29/wE2AMugBBq92bBdYplB4C+DcyV\nmDQbgv+Be7gxymyqHdugb3iPh64Tu6RrZaxAu7cr/XgkAmOYVXIuLm1ciF4o\nJVi3zSSmH5pRrDcHLu6BY4EUEEzYePESdDCZunH3XGOjKFlCgMsI17tQII62\nBuixO6O442F09KFUKJtSp1BuCKUtHLMVPGR0UqqLIb9gW61l9FPZ4CbnxWSq\ntd1oBqbxgB5ATgOrWaGGt6U27X0tf/qyllY9rtWXAT560vHrXtDjnESUwXlJ\nk1Mw1PcGfRbk0crL7oBzttP0+Kiy5bWYhk9pdM32HedXMoaJDBSAXcowBDk5\np4NvbqDQsVL4HFC60WBizwZ3ObC33FtQC49LQFc90fbzWX+1mFhwz+0rnVHg\nqSPCNKKxDTq2Xyta+ztaKMkSLiG9SIbH0S1RZh5asV7Bv3Sz5QxKm4wyYf5G\n89ZO\r\n=D8bZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCccF3PHn4gLWbVIGm2QOCkDQa8q2bjsivSgW0RGEb1AIhAO7RDEVpgBFxXZ1yaXIZQOfsP+kgrMk1Z2aD9DBoDPhx"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-master.20190320072151_1553066678981_0.7495150909834394"},"_hasShrinkwrap":false},"1.3.1-master.20190320085838":{"name":"@atomist/sdm","version":"1.3.1-master.20190320085838","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1e11f0bd5814189346a54a3fcadba813cdf4495f","_id":"@atomist/sdm@1.3.1-master.20190320085838","_npmVersion":"6.5.0-next.0","_nodeVersion":"11.6.0","_npmUser":{"name":"atomist","email":"npm@atomist.com"},"dist":{"integrity":"sha512-NCfaWOF8dq1nl5EEw8ueq2LYSD3pXTIyrEKcpQdz8IiCsxOf7B9Sht/pJFTvODgBoUKG3f4PuxRu4B/6Xt31/A==","shasum":"fcba802eabbb377b056ea760d1e55bfe32cd0885","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-master.20190320085838.tgz","fileCount":741,"unpackedSize":3100805,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJckgGHCRA9TVsSAnZWagAAGBwP/Ahf3zTUABUXncoyUELH\nFateU+2FdmFmsy7KB9TBXq8FbWasnj7TppmLOKAcctx3XQnFusINTC6sc9ip\n2T5dto3kE8DWEQZiF5kv8tXwRkTf1IeuFsg2ux4Rmam1pTt3UHafD+hUFvut\n0E2yhtj5K/TOF2SZkrg2dKlHE1Vu1O2Wunzf0Bfa8rar8ZbLL45wZ/u0u4FG\niFZUxRAdfUeR4nlo/9fsqHVCLRnTssk5tpxaxKaGKAUqkfo0/y8+9FPsbBpX\nAlH8fUTa2fFBntlQBGJpC1DCI514i30s5HKcFdA03J6jyj/WZtKKwbX+u3wx\nNXDxhagDgatGqiPrUG4iyq6I7mK1JhXbgrt1BKV3vFd4uqyzx5U7zjq6ePti\n+7p0aGUjhCG3R3jFlEraA81DGVKSturftVFQw2p7gG+z5NGvK8Rzbmoo5xAe\nJdX2yOctjU4WtdRaI3W7pg+v99mnajUA8VQG2fx7alAZtMo9oTH8iSuW6lYg\no00F/m/EF+Eni03XkxnbaBNYFXH/TpnjJu0n+il8k3NByxtSAkvWAqoqqSFF\nbVvPt7i01aJR0KKc1dQ2SU2YZzeIaEQW6XzOmtTgobiqSqYnhwHdy9+4G+nY\ngWD8gK79JjRG7SAmdXpoj6k7QgCJsvCcSASth+/e7JLgFJOHGha+44K9doyT\nVJNq\r\n=shRH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCM7Fhm5hHo3ssGv7Fcdy9x7ZzSQk42HwTcm81bsTzKrQIgUfmg+AnOIXaHrdEb0yXT6oR0p+gXPpSTUWgt98CG5s0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-master.20190320085838_1553072518910_0.5524645747202468"},"_hasShrinkwrap":false},"1.3.1-nortissej.instructions-after-create-sdm.20190325210343":{"name":"@atomist/sdm","version":"1.3.1-nortissej.instructions-after-create-sdm.20190325210343","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a30d542768866840d6c029a75f6b38d282e31727","_id":"@atomist/sdm@1.3.1-nortissej.instructions-after-create-sdm.20190325210343","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-eDw4+GaHXhN3QD2kcTmDzGFOh/8VHwfLlCRaGGGrqyK/p8DJTmZS7TDoGRVc5iSjOq9HSaxwWYal+D9l7AtyXw==","shasum":"2bca4e0963af87e209f957b7aebea2b4fa9ed5e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-nortissej.instructions-after-create-sdm.20190325210343.tgz","fileCount":741,"unpackedSize":3101034,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmUMjCRA9TVsSAnZWagAAqN4P/02FdGZNlHT1IH4hUCM1\n1BxjyOvHb4vegGV1A6BbcptPHKJr9DFeFGKaIv8Wz5G8I+Sr74TJ2G3iZkvb\noNa+ibaNZbOjS/lLJxLPT3RNnUAflOJApz7cD0b3a5aSIQYAnZvDo7xtTD6y\n+NTs7JJTBEvYpl18V/3u0IU8ur+dD3gGbAUdSnQ2zdO7Ogo2he91gBUyFtU7\nj+avSoq4/PGlUrDBu3cMFKXIfl8cjJU7ayh0Bhg72qHe7aPPPsYryitZ3BV/\nE1KhAL8DD+Pp4OrTF4xsijmx3bBYC0Lr32iWSev3wgp93pNabT1Qk3Gssc6R\n8dKZhXwvqJLEOfwFtVY1TExdj9y9uz6CGfIrcHo1PxFKSF8xbyOsY7WqYJGE\nHi+UA3yNmXV8ZD/JZFWiFBbpAGW0RwfPeFh7tCtC1IwEekyUh6Q1sDpdRj6Z\nkJr9DNgpwOUoIfas6fwxpQ5gWmqEpP9SSkQuCdeIjApX/IpGfz2DZairitt8\nuS9dEpJgWpR2hfWoxOR1BG03OdqRyjSo/AseM8uO2ne/1YNAVXnebLxkyq0N\njfCeAwoMrYpVv90ivb9E0PvD4kw77byE7/0x/kDwp3pWAU+mUUTzi8CNLFqE\nqq+OYhiVyReGjf9Y7ixSmFKAGHjB8cfFG/3sbzCs06oPyDgqrRVf67KT0wmI\nSCsm\r\n=W0e6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUzhxS8ByD+11XC0h5+btHOV2OCR/oyQLIZ1Eq7i8v0AIgM4r9A3OB2FMYDsS3k7ZBMYfAi++TCsX89xS64VYGoog="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-nortissej.instructions-after-create-sdm.20190325210343_1553548066265_0.2475736426892332"},"_hasShrinkwrap":false},"1.3.1-master.20190326042612":{"name":"@atomist/sdm","version":"1.3.1-master.20190326042612","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"833adaad68916b913f06e7eb1606b81f22012556","_id":"@atomist/sdm@1.3.1-master.20190326042612","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Ff6CGewHKnqDPBNKzmzOhoYhM2+g5djUEoNdT8eb0/YIuAUOieRYcf6Dy1YQOjGMFrcuqFP2+3Xu7wFExvTtJQ==","shasum":"e564115121b2b67b0c555401ef7ce566254f8b6c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-master.20190326042612.tgz","fileCount":741,"unpackedSize":3101129,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcmaqZCRA9TVsSAnZWagAAcMgP+wc/q8AxE5pN60WCAk5J\n3uR+ZDTgyvTZhvkRuxY6hO22pTF9CeHOPjGkDORvtnfhVSe2Up//ETDO3Mj/\nOliEnHUugBv7o4ygkqfy4NKAe9XPabhTPwYK6I92ykq8mQpA1axJj7LpecOq\njFL7MLA+/KuM/Sf+ITsJ1abJ/Q2lmZ0KYpc/a7V9UI7Dj4bykxAIwIHjhB99\nuA7Imobja4MpME7usgop5cwhlrRBpH1xDdAogydVAGW3YkvLWBzIrWpWGJC5\nLA9o9WWmeBuYC0Z4uheQqBpqWoNzUYj1Nu53qye2gzE2dKthHe86na6n7CRF\nxkIISK8KXtfWsfWv448Qgb8D7GlpNe+zzOKOqrIQMLwfL+a2/dBfFIoyfA+N\n6mXts8XPWyWwXQvq6ebkejFth3CXSIImjGAYRojEX3TeTJtYr5KVMc7P74Z/\nIl+CNtamdJ+mFZhw9EeHhTbA6Y8KTe1A6YgTtPs8mFtD9D6QlTIlefnpaVSY\nzBE47UhPaA6DAjEUFLSWwF9VOS9S41mCrNRazNCCHyJ2i+rwBv5baVL0QBqD\n/t+nxobTJRq0Y5Re1lr4BH//6bSab8HeIkMK6fAiXqJggfWYU/FB1CIC2QWr\nJJFZKWogo/ewsBgDR5S/CLR3BMnwbQ8DGrQPvy/x+0jShP3bHmhsqk1z6qWI\nAViW\r\n=EXgU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCPKUD337ktAgN3ZyJSMUYj6amtpjq6BZtqH4VHRDSVTQIhAJQdswQCTXWy74TIKzqlz0dz/X58GMkosgtiO4BSyipF"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-master.20190326042612_1553574552624_0.4469284542594598"},"_hasShrinkwrap":false},"1.3.1-master.20190328163658":{"name":"@atomist/sdm","version":"1.3.1-master.20190328163658","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"58ac7f64b470979377d42a4494e16e5b863bdbfb","_id":"@atomist/sdm@1.3.1-master.20190328163658","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-VPAt+UqgRlj6z4gjqdZiChKpDqVQV76z7ZCfEbhTjx7nT+YUEfKQXyo6REeiDAZ9Cy51gDBYgBWo8RvOrBHz5w==","shasum":"348f3a7c18c7a48d66b4d5085a15151e85e4bcee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-master.20190328163658.tgz","fileCount":741,"unpackedSize":3101336,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnPkQCRA9TVsSAnZWagAAdPkQAIdvbxN7177TPVeH2GdS\n51Fr2sXNCsQ54Azp8w1YW/WbkQEwVVt8LvPHXQQkEdWCQB5lHgEyggYI3gcq\nkgDqqpdxEYfS+Ic4qHL0tbaHCcDuhCb0Za+bE+5n9dxeZ/hqewsrA3LQh1NC\nUMIPZ+SNM+EC92hhIOQxvdZu1ICiTELOmKaMIZ/pY4nknarXAFhCGmF7dVkM\nCtDyLHPL5Db8I3TXvS0tTaj2rSAZzn9/G6+fc/+6vNl5fxu3O6oZ4ZwntZpJ\nrWJKg/cdMXF0RPMJlnsW+qtyWQpV1IEeQFufzJ3mHdO1NXk3KcGymHr+FXjs\nQ6J8OQykV1YhnxGujON9qkN6MAQQm/ny0aV+jt/D8APcX+LOhndH3lsSZ4Xm\nuN89ZhZnKzVR2ucX3J0NnZ+PCaVBW4V0Oo3zS+4GJXOdaYr9vFOkmIV2AiU2\nfFdqwimcxqbjiSr6zDOcb2ILT3WVuOSsPjrfJXpPq+LPF3pxofRy7no6OQ1z\nb9aDNGj4ib7i+j1kDXOGTMbOjW6/rGIFopGQrD9+zh4U76W7VSZ3hhnNuV9N\niJn6cXnfUnRaiq97wEhiOyt0VZO1F4cM2eARPZUAcBOqXAb5GHLR/kDhUyRj\ntL4mYW6wEEqCXKNSQZZwleVHCiyRCuxt9+5RYd6ZR69iJ9gGzBBChgOKAsLG\n5R5W\r\n=um7s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAzXSVuE2dwhKHStvnPtLG74y00XdyVyd0uEVh/ygPmIAiBXNyhSbzIpHZ30QY6pqcoq3QAcB094O2oHZgmeDPNBdw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-master.20190328163658_1553791247771_0.11849240592013999"},"_hasShrinkwrap":false},"1.3.1-date-format-utc-1.20190329132023":{"name":"@atomist/sdm","version":"1.3.1-date-format-utc-1.20190329132023","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9cbb3b4be2f1388ff3a4393b883d8f0c89604ecf","_id":"@atomist/sdm@1.3.1-date-format-utc-1.20190329132023","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Qv7as/IebJTWbY98vXrEcOe3l9+/F0HpFmS34gvCzp/5udfSmyv//TmlnRMQuWndLCYShR0jTF38ClbAadoQQQ==","shasum":"347b329691b00a376cf6577a72958559001ec6e5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-date-format-utc-1.20190329132023.tgz","fileCount":741,"unpackedSize":3101742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnhxsCRA9TVsSAnZWagAAxsQP/0heVqZmLL91ZxyAGzPM\nsC7Kgj9+Q65ak9G62fEWB4QhEAWG9raxRkEB63B3mcc/qsE0G4Mgl3ZOvomq\nn8QsKOgY0UFjHt0qIPlIt40a3dI+oxIIIRQ0WLu7XtHzvkIokWfXgSiQkk9y\nynzwA+JwmmvkBubS4VOspYWrfvCxN4ZG94doFH3wouwgtZUz2zQ/trnVt6pR\nUrD+8FrOzSRXzdksTbeOzzJN7Je+ekbewh3x0TBLo4e+8NWsComTcgTWA5md\nlxPWEjYas/4HyoPYwPKLuyxEsnTSfusnOmVO1gI7CsHjgPD0LcP/M+/mj9ha\nvK+0BXmURSVdy45/5qVFJiYYRAXUZ6L97Sq4OcZaJdc7nlS9Cgv6oftZnCBB\nYl1IpMBN2qroIudgmTUIiYeq12zVJlkSQNYa/VwhdLS+kcGRLcJAgnKo00oe\nksF34DnPekiIes2vys0Cxsx7bu58tXSvCEP2HSeKlqStx26eLVQX5YXMiV4H\nYVIExoXSQMVCoq3Pxqs3fDVnehD9Gqic4fuqJ0AJjdWOC8TfsSEuuWPZOund\nY3QOOvQfXRAwLjQrcH17CsgaMR3S3trifkLWipR/ZrUJ3ouuACPcwq3rd7Yp\nCI4wXmH0lYMgj8wce7lNGOp8QJSZ/7Gujnq9yLT+egTdblAjScFtLoOdVThn\nW0q/\r\n=z3En\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQZ94wEp8cVLn/rB4uAYv3EL1PuYP5hBvIm1RKzB6ZnAiEAxG1/OIUsgj30je/vXrIPxlJnkcoZGixOZn4mmoQJFok="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-date-format-utc-1.20190329132023_1553865834672_0.7317222305113527"},"_hasShrinkwrap":false},"1.3.1-date-format-utc-1.20190329132633":{"name":"@atomist/sdm","version":"1.3.1-date-format-utc-1.20190329132633","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5ab314b42a151d45eacbf6287b25a6fd96258e83","_id":"@atomist/sdm@1.3.1-date-format-utc-1.20190329132633","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-pSvfGE7X68oiQlEXNSTFWD0i0UZUgxkKc2vLZOewpidArVve14DT2Tueu6Q1nUh8d8L2tN4ZWXj48CUz4N8c8w==","shasum":"6d0715893cefcc36dfc144bdd62f92c41e21f5ed","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-date-format-utc-1.20190329132633.tgz","fileCount":741,"unpackedSize":3101742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnh3PCRA9TVsSAnZWagAAPQgP/3gsyzUMO9OFBZnNCiuP\nn9Ag1vkN2svOs7VD5pAZj+Sw/hPthv3wg8cX7Mu783wDOX+nsP14ZgDZXkyy\n8TgQHyuH3qboSzBR3LgRTd9Veiyfy2tfMrvLj8BSkqYdj7nmDUxdgXHH2X4s\nQ+wGGY2PkYPv4bCm8JHxW7jWdZrMDZmjCtnvccateZJ2Z0BYXwwl2lCLhWkg\nkLwrTH5RWUkDBtuwfi/73ElujcA1eTWdDsRXY8X9hHvuZuSeBK5UC7uWSNB8\nYC1JNyM5fl8IvTv0GyB+PEf/8RLCVAQaCDDijd960N3dOnF1m6zlPPE6uKK9\nFNxeZLVsyEycmS7okCAvyeZtgnp3lr26x7cJexnrRzYLIvS5qoYgMnpWSqhg\n6JgaL1Df8rAFu3HQZYAtritGXPp49VfVbID36CK1ScQpr9fNDfGeb/m3lxxD\nvif0dUstJflTzudiB0fYq0DbpVgHNcXM8/TssIXb7qT87MR+ZUIAdGnHMfRz\njC/te7EgPzc4nrF6g2E3lSCiloFJCezBdv+RxaMSZPVbWQvOvVQUsA2MMmX2\nBLnQ0/psLHCwQ57p1a34CuZPdrcam/XBmIXEIGhKRCN+Kv96dO9v9f1sYcGW\nj5mZ0MzSrpJp1vZrGjAtYMa8MjHLw5Hd43jOV5QRpf5g+TLrkRzXXxopsrr8\nIfmE\r\n=+UEp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD4Dj9rzPcsI8PjDCFPrW4Pc9/RoiAx8WsnjHEj2CcvWQIhAIWFfOyReANR2+qpe77cJuEWJsftFBsCGDAhgpSWVJPj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-date-format-utc-1.20190329132633_1553866190484_0.3315928603939038"},"_hasShrinkwrap":false},"1.3.1-master.20190329134249":{"name":"@atomist/sdm","version":"1.3.1-master.20190329134249","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"60ff701ed3c90291bfb278a502e1388a28b427b3","_id":"@atomist/sdm@1.3.1-master.20190329134249","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-5eqYYcdzgsfr+06Ow6FGI8IYkoaZs7tnh9dnfhjC+qkN14BXthI4xq7ZYzPure3M0yVeqoFwVmDMGVlUMo/+0A==","shasum":"a2ff1d9b45cd3723ee327e5d2d38fa35a8cb8553","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1-master.20190329134249.tgz","fileCount":741,"unpackedSize":3101826,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcniG4CRA9TVsSAnZWagAAaBgP/RjYYN67gVdfJavISB6V\nWsScB5x4kwbWBPXVGqOqKtS+tOTnkbeckbUsDdaI+NXc8EuGkbEGnrEk8Aeg\nlID1HJi7BNu2+AEh2s15jxSfTz2B9AhRAXk9FY69jzGSJLbYrve2OyEfZwYn\n7eJN2bGLrwGWJW3wlZzQrRjUBHQj3+tx036gmmNWIc4MIPBvB8gImeIuObja\nOJy6c+jWtTR8IxIWL9gg4XHvw+m5Yoe7w4DTOmIW4lpqSDPYtkiQMXMkIGtb\n8ITEHJ/LgOe2gesGMiJi+mkdzg3CnsFMVBvopIfg75AEWcYfdEee88f01jd4\nZ14SgIiNcTc+3bvzk8LTNOc3mxl+fIbpGiX/IfuzgG39qUijxsf2LnxEzxfb\nWAww99r1wwyhJHUGKVtzg3Rb2TOLCqPuoO86lZt4YirMma3h11b+UL68BPbb\nB1FxGoEHnp/UpNZEv3ucS3wN07XgG257HgBJk5xCe9cQE2dHwaJ8JkXti8+o\nUZs1fQ1neRGCC7G/FDu1qWLf/uPsYgVEnC71Fkx0p6yf34y0u6/g38ssQCsr\nVH9dlqaODcFBa3SYH8hxq1nJssdUX9fA65T3gbmSt4rQDnKjUf9bw4ynW4Yh\nqXL1VwNPTiwpmuO02OxakAnHNqGMP1/QYA9FcCuqiRANqTrpdIzhvJ5DbDMk\nJ5BV\r\n=92LR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG59V0ImEgY+VomVA6liRIWagfCucrk8SZIRkuLqTeTIAiA+O7zzuiWE7iOzbcWoPZ8ZSZkFBMdLDw0JRO0qjI+/Ow=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1-master.20190329134249_1553867191872_0.35702489405716986"},"_hasShrinkwrap":false},"1.3.1":{"name":"@atomist/sdm","version":"1.3.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"d80fa2b30650af93d5dc3fd3edb2c2dbed0ecbcf","_id":"@atomist/sdm@1.3.1","_nodeVersion":"11.12.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-HbyFJyDAxraYKL/8YYfosEkpfUa+jUS60M5sU8G142Q0ta9R0L1oBfwEHhLffRLcp01x52b3UYLy86hW5XHZJg==","shasum":"a0a24b1178410259bd58f6c79fc8a7779b53f2fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.1.tgz","fileCount":741,"unpackedSize":3101804,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcnjk2CRA9TVsSAnZWagAAh1wP/24uPRBdHQimHPK4pwCl\n3csPJbvyL8N4F2TRFogC0D1SgR6Jd/IXzJZl/dVLbCfb3g9NHyubc4qA2x4l\nDPRDvHDctXlQjokKWFQ1UmeCt0GuP06J0zwFf4zS4x86RLnvWUkButuKk7AB\nCvzpv7RCYaRKd8pf4JkMMQl8NRI6BeCF1ojS1N7820+q1Mi/97UQOZxsGufh\n8xl49ygPH5mYZTKWh7IA9hJboPLww7oqJHJUbGKRuGky4hSGHHTMovu1OFNQ\nLeSYVFbvd1Mh6FXncsotGz7fanaWuxFZ6A8KWDtTgZEo0j0tK8JAYRGiT5sD\nLj19/rs/CyYU5c1fM5nNOdy8ne7Aqs51KzXCE4Qcg63hwzCobWnkpeJtfOtJ\n4cNRLcrpW+mc4N7oZMl0/dng296lCCd9iEPe5PR5pELNAwgZIqf6Oc/6pUub\noY2Z3Zvd7o5BjCLYL7IWud0rAU82TpbrBjHnHWi2QpcWEF/CqulhLTtHTrpn\nipP1f5ouk9ei9YTAeZ8IA6OLOZqek1+MYV+KV2rfkXgh4VOukwvP+rtMmEtV\n97tA7ekdsK2cDZbSWCT0KSEeYhOU4ZWudspGAv8L2KjiFDIksIUdrRV/mC0Z\nJgqXaIRLmu/pFWbX93Avu7gQqoxOEsSuy6G5lwX3i7exLeyEJU4YLNr72tUs\nr5ZX\r\n=HHcQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGO670x9h2iI4JTBVS6OmVk3C4hB2qJnr5TJ9jKy5IX1AiEA7MLnOyKjMNtZVToOzqGllgkmOa3A52rPWte/BHgpwNE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.1_1553873206010_0.4283487118066711"},"_hasShrinkwrap":false},"1.3.2-project-aware.20190401130953":{"name":"@atomist/sdm","version":"1.3.2-project-aware.20190401130953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fdadd65372b4636870a5b09f3a8adeda7f738890","_id":"@atomist/sdm@1.3.2-project-aware.20190401130953","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Sc9msxMPNC55P1CHLe0Wya5goBDjchbyacHudE4CudBvPd2yrRw/5aKAalqf003aEJAzQK3gKK8l90Elhvo+YQ==","shasum":"4d8fca1b34450496f1601d23f17c80ebd2a69a54","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.2-project-aware.20190401130953.tgz","fileCount":741,"unpackedSize":3103276,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcog5aCRA9TVsSAnZWagAAfdkP/2rF+TtdGVeTpwewnBuZ\nvaf0WoALdoUhl2pdvNSpJeNxDezAX7uy5T26cQw4umWJGRbsgyFjUErJiKez\nIeP7OtUPO3JT4IybHj8ia7pAJWPwjhKp+Fp6dVKWihf1jF0bj/MT6AXWq+Pr\nzLjvLAMcOarH84zqMTQ1YY3PyOiWpe5flWT8Q+Sm74SVvLdqVs07rS3ZN9m3\nCcRF3vq1wjfqWQsEBcitKXxHbX7SwzqtcCBn4a+1gSfNwsmVZ6OkdVERiGtr\nM3chbJCuzrBY5+AZO5p4uCYtuKhH5Iai+nFxx6n9Fsdj0XP40M7qKAJ8aNK9\nnBxORU/+DCqryCDM559t+EsPJhHOM4ND0EXuDMlJ7rbUO25H8ZHbqrWJ7vrJ\ngE7ha9eHAO7ygbG2KsaNVhm7ItvK6a/y9Gn8A7nzaErCAUqnXpDDs721k5UY\nSZzHCoo78N8SFKHhZ7wIIDHlTjN0D1W8BNdCDhr9qq7aTb0q2AO9sjZGRTgA\nMhYAq7Wme/SyGZRSe7/HtXigugE1TC3Ytabmqc9jE/nKMGDfoSeJWIZ7J9Ys\nbxJO3XElgJ/AZsANSX5Va3qhGLUaTTrNd7vPpqHHPj4jGprwKRwfxVYKLOL7\nvbaqFrf1QVK81UMVqkIy5XezbsPBlELS2rpDGzmjWFxNM4gnsVRVpTApecZQ\n2MXZ\r\n=5xRX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBhzRRu2a7yonWF3HcrAoGyQcne6pABfuJ3V0Uk7nJ4cAiApDiE+No8ROC25p4ZisQ7kBbmMEqn3G1S2mSHvn9WeTA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.2-project-aware.20190401130953_1554124377320_0.48093189806336745"},"_hasShrinkwrap":false},"1.3.2-master.20190401141922":{"name":"@atomist/sdm","version":"1.3.2-master.20190401141922","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"383cc831298f9cdd0992e54df7acc655ea021c61","_id":"@atomist/sdm@1.3.2-master.20190401141922","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-hf0DbHAjyoDkOA44Pqipu6Uxo4X0LtaHpd9oF/X/ENgPGFdqzvYCjZc0cTjXQUgaRUKSG8TEGcJtevfCJdsszw==","shasum":"ab7d86ccf22cf789ad8262fb7d705f42387a1483","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.3.2-master.20190401141922.tgz","fileCount":741,"unpackedSize":3103368,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcoh6/CRA9TVsSAnZWagAAUIkQAIGWd7I1tUDpp7b7T6X4\n3XoHmtRkws8FhjZFXCTvfqQZ9NnpglS3qyUxHpgwDaIBiBLfRCSEDBd/bMQV\nS9iaIBb4rPHI+SLLJhT58G0DmuicX8NXaBn9pwZ/3fgJwHy/zFuOzcZO8FeH\nnaQYHda8FO1wn4l0ch+yViSilpaUtyIF597YjJFU/WlMDfKrnC7Ee+AILd/7\niCC9iDqzukhRK4yORARyW1vdLAIg+KG7sXfNOp06W2hWnJVV9iJuSaVgo76D\nQ7KYprYmVMLpNpR313/EZFyWL3lwzn5u3QFv4nr2YZLjKl/rFbgbPvyIwHQR\nStq22jhXHL3/cSzoTDDr/decRtzFTolGQjW8/RuYeHtcdDtquTdedh8QGqJ0\n2dalDqdn4gs1Irl975Zpy6Vf76PcxavfHCM23wULTAgy/UADRG+S9nUokyOG\ntdVFgonMzE8CMbiQ1pvfyjPmX3JfnrsrF/86JSoDa/KC7Yof1uzdkKGrMuAE\nqsBnLZmR0xbHemvC+Ibvkqgc5KVV9Ka5PyBqAszsPKMxL3+g6Ff96MKxywG3\nOdRH2XGab54vfEbnkk9wMMKPYI24+iVTPKcSTFAFFIL7gJWdMATX69S3YptJ\n1dqrzRFxM5bEhW2MtlLQja33qaIZSZ4pLLCtbemZmud/OhiPupXsEt9M8vO6\nOSHO\r\n=o6Tn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCD+GaDbbHmmtbJCKB/T/Eo2cbrIinSNpjqn1RTnO5F6wIgO03R/gw0+t76/TsetwSCdR74x9lTZKSY4wlEMbRcz9A="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.3.2-master.20190401141922_1554128573874_0.1729212376329592"},"_hasShrinkwrap":false},"1.4.0-master.20190401142233":{"name":"@atomist/sdm","version":"1.4.0-master.20190401142233","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"383cc831298f9cdd0992e54df7acc655ea021c61","_id":"@atomist/sdm@1.4.0-master.20190401142233","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-9kBmPO6PXLU895UZnpsvV5yF4YqU0uIj+1dWI0dqHKUZjZSyJqIyCUUy4T2mJ5FjA2E8HC73gr89jAc/IxJTng==","shasum":"edad682775d58b408628fc2f11b92f109635d72c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.0-master.20190401142233.tgz","fileCount":741,"unpackedSize":3103368,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcoiMWCRA9TVsSAnZWagAAW1EP/RpWWMZlYsdS1yTRq8bb\n3lOZMWW7oABUNkbwqVJ/VPRD0z5mqMxCJGq+i5tw+6MIZVkogmJR2CNsTkOm\nd/co9ZzzCj3+GFgPQ8ehCa0dnKsrJXx8FiuOnQ5Du9EKlNUJRVEt1tdS2T6N\nr5NwvqtNEGtKNk/ThjJAhQNYb7Xwa4OziFSJWVLkegl8wBF0NfxThVs19mtP\nV7VpKT4blEzf8oKOqnMNd4gOPqloZyA9OSfc9rsDvsSmtrW/BxVb2lNgPbXC\n5RulRt9Ta/nD+RBd7Ghg3nKUpcaTuXQEi+cyXRfi65SBPO3xcF9hblJeUpi7\nGZ5cOK2Rqpz33Ei2CVqM5aZnCcf/qIxcxbMD0uYBEXhQ+NR9Sr+7ENjJbnZO\nbjOwx/U+6qPWUf47lxvCKsex6DpjOCmeLUxLW6FqAAQp5Vrqm+UOBwTot4mX\nPPlUKYBd7wTiEuo8fSgZlreNx1Mjwt2CVx3A55vmqVHUmkuuOuvsFHtGj2c8\nN0kJymUlQ02zZaJJzXA1v9uhhm2scEhlTnhprIPp+hb7/ov6rArHSNEVYThb\nn311H5CUwcOHglCs1hTrtxA18Etp/jakp/a/PFoOH7KzIYPftTu+5zcrYh10\nXxBkxQoCZiFSNZCot181EFzzVus/8F351/bSJ5VYguKXIxrdOfgBbwjObop5\nnPwW\r\n=Dfa/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCU5RCSpfjZaaj04QkjgS3x0cGzBcp0Pzeb4Su3OTkd3QIgeEAcRpAAlvRAyfgV/ttPPqRe+mzJ6m3g8RiVJ7c0vt4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.0-master.20190401142233_1554129685883_0.6099980946070038"},"_hasShrinkwrap":false},"1.4.0":{"name":"@atomist/sdm","version":"1.4.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"8ece498b2f5b6aca6f9a4c5534efcbc603c84ab3","_id":"@atomist/sdm@1.4.0","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-6I1rXfMki4n1sYUhwkL4W+XlBdWOjecv1mGvyQl3DhQmU/UOyUK7mLXsp2NyxXgTelQlFrbGZJc/VDa9wzU95g==","shasum":"d0b0ae9698b27c7dd26996a6536d07cd5405573f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.0.tgz","fileCount":741,"unpackedSize":3103346,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcoic6CRA9TVsSAnZWagAALYAP/j28s7KlucjTqMBdxyKx\n0Z3lPT/XCPJxpDdrFdjOVdRnKxorMo1hZ5h5QwCAtrOpAQLA6tdTSAcd0V7M\n/OIAJ6QvGIcv9hUBhJwaUnIAGoemuuLUsFayktPIbhx7IOtTw5/wlqHZkPw6\nWbXVzBO/djR/de2MXn4pk066f6TFwFlsOJh+1HrstGTBwMsjLh4t7mXMD4Qh\nzMqFVNF9lcRDj3EwzGLefHPnLngHiPfreDt8QjTS78bhTncOYgdt6qchxUYF\nxpsEtOI4Su4DAWYpkENRu/tZinjjFUhFgaAr8z5/BdwvDxdauja75kzLgoky\nTOg2p+rCDwgkJdSU7/RDxDYlaDfSTkQq4JI09h3oNNtqnyGHlKPkB4Uf5P6p\n75VC1C96yri15DUOam43Aiayu6rgIhSw2xls5mZIgI6BL+2LUyz/YKCtsZvl\nrTozY7cyztKfiYQmBI8K2FbYnSw0Soa7e5UY7Ib1deC0wpL0qL8t4/zNRv9h\ncdKYIuJCtB967YVszv/5A8ijl2HHI5SIFhBbQYkmsarVulMle04Lg1o/95c8\nchP13MX0eRk53NYoqhdM66i40XfbBximiPfXbpUFhq2XsWExDxrPinYW0bFl\nKoByRfCAvzJKTTolerDzU9EWGDLN6VutGt48D1STZouf00+47JruK8d3ta5E\nQJWX\r\n=iq1e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICpeBLJYSvWoyZBermxmFsUeRt6o3fcLnAiF54ST3s+kAiEAlC8Dt8Gi9ADpfBy3BxKHZ77XoaxeQsJVc6M7n9L1NYY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.0_1554130745198_0.49277404994427165"},"_hasShrinkwrap":false},"1.4.1-master.20190401161541":{"name":"@atomist/sdm","version":"1.4.1-master.20190401161541","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"903693d050100681a854ca47b1c272a7390f3f7e","_id":"@atomist/sdm@1.4.1-master.20190401161541","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-drkrIVQWlz30ka+uDof66puHEGZju/RBxPsbi5S91+HmiAgrkImHOWmi32KpdanT39v1WI187SQIHt0P7yBLSA==","shasum":"880671fc10023e779d3e1dd05f632ae1d0630d2e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.1-master.20190401161541.tgz","fileCount":741,"unpackedSize":3103456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcojnfCRA9TVsSAnZWagAAj9gQAJKBwQkoYtB/2NoAgfV7\nXzbrDzvls9AkarRoFLymEwUS7aj9luWX4rUAVlw0gJcCq0sHQd4qtQb58blR\nW4Ucpvn8Io0AatCyZaCybBsCaoKo6/tIaX5KgnlMYFFnroVHS10SX9i3cp4i\ns+Fi6VVibK0XN/xYo/wre9afRzPRJ2VBo9snTK1CmE+/oeUoRYuip46tFuVq\nsIKm6D2HemoSUthYT7aD6sfUWfUISEHEuuP36wv7qb8cbLb2c3heesU+HTmM\nX3El24KN7/D1KbFZPLY6ntaUWYGdQqFfUWZSRD2FIWLpqdPxIRu2Je4FloeI\nFcAqXLfFzXxq4EUCJyEIjkyHrYFcPW14HChcThOmXcJR3CUHZKDfQK2j3B/l\n+StOyUZGQkyUikhlIEfevc60VVRJDXSeGc9PXrOvyVAEXD49oAp4dTgftsF2\nqZBK8fbh/PZWQXCFtI+plU4WfHbJpm0gxcyOBV6synYi6xH1FcpnNOjWKLPA\nHLPaXOPd2WG0FbcY54EHceLR7k1X/fJUByepXcRitS8LbTKFJYIIdXU42lOu\nQtaDB2gfgd6wnuo3y0AZcMU6r2Mpnw1gmcEb71nWISMSyNP34RW2w2vlPfKO\nmK7c1qRzFhWNwNaWobfQXtpOrQcAtKuZ6VHwMoi76mJ8b7bcj4KYtYc+Z7BZ\n/le6\r\n=3O0A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD5liQMUeJc3qCZp4k9gY0WhE2DuZk+On+mATUZYr5cvwIgeg1MyGEsGI9iajv0YHivOtqYf4fpdzndm2MX3Zt03UM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.1-master.20190401161541_1554135518601_0.7132148591684542"},"_hasShrinkwrap":false},"1.4.1":{"name":"@atomist/sdm","version":"1.4.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"9e4912a32aaa33e5755a592919e19564da0f41e0","_id":"@atomist/sdm@1.4.1","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-UAAhh2uKLeOhtz6W+I+rCfgLb3fcdYfJQTO+IGARf6GQ/DXgFpeI/6ENGbXMtv2L1htMzsdG87/5KZfxd9q+DQ==","shasum":"e1d7b3ad8a6fbfd1ec27d94e312dd3b27a6d532e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.1.tgz","fileCount":741,"unpackedSize":3103434,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcomPmCRA9TVsSAnZWagAAuiQP+QC59q26nWxO5jC6Pnbh\nnFWChmvF2I8/07w4nD6hBPAEXc9iQ+IlMhMG+QQOJ5WdzBa2wtwGdyAURiqk\nsPdfatF1X9Hoj22EheoklH5bmINkc6/pYLiYk8262bskaR7+QqbBaHV/4hQ9\noLIwIS0R6CxfQxKRC/Eq2O2bN8qCQg4c4M2NGRNBeE/RX86R9sQoC5gswGC8\nba43LyqLmTQzLCH2+DLR4ucArgYU8KLBlrZfEXRZPIzX1uZQ6GOMkxCPQZUH\njhWfDUQz7/X7I/TvG6cSUL/74oH+DUombgAX6bBRouODTtiyoA7IFdKWPmKq\nW+d39KvkiCwHwL7TwByWwH/rDSF1h51gJSVS2BmK1As/PKqZwZBOWov6ACM3\neuqrcqG9lebhTdH+jWvKfd3b3VKNoS/cybvzmBDaUhm6OldDcYV76eUaz70D\nrFysGxAHcgupFDS8mmENmYjWOERZBYefnKBNo7lIpCS8+3b9RNkvMQ6cwKJ/\nc6MfkZFWte7n0LDwMO7mTqaHo3GHR3ggmLBKtcHidHsL+nSThvit40VZ/dwU\nHdEQhgIWtdG0bP2M1V1TCsZ+fk6TKegWRK3TrCNY/ol5EeGC87gW3sc76aeE\nWD1mY6dFhUZXPQ/JBmU73Ku4jEEuJi/76fVhyqWlCd8rrxUqlm+JJmAbvgvE\nKxsC\r\n=LCjS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB6XOUBSZG6xnQTiXnBZAnGS3HH3hFr1oMARDRY2ElkSAiBDrML/vbPSX9slEeWmjoJA9IU5+g72YL654w83yMmffQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.1_1554146277281_0.3155364314931022"},"_hasShrinkwrap":false},"1.4.2-master.20190404124146":{"name":"@atomist/sdm","version":"1.4.2-master.20190404124146","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"37c93ff40c37207200f27d8bcd800474963b4972","_id":"@atomist/sdm@1.4.2-master.20190404124146","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-q8UAp59dy6ePt5IcxWQQoTHum59VsoigDyJCzGeCVhhTkO462Kv59ElVvIKv0TNJILZOodQT0aJdSPA3uJrOdw==","shasum":"a8beeaee2bb7eb3c8e4aa8ce09e367b9c265a756","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.2-master.20190404124146.tgz","fileCount":741,"unpackedSize":3104020,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcpfwrCRA9TVsSAnZWagAAYwUP/1id6XCYzEDV7Az7R/NE\n8jC8xoXyoyjT5VMw3gSqVJ0sLWEt7tIqM0FlxNUhLB0vXVzUO0Wsur7Jp+FK\navY3reH+5thAuPVMOe86adoOD1PJ+msdFb+OkD0iAX7dzRyDAFqsQw2RVVNH\ntW2ojNxkmbWcdn301eJWj9wlSmETNupfWOOAd98dsz6vWcp4RmK1+tUduVB+\nKWpkMboPtGmNaI1mcZomVaG3A6QLJZ1K6RynTOgnVVsbhJMJod0IuMUao3im\n/CF3XuECOmKJVgs2GTrLiM1fVbb8uzPB1PAa2zJmGiWo95JJa2WU2+3jTnEv\nQYzAuwemk/tKRtyMaXCzreAd1CpX9qPDvE2JmHryTfbRhdJ2vwQjRZcqXks7\nWV30JgS0cwE0AtQcww+PymCZeaIaNd+Hsl8Lg6NjrP0B87p6XCs2srBGlLb3\njITtjx5mDz64NH97F2P7bzMBUY0N9+Umo8oO4E6oHbLSce+te0mNCYYoYgPl\nnupZ5GX9breSxleDwWop/QipCV8ACo7Jj1+JDqVuEFo6GKLooelqnhDBTa9T\nEM1QgYxjGYsZsdd3OsxxirAroOiQeDxob1UHTBrlrb1bwAV/PbFTNKviaWYY\npdhv/M/5+fflCnKZNsiGYew7eUH4ezmVuskH4prTuy2sr71foumhm6MJKyHC\ncPiz\r\n=g56H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClr02AUUr+uReXFU2YcDMM4drs1M0A4GTygyL/m/psDQIhAIbSh/b+e+bFaB36ejNnmTYU+l6kU5AvNdOM2RVULpow"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.2-master.20190404124146_1554381867161_0.2545338257358247"},"_hasShrinkwrap":false},"1.4.2-cleanup-push-impact-invocation.20190405085137":{"name":"@atomist/sdm","version":"1.4.2-cleanup-push-impact-invocation.20190405085137","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"84fbfae18ec98b67ca32bce389348de684ebe7e0","_id":"@atomist/sdm@1.4.2-cleanup-push-impact-invocation.20190405085137","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-RAJ09Xy+egv1bzRqVq7Je5Mf2Gyp1xVPoVuePUvb0Yrh+MGpsOm2oyf/XIFDAjBd7N8e6itRMp+6dhvBg+NbOA==","shasum":"6528790204cef63c6c3136f3d045cb27533413b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.2-cleanup-push-impact-invocation.20190405085137.tgz","fileCount":741,"unpackedSize":3103964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcpxfMCRA9TVsSAnZWagAAGisP/2ti9LBmtBRVMxYx44rq\nuKwtVwTPV7+z8HL0qRRn44yvdBhXN1yLkLej+wfKgoBqmj6U1kpWPnbcSQnc\nNMTxSZU6MSclDA3sDewgs/1MGFe9aXW7ZjoqvWgV0F+uZ4Uc88KZ6Npy6FZ0\nOuKc74mVGntw0VLSYvt3G2XhzSsaIc80YTIHmLW77yrIgZX4DBgk45+9VegQ\njJALxlZGIyQNhDTykdF1YKnA1ii98snkI+TZGKVD5Rx516CdC1D9TYjpkddN\nWpUTkbm5KnQqAAYiNzbpl6d5rrGxtAJ5U2A22floeFgOFoMhPd38GpuZ0Qvb\nfxQrLqYuPZIGJm4FxxXQS9ARRxXdIIWswxasg3xW4zUG+PqCTQkiq2zSD+bZ\nUzl5VqlX6+HbD3jjCemgk8ZvtAu5NkMxtg5SqRld0hJXdVaOFBXsb/6hTJq0\nt4OoYMpl8Dhg4+VIc5swQa+dNF2hRDOCTmmFkbi7s1FOulTkVmrDaaeQr0k6\npe9cuEcHiGggaM8R2M0Erlj+gP49zIMeb4Mk9RLo1lXiPTPx9Z6ekOzJz57o\nTC3pwy/b3fajODZvZrMlBrCtVGbCNXVnpGBv/r46zg5xvpHvnoKN6rUvayl+\namwJ2NTTx4Me2/I676g4gCkuD1EPoV5g7CDrNvBiV+4npuhHL6EPF2R3sZNR\n2tBP\r\n=ex+k\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBR9nj2Sh+e5YWMG5p1FdfQHYIr5qPK6Gp0pyecrvRZUAiAzYPYOl/6qxOHQUrHwkQhRpoiSrwNboKERbh+Y4Hj5Nw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.2-cleanup-push-impact-invocation.20190405085137_1554454475401_0.1947063094800252"},"_hasShrinkwrap":false},"1.4.2-master.20190405122526":{"name":"@atomist/sdm","version":"1.4.2-master.20190405122526","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f8d1bdb7c3f1771920881f973be4e2d4e227e219","_id":"@atomist/sdm@1.4.2-master.20190405122526","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-w5Nlcbtrv5a+nMhw/U+Lehr/KqmO2OMVQqzarcG0R+erXhHSQw3oCSKNrHgsXO/JmG6ciLzdNASzlVbUspb/uQ==","shasum":"f2e9d6e9191377173d90437fb94a688f22e53d13","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.2-master.20190405122526.tgz","fileCount":741,"unpackedSize":3104054,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcp0n9CRA9TVsSAnZWagAAaw4QAJDzNOLaH2N2bSAKhz5K\n3so1brwjc4LV58rE6myxGsC2+rCBIJXZ4BY914URo+mUnjfIYqTouprCRv+I\n9yTziTxYGZJt5SqxCcGIgsKD4VQNMGKuQfsq10NKpT/6ReUqfpu3g7azWfKu\nbUxCigduMegli0UuYpDLuslMe42D+4E9QxQ0uEmvxcoHcbC3p1MXs62ehBf6\nRaUl7XgFef4YE+ZuBM2CPkZ3HSUBVajkkKr9/kooXfm7zISHvApqW2k6C5Zp\nrI8aXvrEYEg32uvbGU9uxRZztsBFC3VmXRmQbjZPr4oh8xtahHyahq57Rg3J\n+RsDCPPMPMzxgzztKQiLJvF+tXVKGMEWH2NTnRzcfqA7tYbWZClrRscFooYk\nXtqpWjHyLjFhYQYr/XvnDoip6NjTp1CCtZvwbKjUP7/TUsAzQLkeYvtIi1EA\nZnfjNq2T7HNB/xuWumsfZzg7pItn3en+3YUrNUJgPhB0eMHDTdH+FEBEE06U\n+HjklgRaXkwEC58c0BNwtJ7dkCeWHX4KVbRvUAUNdAHRFH3XUd/U7UVhFryn\nFbypZ3jzGnocTWM3dYZI8chw6fyQ+nXdZhmtIYva+Ip2t1tNO2rNBpB6OGzC\npYA3HvfcrqR52Sab8ADnGWtMX2frkHgdRWrfMiTMdgAHhX859Uipu2IehxkA\n7TBe\r\n=vPn9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBUK9UGjDqb1/dOuELQQLQKZB3DzzFDcWpeP6f7FDXsgIhAJKdlNh23Uokw0U9VerfXbkrylvz34dzrLXSd8fAxDcx"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.2-master.20190405122526_1554467324550_0.6696319560896355"},"_hasShrinkwrap":false},"1.4.2-master.20190409221444":{"name":"@atomist/sdm","version":"1.4.2-master.20190409221444","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e2014a64e7edcd4772a9653f07614c87d69e64ee","_id":"@atomist/sdm@1.4.2-master.20190409221444","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-fbF8/+Kp+l8+Oaww1qYpHDzrthIrenH35ZCiLOj/jP9fNtu42vSsg94LyfogfI5L4p2JIcFE86FIDBBFwUSgEg==","shasum":"f024469ae9d9d70db378ef9d3080662d4b338a03","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.2-master.20190409221444.tgz","fileCount":741,"unpackedSize":3105453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrRoXCRA9TVsSAnZWagAAUUMP/iC0W99gdUm1Rr3B/oHh\n7/TLhxTz8VaDIPILl61Fv/U9MMDKePA9uIOfMin7bGuFcKpsxB0xkGoNwp7k\nmBIH7LsyTWBSHhC6RpTE6osT/0jstccycSD+Fy04k0nRlG8IH3tttTEgOZfD\ntRYXSZSipCdLptWnSJqJH7FZ3U05kqT2OaslZmI6IhmqbMIXfoyfrUjo3qN8\nC2LIxQxitKUCyl/Y/jvpISOiZI66y/vCZb4VamyKo7qyVL/DHniL6RuBc3MW\nOEqVML2JDdf3FqSeUKkY+WAT7vJ8zHwBHf0eHan1s0P7RWc9ctBGEiy9j0Ay\nz4FBCQC7AdPtnDEBEkgHd8ngeEurQrvEDNOpmFDxFKxuzzaDEL3mYYDApggA\nCuxWw5KTD1IxZdcXKhVJhPpKQ9+PRqF6RyjOlF6lF8zz96wbB6kBrHOCi+s1\nQQeFsBlf2ooVv+oGrorc2nN/clE53TqKWuFboYTgSV7e1XFkYCSM4sTq8jx5\nCl6OqV5mUT5FFpzUh7khocOafeHbSynVD9euBeGWYmFZoVSz8s1FyKPMmUl3\nk1Db0DdgQyi9znyOVJFhDfhci6hKUbr6S7aiEETKZinT/goGS3nAAXMypMKL\nvoP0B6byGEGJ5wairgL95toW173LJUTMEThAM6oN3us/IFvGb3vyDoJKNIfY\nJRoi\r\n=coy9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHSS3XxJFfz3brIltVB6cuQ6wiuhFuT/6dyMKT2t0f5ZAiAa/Y4MmNShGu0TvDuADlLaFCiHg/+4fRgjuE9T8AXjHQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.2-master.20190409221444_1554848278239_0.6460179823658732"},"_hasShrinkwrap":false},"1.4.2-master.20190409224151":{"name":"@atomist/sdm","version":"1.4.2-master.20190409224151","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9af84aff5020a80684f3fdcb498d8919bffe4baa","_id":"@atomist/sdm@1.4.2-master.20190409224151","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-/h9srjnUxAFrbIV2D5SOClZf+wNpVyTsLnMPPHsH2J+i0e4FcXNrwzPF7z3YFy+ex8ddU+ZmyCxOXQcodv+7fg==","shasum":"e533433a1839e585cdbc6ffe6174f92a42c9894c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.4.2-master.20190409224151.tgz","fileCount":741,"unpackedSize":3107283,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrSB8CRA9TVsSAnZWagAADwEP/AyO+C9/JhOE0PJHP7cV\nK/SHmeltJbbeZeDm4Q7kn/yTGBpsigIdIwlZCUd+uDvdws19s1E1SXIyll8U\n4v+r09IfIeJ6Ju2TCDdyfMYxREG6MvtKAVEOuq5W05lyxbQaenZF/ySmAbNO\njYN3dqLhMPYhPSFDOAs6NgqK+XWr8KAQmDCjqDn1BClOvc6gLv4xaA3HpVac\npo4P6NilKInMQTMLsg+w8SBEqhBZF74jcRD6PjEdfWkoy2aolBlGtG7KfNyq\nAUyYDecCi11/zUafpl1jnbBdYn2dqAcC6srGoAdTqdGGk3jrrQfMAi7jDFMU\neNNvS91y91XxBlaGwwVb7TvmR6208XJGycoBspEKjAtRbiLwGFDVVe/mt9fW\n+THWqsIqHr4BLrfGm2KLOO3F+ujM1AczECzYx5qLC7OI1+K1VtjYfK95rjLo\nXf/LTgD6pM+Md/JyddTAZpjA37lZ3F8e1fPdYc0liyTOfle/W17egn8VEcYn\ngW1sYTEnQkcWAn+qdV16YNY7ZLvQSRT7+4wQUbrNMLn4NNrj8humPa361Urh\nxfD5A/QhieIlvCSuK+G6ZOuvmE/YMZkGV2ASg022qgsDF+KHLd45y7JrC2Ma\nTvaiCloRqh3Xouv7/CExWXOu57dE7XGJnqNXKm/8ZwZveCR7uPH1YljXw6Ob\n6J7m\r\n=CkHs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1mYvZSywQWscgjA9hJNVaYa2NsomLYH4sKkvQ/psAKwIgckK0nnrOt65wzRSsN9902pnQaRGRTcMGHOwguI/Iz5o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.4.2-master.20190409224151_1554849915722_0.7433816770452693"},"_hasShrinkwrap":false},"1.5.0-master.20190409224437":{"name":"@atomist/sdm","version":"1.5.0-master.20190409224437","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9af84aff5020a80684f3fdcb498d8919bffe4baa","_id":"@atomist/sdm@1.5.0-master.20190409224437","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-9Pn8YUjNB3l4+EzIuwvms2jAbdh4vu5c7VDOpv0X4uaauxUMqEocW53QpVziLbO3wcCVQJUCmV97MbeoZAMHow==","shasum":"9ade911a4d80d70cdaf5216b16c592bea5d4c7da","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190409224437.tgz","fileCount":741,"unpackedSize":3107283,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrSE8CRA9TVsSAnZWagAAUZoP/R/h4EuLYlZI7GhVDOk2\nanTwNSFgJ8/9tVuBaWPErpUv3ZsWggdyxnicAMb1pDzfAxiBHeYRzv/WGeZG\nwecdAk1ODmmVSiqf436GYATQmxEeJf/KBEf0wWDWFctI8f0iuPBw7Vgq8XwA\nQEKsIplEAMTvSTfPOhx/vDBCQlrfKyWlRIsOebAAg/npnov9pZ4ZKzH7d/vc\n3TC9RZ7GkV32lh5E1NBpU8NoqzWxrFST0DHePIr6XwK5TV8W4EbxSpB0YU3f\naEaF/SDXZqHvoFgQzK6LvJuqu4OmNK3xb1cBOXgQIV+/ud8oMQnqKva6acao\nxeO837IsIGKaAJPT67MRvY9gF4qjayjKpTzBPTS/CIY7j5hmeEYm2zyMJR6Z\nfucgXI/BoD9MEJOGC7V/dsL1uO15YnoBtdlkFiN6Ewkal5vV1qcraRrNFSf7\nb1JzarReZ0vQG2KybXeJm49eLsSanRTGwKInFtpTe1wZCBnvyo2mRg5GNQym\nRblT9O5TmtpXDXPZY1fG/v192MDLdOcO/LcN+YiwZ3iVBKaqN1Kge/UkxfMk\nC6TpJjZcnkuRrM3Ihrqgx0xyKBWFOlLl6wrroHmezvFQdTWefgqnSW+SofFu\n/+IrZz6HNOqIquMfrlm2WiefQuy8YYCu2z3kN/L6nHq4mZH2jI++xDLMTNbC\nUiZa\r\n=Z+7p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDaacx06uCFwZHtA5DyWgtME4XkRMxNf+S2UKOaobl6RwIgSrowMjZAXC+bLxAnDNCv00E1NQeQRwT8MNeXW3caP5g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190409224437_1554850107316_0.2536756525954078"},"_hasShrinkwrap":false},"1.5.0-master.20190409224507":{"name":"@atomist/sdm","version":"1.5.0-master.20190409224507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9af84aff5020a80684f3fdcb498d8919bffe4baa","_id":"@atomist/sdm@1.5.0-master.20190409224507","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-r9wNdnXyhUf+qKRjf5Tni/tyiHA7w1DlsuU2NjA6IM0YZZryrgtohnVTZ5q+OqYiJbScBSLwJe9n1bJuYfX7dg==","shasum":"0a653d892e3da954abf30c70f58c26e8a5b26aa8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190409224507.tgz","fileCount":741,"unpackedSize":3107283,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrSFbCRA9TVsSAnZWagAAIO8P/R6gjoxzYQ4GtnMlwRcA\nezm6H+xLwvKwLFxtKsWF7FXa/ZRs5g6bBkqpeCEFXvuexwFHbRVDlGV8uVX2\n3JtjCerdwhqMdJcTeRuAdkteHBBMf0PSuk96hbyhnjjPftw1xhmjhXMc4uPT\ntqTEnk72poPBoPFCh48UXx4dr9D5t01PP1Mz//rmThYh1DUWthPu+fHJX7eX\ngggMrz56Q6WOt04b7fgvvObZhClNaLN5rnjIq+AWxRI24mTyzGvLRcYeEG6o\nodxG1R7Y+SdjBOwU2xMNSF+iqYdxGGzQ9qtpaDmiMe0f5vQJFM3hSfNG/v4l\nngiqlYZ2d8o2gCeULYPEEDfVkS/wkzLhUSyUGU+cUYUrtyhxqfV3HvcocU5K\nrlEABefiVuS4vQqngr2ipggWO9M8b85OVEc9zihkFbyjQtXpVc4/vgkOewmV\nLlulpBGK+hxH2qq6hZ10r/iUVZ2NslJGcmCBCUgNyFm7Fn/IQwgI6pSsMcob\n5bZsH1f5B7STNBZNO0m473Uc6VMtSCBAk12+m8WDLw5LM+vdqH+JpkIXsWRv\n6Iwtlhxvrv2WeSCnH9PZ6shBDxjvvsYkWfmeKUnKDFWrBDFm7n/JSzp/xbeg\niXRCXBV6HCxSEO+D2UBWc3fkTiv0j4dngTCbHw0BlfQhtUTYPOv1l0d6pIO1\nDUCQ\r\n=ms9s\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA0915Vo7zqI2dk7KC6Fn7VKleapWdKCiBm/8s4kknLCAiEA1ODHAJPxzE1C+1QKBdqrqiClz6ATIiSNNXk4CrQlupY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190409224507_1554850138553_0.688153412369441"},"_hasShrinkwrap":false},"1.5.0-master.20190411065419":{"name":"@atomist/sdm","version":"1.5.0-master.20190411065419","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d45b4c99557dee6c65457e418e16a1bf4088e8fb","_id":"@atomist/sdm@1.5.0-master.20190411065419","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-KvDhglGiIycZ/IY4WMORQpilNjGiZAUiOw3gvtXc+6ENyJESsDfmbSaz96+brc3ksJrFAe9LgV+gm4xLktx2mw==","shasum":"9f3802fdcfb126e62851f57c50fd152c3ffcb2d7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190411065419.tgz","fileCount":741,"unpackedSize":3107458,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcruVJCRA9TVsSAnZWagAA0pAP/39NEAIzk08FqaS8bDcw\no9mppdFeSeaM9mEkCiD4XCoMuDXIoscSCRubEJOJTZBOoICK/c/JIV0FIMFp\np91NZpfuzNouH4gto+ZEhsHHicd2xFJX5doUeYhNI7+UD4ozz+m3qF3N5yXA\nknu/chINMtz3YF45LLcYV1v7edxez0blcCWb5gt47Rkxbr9NlGKNMeKNEpWP\nhiL0K/AMZSYeXnPH1AfKMthZeesM6mUZBUKMcsNocenfGAzDC1LudL6tGAwP\nlIrysHGXrD+wQRBq0XrvI52Mlq9Ti8RfORuaJttkeTO6Mh1rFtjH2AW1/lAm\nGMC6lCcoJ3NMUG67MmkUvEkvT+mTZmyKOoyS254Anp4MHtohWOCDqPcO1DjP\n4bXZ2rYzuiGIlGdYI8/aA6xA85ILH0wC6MhFjGoZuEuTJTY9u9fqun8CBFpH\nk6KoJDLZEkbz5Y6ynR3OYJ7Fcu696aLH0KYHHt36um8Aigmlxh6Fr0yhN5qE\nccT7d+a3Mz4X399vjyzmlI2ZY82aNBOxU7VCroXwzSND+miHyNKwLjuc0pdd\niH5musR/og0h8ouT2w+P+lZ78yQoh9b+SzjfsJwUwK48JGBJoQL4QOlSeNT0\nS+Y/U0djO1hMvZMJmDTQPjk9GHJa1ERqtQHyaIMG7clSl/yabQJnJsK6KeRl\nRQVy\r\n=/y4j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/AsZkLoiNRpNjMoVkRnGDzrt+lpLhHLvqHcDqoxCJmgIgJNW3Yge0qA3KKXUEzP8dxWCeVKh+EBoPImVzj58rsXU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190411065419_1554965832930_0.09855538236161365"},"_hasShrinkwrap":false},"1.5.0-master.20190411070302":{"name":"@atomist/sdm","version":"1.5.0-master.20190411070302","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8a5f058cb58fc473ab70494ce20bb3f7d8c44b2d","_id":"@atomist/sdm@1.5.0-master.20190411070302","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-rToZzl0oVZ9/q3oZWVhcNwkefJ/lyjguzprN90AvJWtkMnF8PzZH3jLRhAx9VWI7a3BAa+PW5DY6VVPu+pGpAQ==","shasum":"b7bb002f8e477fd491161fb8eafa7b53368eafc5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190411070302.tgz","fileCount":741,"unpackedSize":3107614,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrudBCRA9TVsSAnZWagAAZKMQAKFNYXc2Cy2/V2+KwVbp\nDgnsDrTGN9+1kBoIM5usGTW4DjzF9QbIp0oOFXjXzR+baBOzQzZ7NHbLaRZx\n3D2Iw3W8j2sWYwR5gKwpIQMVY+2//ETdP7KtVqVFwZWBGwCDNG1YhnDXgPbY\nhoQMvFgb/+7CmtVrStTbQ5nkDdDsJgn+pe2JjjS+PClsK+6wTwEkxgPMaqVo\nuPcZhpeTsgpjQ2AOJ85vfNB47k/VvQbcH1FGQ5KtmG13n7H7gJaYQeSwZ+ZH\nd3zxcK9AyRbC33dpjbwYiiSqhFyCBWaJ0lfNu26spoETfNdbVL6aFXoa07oP\nfqGULt+jyjZtRoG5EqNrCTvrnDREqPNgba1YfACPcpjKaOM8bT6x2VcbOP9g\nvs8My3g+63O4Ygqo68IxiArZ1hB/QWrFI4PMhqlQY7RSQtAxOO5JJdgr10cz\nxd7grjHnH+3b70heflopbDFCZ46sUJ+vP0lXaSyZjvy8Gv6b7ey0q1LC0jVN\ntU1+F6HJUwoAkFBZDLT4YJrYk8ydpzvIPLp3Tk82NqL/0QuNHL6lMU8PKM6W\nUEV6/u4OnoxlQcsclte15ZJnZx2pcEQU08PODwuKaMrEPLzW5ehDaygbbKv1\nXh0TApFOB0o147ReHFmUX4rhOmPzbOYDfYGXziLsEoqv/8gr++yZjFTeNW4W\nHdHw\r\n=MpMX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEH7I4is+veiBwTpQ+eoa4Gg7193Fs7oAI9Tpoqp9oybAiBQcIsvioHGVxuXrrLIglbupVEwInA/yx7ACWH10QU1yQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190411070302_1554966336369_0.1102815030150881"},"_hasShrinkwrap":false},"1.5.0-master.20190411091959":{"name":"@atomist/sdm","version":"1.5.0-master.20190411091959","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ba0f5961508ced19a16011d882b4914729a3e5ad","_id":"@atomist/sdm@1.5.0-master.20190411091959","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-l+uHjRvCweBGlQ3XPN+ggciytiW2yxX/e28rV6l2XYnga0bkL+kpMoIRyvpoN+RQDlZRFDjljTLw2W5sFDm3Rw==","shasum":"d8aaca786fcbb01a5ce7bb93ea9c7df4575acb1d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190411091959.tgz","fileCount":741,"unpackedSize":3107650,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcrweSCRA9TVsSAnZWagAA9ZoP/R2d+WHibos0yI7Ld+D6\n4vavXgOOwyWFw755j1KyBHMvUEfHpm4eUJUH3mVcBZMqp9EInmkFEhXVEkYn\noVdKTNKXeHNDvsm/SWafhQYYUkSs3hyJ7mg3vaDTmGC56tlbVhAKy3dAVdRI\nRZG2Y1innAC7/UkTYpabwvVQBxJxXPrhO7fNPxY4ItielcIhonMNUnhrEo1n\n9lAduA35oSLWewpVaVlfwE+EQkipjK4ZqhC2rJo2XLwbYuGeFL4/UxPx2e13\n4QP2/xlfytJoCCVX85aA26MM4h+o0MTfsBURTNAb1+DzwQSqA0Bpejl/+dqO\nXjGtZPhxrzhWwGFaXsPH9YfAmOxAIg8w8X3NCMBHsIO5Bovv30Xx8n+CY1Yw\nLN5dPPvRAXVPYQTCh8p6Hj/B5FvyOQk3nxFbuW6O4pQK8mhHnO/0dQCJtQ9x\ngn+JP8KG36HNL+EK6LhZwql++WUhoNwOoI7XUgVc8pSHKiNdVPG7SpkJCZ1R\nUo6oEgR9Oy5oHqXQJ0AIuDubSF4VIxpYkNx1BQYJSNPEILJe6aV0ynaAsrwC\nFGY25s7kyj3ExQ+f90Qhi02rLEXjqLdG4wJ8vcZG39APqYR1R0CDXwkpLXK8\n8qd8zcU+2YKKfqHBk1Z7F2FlCJvZsiA34c7UA9H7quJIyadFQQrMDaoMVJ41\nkVIo\r\n=H6Ss\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC9FYIvpC2eVejrMfJDZfONaA2t4GEcXeLccpY1EDK3wAiEA82g+HDajIdbcbSpq8dVnqDinnSK8conE2Kf3PBJRUGM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190411091959_1554974609991_0.3853106245374287"},"_hasShrinkwrap":false},"1.5.0-master.20190412095048":{"name":"@atomist/sdm","version":"1.5.0-master.20190412095048","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cab86358f1af189756ada0029114c589c8fb2257","_id":"@atomist/sdm@1.5.0-master.20190412095048","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-dnmzojEFHDcjdE8ru4JBm3ukk8fTLHD/ndAmzDcnF7yOLUAZt/VhS5FHxz8WGVmCNbrByiP4CnAOut2w/bdd3A==","shasum":"9ca0686ca9e9ec6334b5ac8b45a6bc57c45b3d0a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190412095048.tgz","fileCount":741,"unpackedSize":3107570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsGBPCRA9TVsSAnZWagAAiTMP/3Pns8cnH8lwgV1vFtgV\nwPjzGrFMlcFcCljYMCRl/UnV53Drzj2JbJVC/Ai8p+QZqadgz/IqhiWL8AVO\n2n/MHLSAsklEUBx6TBfPhiRs+H4+x68PJ+4Me1MWVGPDRrzZrL/5K+A29/JS\nkQpy6pHnH/2rKGAZMcUK/FBXk6nkFadCC5ZAAbR/sqY77gLPp9Gwrqf8F7Xi\nK0iHsx4eV6pztgA0lbpxGcqBClZCjDAELvWNKcZa4G3yP0DEWYgIz8+vEZA4\n+C//xLThJU0xyp2ygr9erZMV/huoE988CkNDm9I/3g+ibNd8bizKzcj7hIHQ\nplp1BeJZr1/ri5sQ2hNfyoulYPpD97h4PSPSZgibGsTG3aeC8Z3PnS8fk2cL\nReyeBB74Da6Anm+DgPmOnesHNX/sp2NFwvpbdsCMFAl5C040fQ+lYwpQ+1Mh\nQ7PU0JE8hzfQYIAiKoQL8qNrMhTf5qDU6a4z5B9OWpWFDp0kEV8U4/X19YEW\nrAWKA8V/PbATQCTXe4MzqQmpn20wg9iKtEzNylwtp39OtUSePKLIqlweGn64\njrg4Iz224SMKRdBka4Zj9kjFLkYGKOgeKSuhTO5ADdIIK7sEzRjE3jZmvRMY\nroMbJ7latC3jGm5W6ZBCRs41EJG1QgEjuIV08UZKWpDfi2jVrEPVZswZK55T\nL3Gw\r\n=8JeD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCXpga5rW8+qnjfP3sYNnxg7EiXwIbSbTIe5LdLNUAJDgIhAORyX4hhgj7gT7M3hD5dw5V163a2GpibpTMiZH6FzdKj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190412095048_1555062862801_0.20636952823939714"},"_hasShrinkwrap":false},"1.5.0-master.20190412214956":{"name":"@atomist/sdm","version":"1.5.0-master.20190412214956","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4f033e09bd92eab40be82c0d3344e481d31b44ed","_id":"@atomist/sdm@1.5.0-master.20190412214956","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-P8PsrM2dS8+iB3iBBpsvdZF2J4VJ/zj7Y7bg1PA6l+movQQ7FBv0k/yb46R0ZxrSp4tP19i1/Ugw0ErYb8Qs9w==","shasum":"4941b3bc810f671132e0167064aad26cae642b02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190412214956.tgz","fileCount":741,"unpackedSize":3107570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsQjjCRA9TVsSAnZWagAA/Z8P/055z3YHoGsTW3rjBzjV\naMJPPyRPO/cMy6mezRvC2SkzP65Cu4Yppp9R6RY5NExgG5MHtstx+xBHqPXj\nCbZXzzlZc0XJbGPkoq3OENE/k4oUuNT6pucKPdnsYF06KLb9hyJcYTH3wPso\nIXUbt0wZWHO1AytMVdnD0ODaRjwlwTK1gHJVdltZqASu/KylS1KaX0KO/zMF\n0ONu6oOoHoikvVSa8xqQyANJT9ABrJ7LCR3/6jMo1PGML6JF1dqQegZpr+pW\nsFU/ByGwtcXjIW9Q84jR3bm0uMSN1z/2jD3S7nMC00QojQRPVMKm6XkHfv3U\n3xCFNQt3wYsjFQeAj7+8unB40xruS2WpZ4Xta043/hHQoeYxUF/c39NUwun/\nCR9cImTm5OIako9lTm7MPKvIWolVGvwd1lwJIQhEstZyWbuIeJhT/nZvzKVp\nPQWTblYb3eJqD88zvGLXPMfgWaJmT1c0QUkhGMqYObDK8a06GtmT69fTLBVg\nIM6ayLwrH6SwqzDJn4S4NW+o8Wl1JhUrZxOaN1Lahgr8Q2vNhock23Tvym4g\nuQlvoU2eNx413WRhgEzrConX4kTdZD6jQOHgicFDsfYJZh1j5IaDwtiZ0Kcg\noNnEXtWtpdybMredbbLbHzBkq72OIPZsiwd4gJdiLUH5ELQitKKUOqivMkfV\nSu3i\r\n=5mqT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmpj0svdcHoTsLU47GKD8Aetnx5yiJbkoAZMlmB0K30QIhAOJb3yn5SlyKkhQKq9+Ts9z6EmEpjwsh4jn6ZxiwG7cw"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190412214956_1555106018945_0.6440778288734041"},"_hasShrinkwrap":false},"1.5.0-master.20190413072358":{"name":"@atomist/sdm","version":"1.5.0-master.20190413072358","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"41c1245ceaf57c1ccd7d30adb2a92478f3d155a0","_id":"@atomist/sdm@1.5.0-master.20190413072358","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-7KYtYQ+2oj9oQo9G6F3PUTzSPa0AMf36VqkeiwOHTC583+8MEnV3rJtZRMBR8NB9D05Q/zsz1DR92ygnAg6aXg==","shasum":"d9df23c04407aae5cf507c8ea6ba28830ec8600f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190413072358.tgz","fileCount":741,"unpackedSize":3107570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsY9CCRA9TVsSAnZWagAAS0oP/1eWv89tMK0aVSK6z8DT\nICNTtCqG7SEpjru4QkWW7agwixt8tA1CGZZI+nue2zUYgooPyp7LodfwurlK\nINRzqBpbeE7PN4yBEMNX/KRsuD3+YTuyYJeur6/kTX0f/8oXK+YQY2asbNXa\nwrbJPRjj++Llq7mmLRfIo4pbbKfhyJH6JFHoXK5gYtmMc4ftRWtZHZxUQRS7\nEtB6vD6HFoHRQhta8+66rceEaT95wAiEzfR3rkj0XVXn6nv1ztP/w48fnpX6\nAaCeIPDEDGUjTzTWxAFoz8yKw+eoXIPI1es+Vjfx13malS8Wv+foVMxsVrY4\n/BAMVSfE+mYqfAljOQcdRPqE6ga9UkBQ6YtPN5ftshElvX6r5tEeQwg493ZC\n9KHJ8egfLuMiv/UUeKBa9mATEFJsP+I4yRHjVzBijwLR+uRt5f97jI/yTbJe\nTbxDmo6o/whFFAmYFTLtHLtOddbAbD8Z2IkHfpm3klf/Q6gVBoFchVdE2TYP\nyLwAubBAKPaKSKZEQT76E4Lgh1X9Tgf+MK223Mg5I6Lv7C2/meKeMBoI43tr\nXvdJk3IybGQsis9Y+E2zDOneQwM6yL4AqEhvA0LUwvPKloMkQHgJIdMr/rGP\nC2g1BsD0M5xo9FtE5Y06I9EWjobR/+sCPnXeuJhRwnPuPDEOv75hgek5mM/y\nm+DJ\r\n=5E0i\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvomDIyjbUVA05E7op24h9/it5NP54AyMC1qqNb6MAtgIhAPkHmuZOkYjoVC8aRUkpc7kraewucYW4Dd4M6OshzLEd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190413072358_1555140417851_0.02734778943351679"},"_hasShrinkwrap":false},"1.5.0-master.20190413092902":{"name":"@atomist/sdm","version":"1.5.0-master.20190413092902","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b3bb378d36a301ec6d1966ee2350940bcc7a64a7","_id":"@atomist/sdm@1.5.0-master.20190413092902","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-yi6jVvDrr43xZg94c2tOtK1nTbuDuhOoLTJJcUJqAnzSLQNPYkVxGBp9cn1kfau3rUv6kavR0mDpG0No+9TjYQ==","shasum":"baa674e70fbd872fb1be5a040e5974e478e4417e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190413092902.tgz","fileCount":741,"unpackedSize":3107570,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsaydCRA9TVsSAnZWagAAwWkP/0Tpz1oYKwSpvgKfs58W\niY5YOyBMg/EqzJ7Z2W7h1vEqwSaBFluAluVBUyUbZ6IoTvQc5Pu3h4o7JhrE\nEcEPphzSXj1Q3EJx1YgRfZJVHEWn97LxJaHKBvDzBIEmUe/Zhm3orz1+xnUz\nTQhZRY4SA79s5WO2PdZ/zVrkloFHlV3tcVl0AXRjN9bBHYr02XPMlB2x5nZ2\nf614aUnyMCWg95+XwKspyHIt4jfLdlAkOi05c7C7E67Iip5hKpF7LCsmvrEe\nBPK8TUzJwr0CuTv1gFLfl9dZZtdt+tfSEHT5tp7wM8/Ay2IYNzV7CI7UJj8x\nt3hGM68QfZLb05W3UioP9T32tbfsfyxVIoeAh3nyMC7t6f77sb/xEQzrxHbB\n+A1JuM/qpQYQxxgt8ko+P5PiTpcZs1NVNQR92USFC0FHW2N0BvE5Gsbh3803\ntdiNcqE+v7O+jHnZwxKAJBzvSzNoKPYnvkK+YGL1GJn/Qth3H+NR9TqSClNn\npkLHhb02IVHYGtWdCFsT3MsZvcUro0suNA3C7MTs9akm2ny/68BvT83jN1E/\n78IGnVBTxwSHZtb3KzzeywKndspco0r0aSSyivWrJ6fKJ10o1EK748QsRZSC\njzith37soKLy0T19L0e0RHo1gCVB93zwj6bFm6rGemtBQA0WMiar67ouwEXW\nZbrZ\r\n=wGnq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBy6AYQHUpL3Pl5mhdKjmdMIzTiQ+lfbRIhWc3JhnYdgIhAMHIcMOiPsxUbHTF3IQghzWaABNkJKfPI+N3SFH2zXST"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190413092902_1555147932387_0.8774225298608227"},"_hasShrinkwrap":false},"1.5.0-enclosing-rule.20190414091448":{"name":"@atomist/sdm","version":"1.5.0-enclosing-rule.20190414091448","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"83566d34f6200cf84c9a7d9b8491b9abc1f336b1","_id":"@atomist/sdm@1.5.0-enclosing-rule.20190414091448","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-waikVzINezgx2/yUI/8z7B7JPpcau4eNHihhOWUu9Jv6vs+p+AJIlIr8xvKmgqR7GBmRPAEZp5YU0sh83JGLLQ==","shasum":"e8f5fb393767d8f5f223558a2e650d34afe44e15","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-enclosing-rule.20190414091448.tgz","fileCount":741,"unpackedSize":3107946,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsvq8CRA9TVsSAnZWagAAIoQP/3SYcE2vKH/4+1F9l29p\ntOFHzDj8u6c6nN+VO5ecovfHvplAjgrJJgvMUufaMlg42sOtr56DnpXI8m/t\nnfBoHTxTBP/2p0AsCou444qFAEPWWer8a2/CqBg1oPg64L7OTdFJLqJCYMyK\n0UFe4qiMzIlXklAnbQ7sO8lHpWFvG3sxwMrmqhEYn6r9JERLIoaaC1ipX6pU\nf+ZLwFU74UgCdAGBOn7bWXInDpJOS/YEHBIIcW27rTvlC9Zu3hry7Cvox94q\nFDrIYUhzhlhe17AP1JJDufyO6c3XLzc5ii/KMIGHb9B1MbPKhcLNylxk5yLP\nGpMImVrPdVw5dQfxTrWIGBafIvAZPuJ/c4ORrGlLhWdaCFfRmqwMDbhtDfLX\nBx0UtT3cCVLX6E4QFRqnGkAUASrBbRi9xMkhJX5LKPvdqQPNLAeKwA50nTpV\n3mRKRW4/fi8sLAAgK5XCKToP3KjskYw8rc6YK84OEiwHtK0JqiciqtdT7Q42\nm7UVcwCJJMqK9J3wXcHmiDY9btY2QtqCNIKmNtOQYA2PIy3/4wtpa/qCqGd9\n3oyP6ivgpOv3yoN26WA5ascjYGA0mQsuDX52yULkLtLoVtGfoobTR1IwurUz\n4Q22bGEoA8JilnLH1/nawTRjJFhpRA3jSN9jbqT5fTUrdHUAMj7eJ0YGW2M+\n4723\r\n=8hF0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGKhTLgJgIGMLuuZmP9TALTq/PUwpPZG8N/NpwEXgeYPAiEA/rsDHXzaUtXc0sJ7WuBBYJrGGVas9ES69oo0yfzJlqw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-enclosing-rule.20190414091448_1555233467066_0.7595431955299239"},"_hasShrinkwrap":false},"1.5.0-master.20190414092108":{"name":"@atomist/sdm","version":"1.5.0-master.20190414092108","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b7f037cdbbf373c1d0cd4fc37dd8f4ac4fb85699","_id":"@atomist/sdm@1.5.0-master.20190414092108","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-BX70e7+1v7btI0QlEz7nsSKCUVk7b6IPGlaPkigW06jaLawr3bGHqk2iREprrjEGqSN5ICx3dbItCty6BhvEaQ==","shasum":"79a50bdc63398b92eb842b0e0c22e652a77019eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190414092108.tgz","fileCount":741,"unpackedSize":3108056,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsvw6CRA9TVsSAnZWagAAbKoP/34uKbineXvbAMDO6wqe\n5jUEYoHylB9YQ+ZN2vpfYfKWWo3zyEqE5fbAc+q8SXb6Uv9fcceZ8lAZ1kCu\n8G3VVXgWmYIa3yq/xqwlJBrBj+jQkcUqZlhJOY7FjDDkAijDqMUfgcXSIRke\nDC9Spl376BDY4xIsizSrJhTT8m4s4ujggxA6aNHIENP8aHOHkMwnYvGHhEeZ\nWOPkLkHEBtxWIvyVsQfM/0JuejEIqbVd34RCqhdpj3lYZw7mFFn7YEhLFbBX\n/3iiPYNpKAkDCn3x8VhwxHIkywxKr3OiA5TGImm87HUymPDTb1LtVqeT9DhZ\nfslq2CJQ3Ci8Qu6LEK+jif4OM9U4Vb1e3HJYRktrZpQvh9PsbhGqA3xvKnC2\noVdCGuiubEq4dTBNPFWRjhu/n1wTi+jQQqiC9mzHRBa1HXhTBjNBNKMUoNKW\nVSnoiQ8TnuilhMEy68wZ3QTvaOzcMdQqvHT7KMKrh0vUvgxlAqQ/ke0WUAZY\nMnkIpXkSul6UMpOGju6jiFYur/kMaL+inXuEXlZkShWTG6TYfNGMyALAv7Bb\nxFyTNatQBv5yIkvV4x9lC8OEv8MosHOd6Sa/Z6Fz8GejgShts8ZCVRx4/pFz\nFuX55OYvvLmV7wF47T0pwYb3wMlosuFbM5MJjdqzKNlTpJeR2a3zSmaIkuPs\nXkiB\r\n=Hu70\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICYgxgyUNKfFL6ERuvFLmfsp+b27HrG9t6JLMibiZ/73AiBZ2M3muLdLpgc3pVIWxM/oiuM6DhQgFW0BVcdvbT9/BA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190414092108_1555233849592_0.8280396537504209"},"_hasShrinkwrap":false},"1.5.0-master.20190414092206":{"name":"@atomist/sdm","version":"1.5.0-master.20190414092206","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b7f037cdbbf373c1d0cd4fc37dd8f4ac4fb85699","_id":"@atomist/sdm@1.5.0-master.20190414092206","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-WPBHC7GNAH/fL3EMHSoxUI6GHuhHrL/BF/+ZDa2gnIxjZ12Odxr9ERpC6a//p53TszxBuKg/Oeh7IJqLNVp6Pw==","shasum":"1bf05f8542a755aa89d315cea720d4687bf9d61a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190414092206.tgz","fileCount":741,"unpackedSize":3108056,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcsvxvCRA9TVsSAnZWagAA/+kQAJjtS7ysd3B4fX+FUkXi\npeEEeTf7L2d/HELtzVvdE2QDoomwL4TzAqlvyLwv7Y7OUOQnxyaHBLmdzCPG\nfpUoCFhDanbxaU8P2Uko7qzMyIbiw9+5d465zeFOl/94DwdQGbMsRzMV/RC3\nwjj1u58r4gW1rbVxKNL41zhtISVQr3O+VgAHaxWmaLuio/imXxcEcaZ89a2B\n+DGCrZC8w44E1OPhPG7m4fONgTLrXfQP7JujEBc/iPew65E1WOUarB97A22b\nlXTyp4z5Lc2Hoo7Ic6v4Y1EiulhY5vFSiLKfbSnzEvmJl5gcd8vIlRe7RKux\n9jblCJ0g0F8aYmc7vDh3GhB8tIqTJVoT7NYM0FGMHaMA9nzpZ4tKQtpy45a1\nPsTl/nqwF2nKdij0KeeeTVf3Fk96n62hLcjFAQ+CG5hcUm5drQRJQESpqxS9\nps1gEyLJCXnQHgkkUm4rJKs5b0Q5wECwZ17waWoD1/mNdFoFklDQ2shgt3Im\nR/oPLHvxxIXNIyVenZ+kQz0/DpgkxJJuHfGnaaEqlReMxbgd4pdJky0WFRFt\nh0+R6AGx91XHRURJ3E9jUhxs6dmN6fxnf2exz0dvnNV3zYj161v14rd04l16\nbyE7il1Sn2iZSqoBkyGsYj6lC+rt2ZoJFlWz0Sw9HrCapVox37ric9s9CvE1\n0EsW\r\n=IA+B\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHJMxoUCnzuUH27x1Ky4XSXna0MnvFwVt1fwcD+Epr8mAiB9IE3AykloZyDSsPJp86Ub8tE7k7+jFOQQqff+7IXzjw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190414092206_1555233902366_0.4753427397392074"},"_hasShrinkwrap":false},"1.5.0-master.20190414160706":{"name":"@atomist/sdm","version":"1.5.0-master.20190414160706","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2c40e47c75c31eb17efbd14e65e778926dd8db48","_id":"@atomist/sdm@1.5.0-master.20190414160706","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-dqL8BRa7tQjn45Xl/DLjKKwIy39G6/jcGjIghB5eh+oWVusicGsCohrYRzO9LK4NWfTKejEWKIb5bC3TwdnxeA==","shasum":"beca86c34857ed2d65c5597d61474bd0996aa675","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190414160706.tgz","fileCount":741,"unpackedSize":3108218,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcs1tYCRA9TVsSAnZWagAAUtIP/Rb9c7wbB+4awGrhYNLX\nB9gSZ/u8f5vR4lJ1ZFCmD8h6rc79DeJY43L4DErN7x0mqrXwfpPpgRTBkCtr\ndp115EVdWQvCShGgT96b48o9iz+4otTFTYJpdwDmpvwhFay+kqcMU537LH1g\nWAqWpnvKFO0MTEzlSNtjeI6Jzi+u8qaKOTJduMYCmGMaUFAC53y8lQUzQ8Sh\ncnsN6SQvJ8ATRh5Z7c9CMoAwdhT4CTGLA+HyD2iJA9ATrA3mx7AuEClxNFRl\niAWhvTNhSCuN1PsSLg93OtbiTUSr0H4a3UJ699E1qIOXkBno9GT7Wgygoqvl\n85ZIAu+k2AG8iCEPvMU07DLAGZYC4oyGHksMKI85d8wSaCianfs0Yc2Rz2j6\nT2uUvQwfK8ZlynRAj+t7vMXSzBdFIRphExKuGJJhd7+wo+aVHGF2nR1RSEEf\nuZcYKI7e1qw19shOyWdWIoIReMU19cj7/Dy5aDBOodhEB8kqCrL87a4r1hdz\nDJV26fe0rAxOx6WLJqIWzkg9kSZHWXSOK/s6EpIbYX/XFiDIDPyNXdoqJDyY\nN0kYNRcEEru8VdfGd05aF/Rh1JVviFmINFJ3gwF0pDiadOb6+6xd4jBMh04t\n5fGdfa8Q90eVNBPtNuNs2le2v5jZt7T8fcZUCY8Qgtg6ac/QnUPBsyXRAe2h\nhaUV\r\n=D4gF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBLSBWyrh7+RxfoxrmL2EbXNy+LiQKJKV1Nx5PW91IcDAiBV6yy5Du8/xIGvlpT+3Vf8TyUT5AgPObJxqX+wtB3/sg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190414160706_1555258199953_0.030991001664209428"},"_hasShrinkwrap":false},"1.5.0-master.20190416091757":{"name":"@atomist/sdm","version":"1.5.0-master.20190416091757","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d51517ac927aa9cc5726ece76b99ca83d55b4f4f","_id":"@atomist/sdm@1.5.0-master.20190416091757","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-LRbEgFZnsXIMgme14trZEQ9CGaW84vRc46s4MtKjGO+m+NJSaOyoTbnK4J4NJBoctnvhkRvCmjxQP1kU91RS0g==","shasum":"2fc6571eb2aefde4b017b88f26225b58aa20026a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190416091757.tgz","fileCount":741,"unpackedSize":3108228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJctZ51CRA9TVsSAnZWagAAtpEP/jrzaJ0nrlyXCa7dZ/aT\nCK5HRJzow+bWiezI69hSCN0CUG+pwDXmwDyo4G/SVMWoT9goi80AnrdA+79i\nZGcRyDLTdpyDeJy5ZbS0eKB29pmvRSYt3ihznGPkMASs3KLQitUftIBctIRg\n5PwWDm3mpkmOL7xqU19S/fXU4ZqNvOAO0WGKCUeZGn1OHqrWGQLfuz/MY7Tq\nJdcquYXgGzd5LuY/tcnAHizD6gtWpdpGazt1isl1QDRahS7ogSG5C55WgbEq\npWqrfuo3lQb4YP+P2rW9F10qdXeh3n86sWIVa9/2IP63x4XPcejVz66pUm6u\nMsX3LYA0BEuEKi1Dib7UbPZSgTS45IkIp2JqGw/rqWGfpTIjeo9YOL52epey\neWLu0Ox4/eDR1MXDG56XZdDnWC37BQhnMS07kvaqiFSRHp2VCmFPmPFOeLxI\nfkX2As9BEp2WwI5lBPV3WK2wdDbAWw5shoh1fMKbd2HDj7IarRsIXY1Ggd/Q\nEQAvd3vn85Gn6WtreawyCBf9B7oSkWm6A3S6LpQEfSPziu9IkrAjJjOWgFkA\nf0cGHQjmhj1G1ofLfNOzFe9z3xs6Ap2+A9ARqr0jfFTyI86vlagf6mPWldVK\nBSGY5jAHJ2R39IuiJAHPgRDVD/udfhzIhKBV4uk+YfWUHCJ8D4/cDCdET+Z+\nNUYQ\r\n=J61y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCNzxVlBj+e9pmRepNI3jWkW4kY4Yc26VkBSIkhwIpNRQIhALDnFTnk3uGUJUfLATM0DFnIn1ttC0Te+Ifl08HouowP"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190416091757_1555406452668_0.33483981419472975"},"_hasShrinkwrap":false},"1.5.0-master.20190416173751":{"name":"@atomist/sdm","version":"1.5.0-master.20190416173751","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1532187f6050f4950f6352f3d756d4ce1288e5b0","_id":"@atomist/sdm@1.5.0-master.20190416173751","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-3TpNcaav3lKbmUng3D3pERQGnn69NJsmxu81twG/Cu2CupJa5hREc6cc3LiONyraQ33U//m9hJEOmWwamvuuVQ==","shasum":"e66cec6b585747291d26da9adf10afca787ac613","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190416173751.tgz","fileCount":741,"unpackedSize":3108890,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcthO0CRA9TVsSAnZWagAAnskQAJx1a7VHpm2SSgvV+TX8\nGPSmpDI0ERUjoPC5OiGKool9BTLF41qt7r8COXDvVjydy76q8Gyna2BKQTPG\nevaU/ZYTIID2KpcstfptJ4kGSgsS/ZYwVsRcwVivHlSnvS7RafE6RMenZGZf\nZwPQxKdMmB27d/+Cqfj2lJQsXFkJ450wOab7Vy1qRw/9yKZd8QAduMdbibCH\n4GD33au5Zso33S4C85lXL259FYnudHVPiWlKsDsoUjZzd9j7dz7rVdpC4Akb\nPz3BR3guVRVHgTrzIXv3bGWC/dAC+fMnFZz8ljGkVvRL2uD9YhyvIlsNGP6q\n0+MRQj4aHPVIPvh8+hvzDzoSxuSktrcUnWrxHYk+TLSGqS0KUxYETI3pp99u\nc12LcVqPwcQOs9iBrEyY+Pl/m3/VDVoRWA4uV4HalykdC4NxKd4XMeCPkBhg\nIc06aW/icIGCcDBkG4LmcqxM3vjYwEwkcCQanQa3pWkWEFqrap8K1+ENajuX\nT7p8vIyAHS2mALyRrQ+LLH+5FF/dyybKPOFDpkYys3mCMUz/JoKxKnqxFnDC\n0OhVYLowJX0TtyWa6/PI3OOsHbD9tqtpSKdskRR3Qk8FzFMLG0krcGrztY+L\nf0Bmb3QtLFr3BL1GorRSy/oBwthoTLaBB2bB5dgkeFhBBB8xNQamwaaeheRU\nk67g\r\n=DXvO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGS4rAspsCss0ENJxegzVbyI/p5YWu57AHL9z37YD2J2AiEAtuujUXavPDpnLKJw9dLAhj5d428t1ps60y0WMirrTMI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190416173751_1555436467175_0.08483499340924383"},"_hasShrinkwrap":false},"1.5.0-master.20190416173936":{"name":"@atomist/sdm","version":"1.5.0-master.20190416173936","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1532187f6050f4950f6352f3d756d4ce1288e5b0","_id":"@atomist/sdm@1.5.0-master.20190416173936","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-QM8VLOqoYU6HjuO0dzKuoJP78FLvYpd9KHpYOjxOlGhwfHx9TaY8eB5Jiwx5EL4rcheljSxDJzujGWWoi5iI4g==","shasum":"862f420e522285bb33bb10a57c99dff48112dc0b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190416173936.tgz","fileCount":741,"unpackedSize":3108890,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcthQDCRA9TVsSAnZWagAA/k0P/1Wce64rGP49a5jKyKM+\nhO5m+mU5Eae0p+HK9NXKgUyfM4+KP8TI7urHh9FHZ5uhGzEkYLfDaqT6SgNW\n4yXYAso08A3HsmhtgrlUtw+o6LD6FFjVO98L9PUzw/IuxAVWqaCSUusqjUu7\nnGMIh6jjM83Nl1GZCDZUfumj38s2+2NfMgmPNYxtxoGoj2/PeVzZ6DgWeSnZ\njVrXhA2LjmgSzO4h7uhjVlYcy9o+V1vTKgvqFEUiVqCwlo6U9/S0t2pcM5vc\n4TPwKOSZnEqYfmSqmnR4ygFNyrSLTiTAPpK8lz/IENlrwd5DE5SAjc9bzE8B\n8G0tnjXqMnm6jnu2vigkTj1kRwAqY+5xD1zjJK5qqgxeCx8Kd4cj37fV1p3I\nfLcy5F2BYrTZwHXRMOeonquTVXnKjsRBJWbO8Bra2NMa/R0HyVDeLTFMvvSc\nY3MMlBlo4haGPuNQ9Yafh7/MCzKKWwQsmNNsiNvLYQ62b56cG0n3YfAaiqRm\nXpWyd6ujJzQcNb9gC+rTJmrwZMhR5I3rXBUNtcHdViXcnnkc0oEtoXN4tbju\nXgd5VeQe5sNLc6kb1f/XcBwtYyTuko0VbQBet6laCzT0pACo7JbzJzgFzf4b\nwrDOB5TTfVhiDwgbLEdBzx9CBDFDtn53eEUdNpS5QxEWtTo3JZi0Z4L2ckOy\nz8iu\r\n=y8Sq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCv/ZU6JovZbsQ5L2HIsLAUoDtmglQ0M/tk9KA7Bqi9dAIgC/W8TC22CDrhdKZFSiXy+v9IcdzEWXC6A8nt7xygIXM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190416173936_1555436546416_0.5575405915714928"},"_hasShrinkwrap":false},"1.5.0-master.20190417073800":{"name":"@atomist/sdm","version":"1.5.0-master.20190417073800","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"78041752f6275661258bb9b4d14c6d8bc79928e9","_id":"@atomist/sdm@1.5.0-master.20190417073800","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-gPvTlB6JXNbwswPrZIgRe3WeCRZUnV4b+hDk+GiA/X9Rb4rXOimRWPXo6xof0zB98GwhkE6k3BtEKa2jGiVYMw==","shasum":"18a9a414455d6f99194bd3742626e53164494598","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190417073800.tgz","fileCount":741,"unpackedSize":3108981,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJctth/CRA9TVsSAnZWagAAIosP/0+3houtMCds84crNWXy\nr/0sIwWj1/VoC17tEiyzX8PAbygpvXSnxxTAbykJbh/bJzp8ETTN4LZgNjnh\n2/Hgo0GLNFPhu7pJ/fAnuFWUviMVpgh8pDZd4WR2om5nJA1cw8goHIjZL0gF\n7FFPfNGMlgU8NvCea0nj0/I+1xFpXaCRPRqCyG4LVRFFKCndiDFn4hkr+nb6\nT/EobnJxh90SO+AlxJOgZSdN2hbTVhTDbEQuraZErFQAMghB3Y01LL+5da94\nV8WQtg5D4NZURXNV1znwnOUZAHV5ESeCJx489FKfIb47B1cUuZuRIE8Xf0IT\n3j9TVhb2PXxVYB5fQlZr8eK4LHb4J94izGoT+nrw+MOCgZ8RKzfC+F1dHLhw\n9sDTD/HvbOktEkxbU0bpnPS73nPxEFLVuX4+LkPGzjnI7NFCTmKZbHrZvcKi\nVCt1kLxK3zh7OBNy7Kc+lWcLn2ip/ekUoG5J0WxU4069wFI68wecSCUmll1Y\nIUdjwskKBQ6Z/eSgPvXaGKeRJrZyDle+GctHE5KAyf+P/UIUjdmU8Mog2+2K\n94268P2CXGX+FWlu+zssMIEokwmuba0Y2FG9ka11w7jnW1wlbdoXYHo/hylR\ndGx0I0jkc0bjNyGOOMJFFMBt6y8r7q01F4RwakrNfAarkrlTEYQu9BuVQzyr\nwqgC\r\n=9gyt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICoZoESEIO+ZopT8D2E2CdcqAgGa0yf+WB+0IupBLfAiAiAfZppPbrIMFw0zhQz51/JXYvUNMF5nlWt0E8mB/mbc5Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190417073800_1555486846599_0.4052304628601069"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190418153138":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190418153138","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f6cbbe498acd0036ab0e975328c1b4415661defc","_id":"@atomist/sdm@1.5.0-issue-687.20190418153138","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-3fn9zpjTfUtZTJwP69DQM4lUcdoaYI8bziIxsI8LyxNCQaD/Ai5cWedk1tWfZmx3tDtSTV6HdglPtRIOY4MT9g==","shasum":"a3f6872310578ab28864185fa01c2b63b12a1bf6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190418153138.tgz","fileCount":741,"unpackedSize":3112642,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuJlTCRA9TVsSAnZWagAAxS8P/iBVACB3apvEY0F3eZk2\nz4v5jveFXew2u2G9136hsAq1LutazTW0uBOo5iEFjX9+fMQZ9PnFb4RdIaT+\nBLmB6/6/np2SXvYQHEUhSXk15pGsCRF45pGVF1kFxsRySC0o+iZueDc4bbT/\neRBLWUIS3+ZG6eF4lar3moJDOzuk9ekNQ+q16pvKTYoNggRyR+o/9aU/g56p\n1XcOyR7OAVnJfCZMXJkOVWIevpiqf77wR05CnAdLtzc+lkd1k2uujSyDy/g/\neQboAs9gzCkmVd1J8fINtImP//O4wsn9lObFbFaUMs9N1ojcA/84FGmAICBk\nA8fdq5fEhMwQfSB0RdFjb0S3rmv7qdrVmA8f4nae3UlBC3GZeUsNHIp9Jghw\n3TD2fev8vMKV/6GL07CLpsXHjhlM+/Rwrb1LxqT75ynFXb7Fme7gwoxBLO4e\neMnkou6NCAqd+bQu90Y8jw0+l/pdmVDLMC3mI3wU+2rN64WpdKcEYRjo9QCH\nJe1VsmtYeAIUDo1ckGOZYRlyhjW20rkn8yr4RAcUzP4QzT+c489+JqdNZZwJ\nSGYe6lMbnAeHhTrdyVi5jUGSIAOiRSAe7eCNnnVdzIjV900aI1+sVlSeLndA\nh/5cT2y5IiKOSrrrisiWKZLZE8BYlzSHyZ3rMxv81c7eP6xDndaC5k3NRCda\nXC6N\r\n=xnR3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA7+7aIrhQ5cgFQ0stupXWA73QEgjJnG+biYzhJYgz4tAiEAn94BidvNu+eOwIyDZeW5i/UtfmyHY3coGAVD/3odLLc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190418153138_1555601746205_0.10682645776681188"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190418175340":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190418175340","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"14e06e2801c8010542fa0fcda765ad5da30ab639","_id":"@atomist/sdm@1.5.0-issue-687.20190418175340","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-TDzxLVr5M9arFZMPHjmCr+kTJ2HEPihgNIIPb2x5KAPuYS1bggfMNF3OXtZ3L0g4HlArxY8AXL3UCJIT2UajmQ==","shasum":"ed974cba2da1d11b8c034f367c26f6cce2ecaa09","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190418175340.tgz","fileCount":741,"unpackedSize":3113024,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuLpPCRA9TVsSAnZWagAAtbgQAJibpsNUg8Vg/bmPPusL\nE86raxVjJiiPq8WVtGP75xxRcK4h7YP0oanJ6ymaoWmRKHnbe/UmdWIb9NEn\nFn+50wVvRcIEk0/9dc62cNVssQEgnySgOsnNWclGqa4fxmkw9hvFIVLeMfuS\n5152f82xo7FXOWNVwDYebp18N+/qyVFw+w+Ow3NGlLlD/TvAy8oxN/aSxzWj\n+Y3JfHCp3CD6dFwK7xmbU1mT/WQUdKnzPm9yVgsChxMYcWStu9ZOk+VEW84G\nXTmeoMscWwAOkzHgjB2z/N782k2sIRc+ECQZBFO8zpfl9zaVrb+kcDHM2cth\nXs2EbDFjvNqgscokrhQDwV4wczD0Ek4Ha8C9zGh9S95dfOdYsgGaWCGXbM1T\nhxBVYNO2umBPUF44nm/4b4aNNyQFvACt2VVyrbhdVdKFFMbfcVfYASTnU6gZ\n2cuKiJyPYKLdiQSNKrdzwHkeTxpm8/9WpHdEXSNZCVBFZt0uUCz33T0b7dEJ\n0MKLEBId7zgPftLFDNBp6uNPY3cn25HqCtqhWP5avX60x8hDaz2vK0DjB+zy\nGDYYskVm0NEjr24y3XFg8XhXKLZIT+mj9UEcvbG7WBp+85Xbnnh/wjbn448H\nF5QNV5pH7lciAAFShOQg+Nw3fO3InznB2eLhOeTROlXt677h1d9fkQ7ud6Au\nuqpf\r\n=T26b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDtVZc1XdW6gJaqzeMOBT6cp64kK92CoNjDknOo2ymOigIgZmkyVByqQOgdoRxO1a3sWhoc42Lie0098D7B32/+ljc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190418175340_1555610190743_0.9151773947380584"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190418205513":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190418205513","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"52cf7d6beb3a76f5f22b540c4b29737eb0fb26c4","_id":"@atomist/sdm@1.5.0-issue-687.20190418205513","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-U9MTSVmjwjFOZmr9rQWmfkDaxz0yoOj920qN9gjRWTQ87Xck6tfL+ZO/gCld577XzeEgF4KPEgNk9E8WQXQGHg==","shasum":"bf648eb5f726b4f2cfbe2a3e27c4425ea0660c0f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190418205513.tgz","fileCount":741,"unpackedSize":3113044,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuOTpCRA9TVsSAnZWagAAPkQP/iyj5+Tx5GayUfXF8M1p\nhPQo9DAz28KtXvkWYEvHoZCdKC6I/VIqXIOIbcNbTY+w2K+rjNBsEvmY5OTU\n1kYmlWd/nrdza2NIiYnkqvRPi78XDkpv60ButKSiNPYbLmtOWdU77oFISi94\n/KE6vRrzV++Ao0yJE6m5tu0cEAcSMQ9wZdDVdwpnLHivyGIzDqxf0AqJe4EI\npSXX0JC+wYXKvp4XU5hQUwbZi0+bwqAr4Nlxj8eI0I5MrHLgb1tRX8ag+qvH\n67zTfed/efhzFqJMtDm3n8SQccPPFajuxh2Wa8GzqGFGH7gvxExX8RWxXPuZ\nco51B+lS3hZRQbpYJyWIP7qctLvdq9gY7tDNbX6lKr6Yqb1i/TSpwpzP98vo\nFDUBv7E/Bsiu1yqJtnJUWTh9p11ji0JYKGxAmwN6DMtSAn1pqiyqH5qi7JwO\nPV3kv3ZhVvVpXCEJzL0FRr+v4+4pGdwWrSdjnUNJwiz/I2a5gUsj4HZTQHft\nI7KyvXQAJ4a82whAgXprPSc/R/QI865dQyReq3/3zOGrDrQrXc1bsGR42uaP\nbyHxLj+qtE8E8wOu83CSb9qsixYi0iUYLnQmpe4d5dDG54e0RKiZmDEujogL\n5OpXHWpHpG3RTjYZHZiX6cqcYeZCMPkcgA3CEZpgGLON9rW6zHrYFnZQfnXf\nEr68\r\n=RFST\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDF7rv1ksA4rHNXjrxMirZAbRg/NE+AsxGuEp10/slsJAIgE0dyPBIw85B2z7KCryola0YjQYCgT3jn/QJJB2UOAck="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190418205513_1555621096971_0.4050648927669551"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190419091130":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190419091130","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0a467153fb8562eb36e088884cc68c6a331fd63a","_id":"@atomist/sdm@1.5.0-issue-687.20190419091130","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-pdeVyC4IBsX/Ws35B57R74LZbFlPdFkRD1u8CMGshh6GhgbycCEq+Rv4JDPznUX641DWs0odIdMqy2Nu0EjpDw==","shasum":"c563081eb9206fd46552e67c5d1cb6cf112f80c8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190419091130.tgz","fileCount":741,"unpackedSize":3113128,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuZGgCRA9TVsSAnZWagAAs78P/1o6hQ4LO8jj9QYQsJqi\nNITpEk2HO3RIy3d6jtXM9qVYTLEGF/nDKz4TBvssI/ZowCcpI/3N06JfhI/6\nTeR5WYlUlA6N0Gzk8/IVx8r+tQYhnVj/+wy/QB7bjJgAxQd35Ewd4DijAGXR\nnhq9UJ+j+G3677vIh8XH8rH2pLpmHm+fFhWrMY2m/nEX+gAyVMLRty83sXEc\npXteiuP01pOvoOTU7BuBhFxjCt/0TdYQZb2qstUzk8Pt1fuZASWX/Dv5go5I\nMnF3I5cvztZhveNB6c7ibhz0Vsafm2tYYOmGwsvEiL2mG6TDjvoDQZJciJp9\nyFbaEuk57TP1JX4Z7TWXXyloyl4ojkzJ5mpAVE2d1Wbxg8iBNwAETCiWhAKv\nKBCOBnHO/agDjgA4U98A2vHOH/EBQKrl6NolM8zpQ6HBJulYFFNv1qXMjvoy\nwTMRqa7VLnuBzI8aaG9llz+ILY8pLmsl6FsIIoqmBDrR6IjS/nT/ilcm1EBO\n0BV6VdtustHXfhBmBRPz6fjivlhtEIhBVnDzLywlBAClrwsbnntVJU/fq1bQ\nAqRa3Tv9/KgvolL5Qnx3jkIWaOJloPocqFrZBHU7QXTjU784otxUlGDMA1Gn\n+j2bk0shf2iF0lYrm7HYxeD08HEhnpSoSHt7gCp4n3/tEWPiibNMa1qtB5RU\nBtIG\r\n=aZEq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDP9dYD0Wm8qsKqpCEIiRyU+RpPHb4Wm321sXPN9YeoTAiBbEeoS5Qvm7vdTij9lo5/ZooDJk/NH/1lUZP88sqd7Tw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190419091130_1555665311063_0.6529722217361431"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190419091627":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190419091627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9250dd7a444c3a4b7647d9ee9cbf64e731777b95","_id":"@atomist/sdm@1.5.0-issue-687.20190419091627","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-2BAYGUsGe9nQouy8cMEWzrcHBn90f7D23pXvam31iNfUK9upWPtluSd/TGGHIxCxvKnUlxEf3hIrX8xuCDtw5A==","shasum":"b7316364b976dd5d2b825619ce8765f8dde4fc96","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190419091627.tgz","fileCount":741,"unpackedSize":3113128,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuZLKCRA9TVsSAnZWagAAmD0P/Rp2zy6XF8e3oj0zzg3t\nfxl9ArCau3yYTHxiVbEtJHnvjKzXAYDLtR0gQjsK/Rvzai+EOpCHnVu52buS\nywQbKkilAfVKYS7hE4vO4MP5CyronVzRqYYq9Yt2AcAH/WTGYRat+VDupsjr\ntCGg3qm5tHliS6HG9bajX2ZJ4IbusAWDbMi4kjTob9+vLiIWBahMSFGhyHNr\nAF/RZU28f+cIvfnjQdoDs9MzvE6LBYYVpAkYdVGhi7LTh1fwtStOjXD9nQxe\nAryPdEJdam2K+42TS9ihLcB3pfDw1eIp63mJFMEmnLfW2fUqhH2foDK6Wjwn\nPTV/sU/UeNPpKsI7r3uN2UK5CfDFgGRqtIhhvJngHVusCpt4dIwzPZ9mZ2au\nfLb23ewGqg7blvJuCw6/cpPzKu2Yz6Gqd9IPtp3F9xVRScX1tTFqnsKGsNFq\nhd8ercqUn279dONeGl/TCeKKoaSVTlQvGiZ3aAuCV6d9V9PJYsODMoxOrKsb\n4dHZY+nm/Fqf/CzYcXerA/sZlmSbMHCkgrUErc2LIbjf82sUKB/tH5ZpnaBk\np6tg72BYkDiyIZvAG9hKaPZzBVZLm7YTDkl9mZEwsHL6A41/RsvxvdXFBozH\nDlNk7uT5z+vwRFa3i/NZuzmUt/+qL+strcFp7P9Q5etrgVNAyefHIOVuj4P2\nmvdH\r\n=PFD/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBFcZcJv8SyVCYCt4W8+NgnAFNRgiQW3XDIk+sBCxfUMAiEAx7SQMpFv3uSTVLCCC313BnL3DQadmmxWeyghuUxSYOo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190419091627_1555665609849_0.9353589305390033"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190419092434":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190419092434","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"45b8faa14696fa2343ac327198ef5a6f95403e29","_id":"@atomist/sdm@1.5.0-issue-687.20190419092434","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-K7tQsGW4fYtQwxRqUL4Gz7qazEVL5FMkHSpr30/sWIwRgz521xhFqcVoXVuiLxYU9wm32tCwLUkGgQyB9dfEhg==","shasum":"7c054bbcd387df9d501c131935077ebbb4b95190","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190419092434.tgz","fileCount":741,"unpackedSize":3113134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuZStCRA9TVsSAnZWagAAiqkP/j7tTJAqE//V9TcRul7o\nEh7oxnvfhs3cwBPviFQ3k5knQeE5zz+MHlCe91FMg29qa9AoW75z+E3f3vlP\nbqmpn18KW+Y+v7XzMSENWc+f5IhV30J6ME6NEDbsS3tmvQQqGomI8WYHpQBE\neFv6L+AKJPB4NeTcI4rdZSgHwXCYnXkdYrpeR1R+48Wy4L8hj+FOn1KqNtgE\n1Ir8Et52FVG7116Gz0FxrWGToinIYoAIMB7kfbM1NNf9AwhS1zlZYRK/ecen\nXW/5ocnJFPzHHBFTixYdCU6O7F2c34JDvJAF8PjcC/L24adlp618mY+ruEA2\n5pgdiQ8Nb3llIO5tOpfbsmY2m4mH5/TmYr0uCOJwknSWxK6DIEoSUqwqk7GZ\nTsJmz+fqmlRb1lJjTQ+RJ74xPHcCpex0AgyGfU9+cCGr0vac1ngomAN+pic7\nJfnR7xt1gN7yigp6WnAlnBV1pihvN2La+4+jaQZ/+r0EP60sqQqfBMudzqGg\n9/+/KCvUGZnYsX/tQGXYy14e2t9msIQaNfyfiWFXB+flgkfs2whVBFOKd2HB\nkDBzXgLhqu3lzrMXqMJR9XBWD55qZWk9ResxNYyEipsZMjOYmtUaHLfHyPgK\n+1ValMsBy8+5KkUCYKPvyGY2/ZH6cP3+YDR5VjV1WNW+BjJudITPxvdDRxeA\nGPrr\r\n=mZyg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDbvX9hpWkJHMQMuYJTxu/0ooUwPRYP9/w75917GjCzLAIgTRwsN6bTreadCCXWxUTevwrquhMNc+D5e/Tkn0wMY+Q="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190419092434_1555666092887_0.5406992385650993"},"_hasShrinkwrap":false},"1.5.0-issue-687.20190419093456":{"name":"@atomist/sdm","version":"1.5.0-issue-687.20190419093456","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4560a1d862c63df514c83df2434e9ca820f37317","_id":"@atomist/sdm@1.5.0-issue-687.20190419093456","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-cPxW75lpc5C5qgVjnByW2PdiPfhX5hw7WmDU3HT8cHTBT9eqT87AlEa5MuFb97fkaAIEvPWPjnfL5msJm5vOeQ==","shasum":"a7fa21693f344563df77e7fec424d1a6468143c2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-687.20190419093456.tgz","fileCount":741,"unpackedSize":3113134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcuZcHCRA9TVsSAnZWagAARlkP/1hr37wzMnskzHYQDTZd\niJlM4nS/2xdz1TWdNzMeu4ykCtH5eGVKnVzF2j9Sp7om/ICiSur7CUl6CgN/\nvHv2Ey3TArtbiE8hqVZ17ZVc2DNEQDomOEFmkkz8tpzxC3IFHqPKprrrWf+w\n9uEPT5M1OMFMvnC44oBHSDjjkF+6syXVMDOs4Bpxw5LfWw3wCxzDRpUIz4l6\nLwkqgsVURe94WZ0Bx1uTLqldF8cBI5eSPluaK6CqpcGx564QMvCV2N+jrnDz\nuD1HDOyTKQewtt/KqonMu3BqjIejNzfZXuMrDGrbL3TqjfJeG/aZ47EfgGE1\nCD4FNaLgProTk3kG9NtKVKzY+tqC3ntV9OZWoDsKi97RLU+1p6uyXPTlHUkY\nLf4qVPPoqLPe0tS85JoKzz+jhRHWQTX4q2CRASWIjxJ29M0GqZY4LHBV4Cqv\nZE6LOuIG5UB6Vzn8gaBqrrVlxZ1qtfIA4g7XrX03HNR7JUq0CL+/23yDjHkG\npnd0TvaEQyZKFrTsk/3hRxdRnUiAlp3z15MbXAtpzHCGNBK8JTrLVPEqJRml\nEBriwRrmbYhhQq8pyC4iKZXM9NwnTnTRLGo1RtjSJNtc5fYAsxmCNGvkx7CI\nu5vBahZCCyZs48EHbtc8qvI4cUxaODb98RvTWdpByWk/jqQnq+0YxWE893+p\nEtoR\r\n=MBp1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCTuU462IT4eYVpB3PEfDlkwoBtX7Y5djxjzk7i6C+mKwIhAIDpMMBh2M/QQmyUwramAxQE/y4bhvw35OcynmVRBpKj"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-687.20190419093456_1555666694690_0.9801606461565566"},"_hasShrinkwrap":false},"1.5.0-master.20190419141554":{"name":"@atomist/sdm","version":"1.5.0-master.20190419141554","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4f91c090708f8d42ebdc4af2ac5073b025f8b32c","_id":"@atomist/sdm@1.5.0-master.20190419141554","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-5PHsu8cWDSfMYSp5LNKQbMI0eAkf8TOUYDAKW8EF+ysmEfcu1pB6TFea0yaqXGZooLw5aNcdyh6hrb/WNzr+uQ==","shasum":"10515deef9f96c00fa78a0cba31f25773a4827e6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190419141554.tgz","fileCount":741,"unpackedSize":3114771,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcudj3CRA9TVsSAnZWagAAancP/juRFTr6WDyXztUPOGtE\nBroExPucyRommSpzXlaYk99+9IzNyGUAiPArXuSbIk7YBdGbl7RAF8t7oMhy\nsODLPJglZG8aggZzjKa1s9ShYIZIn0qbXoZ42VfKftutH62K3nrDv/D529eU\nWrKMDmB4/5u5uKW5c0nzjduWrv+9W804y0CdwRMltJYSqhnF1sABNTMJ8uX1\nHFlKuEkkkvhLh0rcvANtUhxLONUADaR48JDqLgaTLuYWMCPC4jnrNyvfYVrR\n0gXAdorODi6XfF3YJXGhxH7zdkB4i7qO2sNMj75bmk1k2zsDUtAnmDQAXQay\nSMDrKEJSCvEBN9o80VpFt8CpGtmhIhl96/jewL9U4Dv8+2pXOftMIUUz8QpC\nXRb+ZcPa+ekKDcExtCZOU0pxRepnS8iuEDyrYVOBZo6xF/STVhVcMUd64DXi\nfuSC2rQeRRuHYh1sembiU59EOhsQdHgFqzz0VlhzUi2zGdUAdo3QguFasG2n\nG6TjmFLZky9jmStYx5Wg6UbFSpQ1O3M81xK9M1iXqP8lDA2V0su6T8DoWQKw\nDIFScGKoHZUzDd6CmLRlBK3gEXkuHOGajSiRC7J0G3ODXTe1q+U5YqaE0wEo\nxkFllmWEMRcFtWHTjhuxDg5qHUE+SCEKa2smhEx6Ybyu0W+FExQGt8TYcf2m\n5UX7\r\n=3oj3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQClIlxcqFhGaB9EIe7ATdFL0zgQwY5HvpANP2yVIeI9qgIgTmU/Jh0Lm2nwBu+NhyiPzQ1KMfekMuEren9YEYmy7ns="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190419141554_1555683574267_0.7018662551454979"},"_hasShrinkwrap":false},"1.5.0-master.20190427145304":{"name":"@atomist/sdm","version":"1.5.0-master.20190427145304","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"84725dcd21f2ea85b74bcff8f434c8ee69679b25","_id":"@atomist/sdm@1.5.0-master.20190427145304","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-2bFZ80D/Uy+HNNqhka2+9Bl/Qly0bKNJeDQ73nDu/VgrK2JTQ793KosQJ35UknMHPOzILPJeXX9M69Ro5pV9VQ==","shasum":"1ef5d7eea79ac84dc88799a2516edd731ec63646","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190427145304.tgz","fileCount":741,"unpackedSize":3115187,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcxG2DCRA9TVsSAnZWagAAMFsP+gOv/yoyiVut6U1M2L89\nti72LrnCYEdodtyMKnvogqsk4goCbSS0AdQkXXbnH9L1QXM9c1+D4plocXWO\nY0ho3+l4sWX3TAEoVR01zTFP+dcZj6+nsYK1/IWd1lzjXA/lM6mZXgt+YBRh\nxP2hiBSm+xhT+cEWEBoR704lEkUZMzcgICZbYv4cZO8QY7KK1h2W+PVU61oL\nJ2KE6Rlg1+0wxpW6CtvkNYj+1LyGbFdnjecTwaiLgq9gSAnj5BROBAvpKaqI\n0NzzNex+FF9vmkqCElbUubxMNzV44X4F1BCtu6tO9Va2ypHDFUBmhevlaERS\nyGamu4x1q077Tjzm8Zg0qxXeGweJ9zVz+6gh1Voya27BV+HAUrIt5Q62HSQb\nOIkrlYMVAAgceF5VZt9ETC/DHOZlF1dx4N1PDjz/HN1q2RlCdV67XKuIxVqr\nuqZUWHpfDDD7PcAOmYECXeHdH0TXsR86jb7vI3sKrqrKFkWF+8/OmfDeFXZS\nTlJtDfsGwJAZWZTKsZ6fXPjkVBx2UMqxyBnN93M/IME4yXaXH4xHw3B4rtIX\nawlhRqcJdA3b19Lj7x69AfJi+y9S0JG/FlqSczNgrm7TRraaj+0/r5ZsXyX1\nlJpcpju9gamHwx4QHVld5I8xTkHdCC+0nGN/dJNziFDtwX+6y8EbRTvA6r2g\nqYZw\r\n=/awX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCjPfr0kAoMoOR86tlq4Xmyk1rkA1CZk5X6wAumtzMuWAIgJ1nrXPxdMZAGD+YxXWSnmMmzcZgpbMWBtqcNhhcG+wo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190427145304_1556376962984_0.6028051711223095"},"_hasShrinkwrap":false},"1.5.0-master.20190427145645":{"name":"@atomist/sdm","version":"1.5.0-master.20190427145645","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"84725dcd21f2ea85b74bcff8f434c8ee69679b25","_id":"@atomist/sdm@1.5.0-master.20190427145645","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-pWmVIfnXjP+X6I1sWiMGSkCd6jaYX6aBdPIZUtafl7Ooe1qgD3eWRVB2lBH9oLeQeCppJDzY8fuEOLUNathYGA==","shasum":"e0c826b1ee2a3c3976696fd7490d48cdbe6e51f9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190427145645.tgz","fileCount":741,"unpackedSize":3115187,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcxG5iCRA9TVsSAnZWagAA5sMP/RsiAo4Y/I4IgDnhrJNq\n1Pq+54HVqYDpJBzqsoFX5RnRIkWiNRqCdTy7YrU6Un/FLtxro82zOA/LPjyp\nzkV4KnVtENLAcunjQgBEVwUM0vQNdgdlohRqvFzu4pfWhMjK6qP+8kUHAShd\nD4/NlGlowwwEAiIquBy3Fbpn4Sl7x3wbvJaLOYcbOjr9PWKg7GSHD9vlKDSW\nu5bW5jMTB5ByCNW7ySEpqX+SazEwz1OXUOgrPQwFsXfDUZDPSAyFrgem0Cp+\n7asOwjk7CyHs3FC918fIp9pBJRLYRpNEGlClVQtEaWb0qUlFeu6j281/E4PC\niPcc/lrK1BX7/Db2x1HExQ5TI7eNhsaPna0OKAzQqwc9vDqqHbIWC1vEjlmB\n2B4UXEhVhJQ7J03N13pCHMLsCLv3zmkL88Y4Kychymi0biqrS3A4EewCM/3i\n7PARBcXIan5O4iGMrewUAeR1IxFTKqMMCTrUuuk1oJmBKSopescBdf9UIPaA\nA5MUVVtTHVWXLUt8ZKf5NoN10cuyaDDWa38flzgJpiD8PhyRRR0dxzpKJ0Hy\nk8g9O2RNGkGVWj8Xwp8QYnNgb27PITOYkLC+0zGqjHp64c3Hzy1TVkvkw1oJ\ncaSXOGMBd7PQbqtTT9Kh/Ogvf9UrxaEQweUgGda9ble9cL3lO86ZVNrs2WY6\nd8LF\r\n=Mmax\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsEqIP/oFsZnEdw0zQfxOC45pK5U7MFkyfjBuE3GN3rAIgK80ykLh5JD7EGbCEyrPgWz86ccT3sJm1nB8w5B9A6FM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190427145645_1556377185063_0.046653488056294545"},"_hasShrinkwrap":false},"1.5.0-master.20190429154414":{"name":"@atomist/sdm","version":"1.5.0-master.20190429154414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6537625be6148308de3af2c728247dd96f745d18","_id":"@atomist/sdm@1.5.0-master.20190429154414","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Qj5GlJ5KNl8PgMh+UhxiIjRYG4Hx1ap2RRpmv73N5ya5Ucnj2ZL2vrVSO5TvEoL0G4UO8A3uJBZSfJbkZIFvyA==","shasum":"185f43251c58ce0565b75452d448c782b8095064","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190429154414.tgz","fileCount":741,"unpackedSize":3115253,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcxxyuCRA9TVsSAnZWagAAbCkP+weXBXhfyCpWsR1ALRb1\nsnGDkombyL/JM1qPWzAG+RFuxs2hq+uI1xInnF1iw25sKlcr6BLNKVyIaDei\nx2n41YICouzAHDvFAac0773MGv4W93UaS+gtz5ibW+g4tBON2hJGOYjO4pPg\nhnWYcahYJ7HW5JUYs5CVDT3hYT8pfmp1Frrg4DIqZEOUzCKdm0COzlakWxAU\npr5154hms3RihLAEJIH5KQ/I7Dlh3FYVdFCi6Wu34DqajRhKlX1ZA8z11YoW\nMye8H9QyA124VnIyPdz3U4s+HXSGaAS69OjZtRnN9KDSVW7tDazvYN8dHdDx\n8MZ98cCgHMrfe/COVfieMKy5pBqbx2iAnAKPlxTERFgUdccW6fy7P5Kz30Jh\nPVdlc6D+GIH323Sx00Witk5UGe0gQAJU83LDJ6fBL1MCf0GrR5ZyDM0aqheB\nnm1kpbZp/e3hH83YRhP+RctLYrfZglPeavLuN4quHPzZcDW9mpNkFWgaUTv9\npEyCyXM2sPu5RSAIqImbKBdfaLms4LSH+505mXTx7pYmnM5A5kzBoYFjoMh3\n8Sp4lm3Gj5VMMQOu37MJTXX2rL7rXHPNVRgo4gnA0BDp8O+0VLoB/rR7KVfJ\nYLyJN+muzV8cAvBTw80wtR7SPEuLSqVcigmIMo+F9xxoNVjVFojsIgT/yAeA\nK3B/\r\n=wLy0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGgeQcns7WQeVeU8EmHPJx8QCBuNAg1XcwQEtUgJjsDiAiBUOhuhiaqGnwbtP9K8jSqxOu2zrbjhnE/S3SCNjcEhUQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190429154414_1556552877786_0.7024514885625026"},"_hasShrinkwrap":false},"1.5.0-master.20190429185458":{"name":"@atomist/sdm","version":"1.5.0-master.20190429185458","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2d7841c26c18434752a9a97a7521ad28696be9f9","_id":"@atomist/sdm@1.5.0-master.20190429185458","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-UyI1Bn4zF9/g2Lu2xZseCiy97hmTISu+sN3PBAYn7LzUycsWiR4xRoB4iSzYPRK5ENy7zymDrSdljIDJXWxFIA==","shasum":"8274a9485652b94cd390fef649e69baefb1cfdc3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190429185458.tgz","fileCount":741,"unpackedSize":3115069,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcx01iCRA9TVsSAnZWagAA1ZAQAJ8um3ODxzA2IYi5fDEV\nWmOCy2Ve54ITnEaQmow/GcAyvd/JwI+Y/UG1EPb2wYuPECBvx24C6d0Yt+cT\nkC8PZQJsRTtMlvytJgCfOZgZAAhZloLQUcKRmjsGv+R4ScOdXuEfA1Udy8KK\nfywvMIwqWhNIqXLaecl/QTj5LlYZyqD5RsDwKaECKalVbYA5J23ZhQL/Zuvm\nytz4mcdIfIc4MYRbP/cmegrH88LEW/X6qDHRQUurZfX/jshg8sRpPbleMIyG\nJST3ASuvdGul1fTpd1kptd7jgiHvTtS3PdHMYzu2f/P81l30aWGcasIHwyZ+\noXYE0FWDP6p1XvSPO1nM8+oqAt0J/IdpHeGPnfeHRd2erX90pIT9XeBVFU+5\neNTvJ0ABwf79Kt9B5w624Hvepm/xRlP4aDrf66LbK9CTSPDosM/2BsIyGoA/\nmFZQHeo8Joa6OOZ3CYDZ5lCAXR9Tmh1fQxgNzvDQV0TnSTe3tqUFMFmw+tsM\noopnAmmjdhnqASu82eyIGpnBLjcbHFyTZYAik4A9fD9ATEys7zBp364KUWcN\ncsD+2Wqf6sceTJ7trVMtx1/qKziy6kxwgCxDTeWmZzUu6SXA2eh4yH/NMwBX\ny9hVr4VdDrQwI/a1esW3ZdQRtM+UsbMOPL6bcGOSMWqc6/6VO/I7hkJeUCem\nKxsR\r\n=ezPx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD84aGupolAQ8aQdyvr7c+6nV3t8A4aO6L9xlH/cevlmwIgU/GyYaA4k9onAXvilrFzPiuqgvD/TKg+Jakj35hrl8E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190429185458_1556565345488_0.13404867030112833"},"_hasShrinkwrap":false},"1.5.0-master.20190429191706":{"name":"@atomist/sdm","version":"1.5.0-master.20190429191706","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d730ad2a3ba36e5330d77b22a15a5e6dfc874a68","_id":"@atomist/sdm@1.5.0-master.20190429191706","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-MS/OQR1sCPwWFKNEdNahsEfESr/tjRX79RYwieiUe4zRXTssaMhcaKcEdeD71HKFtyziM/KHl6F4E1AVbUkfjA==","shasum":"ffd2431602725578546aabaa5260400f1c345d51","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190429191706.tgz","fileCount":741,"unpackedSize":3115069,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcx05UCRA9TVsSAnZWagAAMGQP/3rMEs9hit4dH6wHpQ4m\nXrmT7TnGWIWDtCHKI8X/uIH+W3iFa30OJvbc0FQ32acKvUoUeDHrIC3eh1VJ\n/0zgqgyIprzeqKDOpjjUbAseiVTWsre/XqUF9WuSOuJQEzy/3srmh0IGGk08\nIW1e2b+BG2zTjVOEuFL00gooqITCwSL+2/9pcMTLYFONk8XqYtZlZPl8NB3+\nFTiWc/Quyk6pLY6o5d03zhJtHqVKqf6cqb9u9H7wrmPt+rKdK0fQ/s54sw8l\nVO79Cc4TGU3wZacXfiKNudm8ldyRzJ1ziEsK2SxIpoZdh7ISAaF3m25DKRIG\n615P76RSmpjfK1jIZOz1CDYKKqOaVCBqg4brqVrY9ni6/6J9B4OplI5npCb/\nYp6GIlFB0xD2jqTahas+umtp1MAKxSc+4tbf/7hwrcTte6gIW03JBYbVxAxp\n/4gbgvHk8KIRJNXT4LVr6K+y3lH3sHwoY0Nqo5O+sVUdJU9e/bLJjLrYMcFJ\ndosGIWT1g1AeA1WcbQynu+dfKDMLVy1giLTVLI/AEllmWf+fV0f/AXcOHT/b\nZ9BPvAybLcITadmd66465hCv2uFYnskUp2ZI0kaHqs6Y1Q2ilNyE0xtg/AzO\nX3v/PYXbSl+14Byx/IzRVjvlML+ciZ/R3r+4cnKaenYiPlX4LP7RZKdfDwKG\nlga4\r\n=uwU8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDRA7DUNV9qC0Q+xPFEETob+iecweUvEVbsuOApWD7ClwIhAMHM3cc/mdGdIhkOvgikTO/ad8bibnmtxfan+KIc/PlM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190429191706_1556565588088_0.523241627901798"},"_hasShrinkwrap":false},"1.5.0-master.20190429215139":{"name":"@atomist/sdm","version":"1.5.0-master.20190429215139","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b37661beb9535b7afefed5ee66d7057ccf24f0de","_id":"@atomist/sdm@1.5.0-master.20190429215139","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-BLJo88fOlkkd/vn1+ZdokRwHrWzk+ZKryP85kzDUSkN5Ug/wj5//m2ILKpRDfkn5q114pkpsFcAL7JgxTwfTbw==","shasum":"cceb30907d314f31d7c0e6e59c331062fef73b16","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190429215139.tgz","fileCount":741,"unpackedSize":3199632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcx3KvCRA9TVsSAnZWagAASfYP/R92JO8HEdmHJ5SnR2kQ\nXEEhfJsIsLQuar9u6tFTk7mZYihvfo1QEKD+0aTyTmukB8VrQlL6dmMJWkTJ\nF1p6z7xj88nmXvVvGi/j7Ip0bgsJaCCnRXSYdyT2NzCnY4tEppJ5AXzXn6bw\nYVb0PmqtZ5JC/4dUXrywz9Pt2/7FDeQni3bhLvKL8NDwuUPwbnpuhSAOx+5Z\nGV74e1J+Y/IK2nBBl8kpOtQqjN8Gn0DqyKb9z9T0QsybyxeabwKOU6yWuwpD\nD1kB3Q5YBaY6K1hEtKLR6/ed89HUwS40hgoXTgAsdwSVCdS0r66Wdfge7kKR\nvogH/VRVXf/oPd3NAYf0/sk2d+h6pMct+rgBiiDWet+lzTgTDqPCTNL3A52t\n5IQhl0GrEGeC2eLgqhQS9fqE+NHjYhI/aB1FI/KWubDZggcZhG4cUC8DAQJE\nfeJW8gAGZSwrPPR/Xwjz0L17KeLvSyU3THl3MmineuMI6FAF+LP0MToVowah\nJ7pX6PlQENkKIRnAYC6UCHoLXETSfAkH75lkpgQkql1c6YMR8kBnyVVL5tdK\nbfrbTPJ5BAMXYlzpulLX8vQdzeOgSjZ38s2Eaczrp1/MN9AxZLf/OZ5uf2V+\nRuC+NTk3Tj0sVgBDJhNjqrTG6Tyx7o560iL+Q3QFVf1Kh927vhRguKfVlFKp\nJLfu\r\n=q/JK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF8ySaavdpCWKp01vj7q+46zVBKFkiZHzSzSBQhxj5icAiEArNdqZ3kkYgV0TbnQ3nqBHFFhhklV5lTixwKvfI1viuQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190429215139_1556574894657_0.3008319374412054"},"_hasShrinkwrap":false},"1.5.0-remove-deployer.20190430085219":{"name":"@atomist/sdm","version":"1.5.0-remove-deployer.20190430085219","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c4e16070ad627d748e7f7d490da156ae21ffe617","_id":"@atomist/sdm@1.5.0-remove-deployer.20190430085219","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Ou+A2GVSV6NR54GrBv108N2DggEFqe0jsfTQT8sANKbxLY8CZlZmS/Dg3GxJvCNM+wO/UlP0SzqLXErMGy/UhQ==","shasum":"884d89f6146519791c0c4cb1a96651246d47dfab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-remove-deployer.20190430085219.tgz","fileCount":720,"unpackedSize":3176501,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcyA2UCRA9TVsSAnZWagAAZSMP/3VjRGdgf37dLLrIuuMa\nTDkcTTr+5BKvjuKbiUW8sQXbZ1KoLDwOxQR44BFf/c4rWof9a98NiLbPP3vg\nyGdBwFMONt4PDWMZZkF9J2owazwBJi3VV7vcEq2SC/90aFRb/YaQqaD/94Rk\nKMcnGckqUBcRdFhTJ0lwSCSv2tUcZBzE7QHHcTbxgc5LfjzSwGxxe7JfY9Fo\n0uahjlaLW8nyE8HPRLPutlUPFsQpq59gB6qkcyHGct08poSDKScCRqi7IVJI\nXXTnFm0V/JgNlIGPOHAfDyS5D9LkzAauNYtWC7qJMMdm5pc/Baj7eePbaZvz\nGuCTWfGgm7zXsBNocGg+3Ai4ycN4vE4cUIzFraV+qAJM4Jg4j0LKXtatC/KG\nSANwCLmJD/yT+l9o0HQwfXptMHm5tvnbqkUrfEj/s8ATD3L90Os1R+3XvuT0\n/sERDmuipEaX0BcLN7Z2vLupyGwcx4llp4bZqi0HGlXYUdnE+zf3Ez+bdNyR\n8mhUHNQ5ORX+R6ueG9lwd1TKteYojBAAmYcZykCX5BhKHKwK9Jb3Tz13z+4G\nCpHiFf8qDO0qxSwu1aR5vOAHzOjh2szUnKttejQ8ohZeRCeTZWeHdh3CwkDU\ns0M895IOrf5595zrzW9YiR2q9gbI59/ZljbsHX9jPfnOQdpzDxng9qFXTT5+\n0lhp\r\n=nHdz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCr8Gnf8DJHdN9MvUYkR4siZ30kwdmStFYNDQMp9BX5VgIhAIb7DIJGshfb12nz9EEE5w/+7s2o41UyBteu3kWMv7n8"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-remove-deployer.20190430085219_1556614548028_0.6868045704589514"},"_hasShrinkwrap":false},"1.5.0-master.20190430120612":{"name":"@atomist/sdm","version":"1.5.0-master.20190430120612","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ce77896967ecc0e45120b7251f33dff1cf464649","_id":"@atomist/sdm@1.5.0-master.20190430120612","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-GDRwDMk4c9c77LPa7icOIMUAGLfmpI02pxL9b284zXQhUlm7f62pcJECkFTnwMQNPAoHlPcKT9Kg/1kmrj67nw==","shasum":"480cd7764be6809e9a1ee8305ad62d4f5ebf4eb3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190430120612.tgz","fileCount":741,"unpackedSize":3199379,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcyDr8CRA9TVsSAnZWagAAQOIP/iVqCe/xon8vDa3jBo1K\nKcj6pTRX3/+dtvkjbfo1b0vLUpIeh4bXC6bhh6eCWZD05vzI/huNOKs76TzQ\nEd+iW/Cec/c19dz0MOXq//x3po93V5SCXa3ZrkF7P81h+zh4MknJGORbitB5\nYZcAhFYNMg5aCha/00M+40hPn5/xzBwFS23K47o8awUXCJdgd/lpzvexSCcM\nU3rxi13UN9W5BUxDcvNJ3QJUaIm7E5dAUj4kHxz4762CrLkwaD0116rr94sW\nLqYH6dgYu9xkkl9c/ZR44Dfg20DRYKRIV+9Z4f9Z9gkK7YaImZDaACV3+bSy\nYD1ynPWWhjZthLQGooKZc2iisHnP+R1ZMdrnPJCOrYcReH2L6FD0Ew0n1pKS\nKxBW72KjU9zdX5tfBYdUeyi0/myYJI5zruCER5aKbRtRQ+j0OHjrHTsHjfH2\nydvh04TcIcEuw108mvw9ASxr29ow/J3u3WsHhrREwvHUz/s0TYACFVd3nksu\nlhy8sKwMp+pEHM24ax/6fDjz3DsaY2Smg1cHBd3fApqGE0jYIG4VoQN+Bux+\nG6cqePhgFMZ6v5H9VZThQBN/XbPlDRjBRHlCCVgmrvUpHXTrDdy+jk1C90nL\nnQK7WU03YKLgjvnX1R/dyW7lX7PL8pD0HANChNT7nignijNFhxHNrCD6LSKB\ng5Y/\r\n=OZIp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF+bd9Y5In968pFqRMGB88KALIHx1QQADUj3Fw9L/yhaAiAROqsyG7yJFK0E0isv8Wm8z9GRkStr+KqOaWQO/De/tw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190430120612_1556626171535_0.9551893354434924"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190430120507":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190430120507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"10ea54c8b6ad565f1b46cf7b3de0201d0d227a79","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190430120507","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-JHMfKPREbLB9vk3LXMOF9zDEU+Zy6NHNaxoxRI0HciMLmLT1JcTMIxK14O2j3ggpf0boYeoIgxK8mj1ztyetZg==","shasum":"c3054e55c1b9f479010c5508732a74f2b9aab8a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190430120507.tgz","fileCount":741,"unpackedSize":3200847,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcyDsRCRA9TVsSAnZWagAAoHoP/R7hbzpOnrK6rUrT4ZUU\nsOS8752EY1yhChjGlQ4Qi5qtf6joj4TmnEBqpZu+K4nqYDsXDPLcYjrhmhEf\nKQFbuTTou9ubbqbmoqjukqIb3T/m1FqhUhoamitUcyKf7ORzX9Uoriz/wtw4\n7BbNKj8uyUItCTe9vaLUo5NIoNAwtiGznp9rbytJri22f96EX05CJCPJFibp\niXvODtmoTvFXftSkm30iHGhy/26I0GlE8p+3HEWyEi5DiLLgwDcZ9Opbbv3a\nxVrTabbgY1RV2wvmRjIBItBruLwc43gX4v66IaohYEZ0cOG/E/nCzmRi/fcl\ni2ZYZ1sgbyPOgq0rO8N9nU0Mr936i1qUGIA6ctUgVfftCc0oEXI4efwxJU0Q\noi/49M97KTJRC3WR95LhvSASGBrj0PzBNiZUHh903nFTA1Wsux6/eb4o+rK+\nC752T5zHaVqBfRkiCWaMdzy39lDVsSEerrEwuBtakoGtSnqyUPTj53bTtD2L\neb8GhekByBgxvbOyIuTa5IflaN86JXcrkF60Ur6DHyWW32xL+9J0lK+ewrfB\nygOyWudNTsMlEk9IHqYL6Xdz+OzeGZ3OE+fyYgqvRH/iNYFEJdji6E2/cRyN\n0r6fI4Xnu8W95pKZla5UHKKlAAg88EhpnIFPktShZq6NXwEZGfmorSwZcFhh\nAuM8\r\n=XpWI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFqHlhMoA6UxEQKXTTOtCZjiZTGRV9rGploFvDnwl3/AIhAM1fA7OcdY6kJY21+AdWb2Hc1dhAQUG7u0BkUoMsq7YX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190430120507_1556626193050_0.39879152549757846"},"_hasShrinkwrap":false},"1.5.0-master.20190503092343":{"name":"@atomist/sdm","version":"1.5.0-master.20190503092343","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"327fa405a7fe3844af3fc93c7ba4f3941cf61ea1","_id":"@atomist/sdm@1.5.0-master.20190503092343","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-sGPq4wl3+MTEn6rzy7vna8YO36waSK823KlybEhlN5YQddgXjEkapvNQHK/Ul0itmTkQh1KpjDEOZV4YN4PqjQ==","shasum":"2bdaae7c516fba4a0b7ce4c095c3b6a409dcc8ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190503092343.tgz","fileCount":741,"unpackedSize":3200022,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJczAmaCRA9TVsSAnZWagAAV/cQAKDBx0zLjxxtV3Jlb4pe\nvB+q4+cveA+x9h23dZE3qlWAIFqdK3WUjYtuvRTiIg4fHJHzW5o9KnvEtqv/\nOduRe8Ub1B3ucfMjWuQWj3PI94lldGVaC/IhV1ooRIjb5rf7KvXjIjUBtpSE\nXjATeBv1uEFI2fj7v09v1dY3iuYBzY6aJR0GlvUSuC7O0GihOwGYKlp8v0V5\nk5xRi7zL+e8rt5MKiQDerRAuUMbUE6mDS/pJgRd7XunCYI/du1Fl6KF5toPB\n+ReS7xSNAsP/84Z3wrZdy0SL41Gh9yqWOXiBp5EKVymvTJj6JIdg4Ozd0s2x\nAiGJ1gBBoBGYV00aNBKiwTC6ejcGihKCGHF7h4e9jcWnsjZ1ZV7D8WfMu+BB\nsuTGIkeMMwJLNres7m6l8x32XozXgT0r86npGhiHmMT6MTL47Z7VStMKa8oQ\nIRjGmTznYlS4pZZK9qV/6pMWHyxwNqlaHWBCWEWzXIjsQskKWF9qpirEijsG\nef7g1+ePvi4MB+KkG3nCxVvlba+pdMkPec1kWH7Uh3aLTL87YU6Jz5b3Kord\n4iAZnCm2eiGpfds2MsbFTeISkfhNyRRdI3wN6Bl6hVLb6voSIPdlDcv61G23\nFaXxHhA3/tdMv1Y6b8Fip0HAkC5wfZNNb3b+tSP8YGJZRqRKTjZDv2I95e+6\n/1ae\r\n=BbcB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBcJ6L9BsZNxZhpfpNBOw8y2dclFlKfPVC7Gm3XdOX2mAiEA+yvSoe7JX0CjlMBtvAIXlf1rz3OKQheCUXFCJivIII0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190503092343_1556875673624_0.6402086252270607"},"_hasShrinkwrap":false},"1.5.0-master.20190503092504":{"name":"@atomist/sdm","version":"1.5.0-master.20190503092504","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"327fa405a7fe3844af3fc93c7ba4f3941cf61ea1","_id":"@atomist/sdm@1.5.0-master.20190503092504","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-ytHKK7zBZxW0QwAQNVFWo6dUcW9qx/4weSEJgeBSKeR900J8lRHehB25KAEhWLA4wndwRZk49bb98KPaQH0+bw==","shasum":"3b1edb2d4dfc14cfd452549631a7ff018923e677","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190503092504.tgz","fileCount":741,"unpackedSize":3200022,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJczAm+CRA9TVsSAnZWagAA0SEP/0AjQ99ggnywgflXLSK4\nIxWw73HmVGJSkOZzFpVss/UIVsmdha9D7VtcDsyVfmNzU9vujX/a+nXtTpuF\nOx2V8xa/4uDfGvb154bIX/eFumsiux5AobQd+GEOHFiZghhAGQ1i3pVTsZgi\npcCruvJ9amg4vpjHKisQaly1BaBopzNwj+ZIQHPBzaMb+EySGuu3Gvq6i0+a\nVoO90ZQXHyNREQ/Pa2QMWJRubDsWkU4FUAbtqHHGHU+Rxsq7kYfgZ/PzelQY\ny1jYm5+qi0hXzysS6vOOusOnqiBrkS+PgFva/evq+qPvcOIJOuLYRqGDocZ+\nxVNuJIYEkVo4xcYZdcrT3g0X+LilxjN195TgThD0KTOOQ2E+ubh9kwGLwBnt\nmwFj6xyTQbimCBiBt/msNxSH+MiaEf5PUcX82vCTFjOa24uJM45oGk+2a+sp\nfRPtslV12qMilxam0rfqXftYiEdTfOzAGTgsNJzPEt3bq+pvvw81VE4PXmVS\nzhMa8vQoK+n8aivRmbYbpp4PxkS1Y6w36eYH+YKNDmwal7a9nCqQTQ9y/tDi\nIT3L8JZaqHpociZs4AKs3nQND8v64OI+tasiGlKW/XEE0pvTuJrpX5Fi1keJ\nT5FTISdubuJNi6U0mcyyR0PjbEtCYBKz92eozlsJRtrajYzd1VJu5ktjp2fv\nobIU\r\n=3RDU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCRXzVtqTXkq+U3dA7w4rVV26Zx5rH5UdbjeB9R3ZP1lgIhAKEDZHuMFlvoUB0Y8vwK30XJ8L3uJfuuiMwxv6olRu4z"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190503092504_1556875710233_0.8993524045239194"},"_hasShrinkwrap":false},"1.5.0-master.20190506192615":{"name":"@atomist/sdm","version":"1.5.0-master.20190506192615","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"82b67d28005b39aa34a45b7280d403079e00c769","_id":"@atomist/sdm@1.5.0-master.20190506192615","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-9dLpbCuZZnZlKOMS1P60VwYodKlhy5oEGMkr3ZfnFZW2CQTn1xunDhPBv0M9AOvy0OcUntHxgrLABDD3Qk13+A==","shasum":"a6a6c2fbfbcb1a71a08600591154d4a99360c21d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190506192615.tgz","fileCount":741,"unpackedSize":3200022,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc0IsxCRA9TVsSAnZWagAADscP/3U7RqyH3Ag3QW/cGxYB\n8ehHKLdU+zxqTAVXCFfEd2A7SftU85JLHumeo5IGt1plQ5qh6FYtgIzR143s\n2LKNXHJ/tCOKNShzh5OfUzN97Xi8T3rxcFvmhPtc+lwYsRvKe+Y0imr9XVyi\niuwf7g90b6NaCqUicN87tGxuftOw/GxOm5IxbDpdmc9Si6FhSopDwlz70yTp\nlAxsaFDvSL9PG2DEccsM2NSyWjCA0KN85Gme9sGX0Cr+6kj56qy7pwMT3Oor\nNIQKMZexnKvNFNKUjoWVt9Th0NyHSb8Csf5g00KZL8fPf5SpAJzpUxR1x5MH\nsCV93xVYI5ZXrxC29RmGv8K2AU4UfhT/lVKUj4VdvBfxEKSBB3CSKZCMGem8\nXw3/dSH/HqfWHbDbd17SwAjoydpxM7g3d2hqiwlTaw3/zulSJbvt/nLG5A/X\n7MJarsDs5LkUSWnehA0kcqPxVmv8vfReW7Zsq+kWdpPfDHG58ahqkjSMWlA9\nc3aTqnm1qoHFdYr9pi97lAxz5lBFdRr5TedC73njWrk4NsA47d/PAHTT0Y8F\n6wEJf/hNop4UdcO/kiT68GnjiF4U97IiVL0c98RQj68S0nNAwHD4pEkXz4h0\n204iT3WPmuJhC/CAOdEisFfnEPIUEBbRu56W0Dfj9LmnllBYJhQpCz9sitdn\noglO\r\n=wZJi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDKtM8UBr9ytux6ujMHGF4Jkih161b2TfvXHFYfjkB59AiEAnFIo9jvIJ/LKQVLMeS/arZ5byHBSbQC9cnC69p8j+AE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190506192615_1557170992368_0.5448304062446017"},"_hasShrinkwrap":false},"1.5.0-master.20190508003011":{"name":"@atomist/sdm","version":"1.5.0-master.20190508003011","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"57551878e481166433298e22632cad4d5ef108a9","_id":"@atomist/sdm@1.5.0-master.20190508003011","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-GGvWQtiy9k4cO8x36Wvwu7O1P4czX9vIhN/b2zHHdOPG3JNqahSh7Tlhx3gxxiVVFToxU3rGqdAhUc/EW+ztmA==","shasum":"e3feb510c1e7a8451e3da5ea73b1fd4d7034ce8d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190508003011.tgz","fileCount":741,"unpackedSize":3200825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc0iPACRA9TVsSAnZWagAAFq8P/1GLXmkms9cL/HxlN3j9\nJhSX1nhJy41GpS0fsfNPxy2CTNWos7XR6/ZNZfo7I1SG/jRwsrkHZ2M47qkU\noVfycU9jy+sNZq2yc7pmHk9Hj+IvZaHJIYMoQ+m63wYmhvjV2tix4wVVUh7R\n5FgqUJNDkzQmBTVfSiH3xXagO1CrFP82u+n/pMGGNpZuwDuYwlVD2YFz/MyA\naY6NRM/DitzWRuKGYlrk7UkHosA48C2lVFR35n2mOHfHdfw6bw/nYAVw1X3/\nn87S/yyPWpAbY0ztoytiZOcQOjs08Bog6E3PFjrSVQvh72KAh+j5Nacjd5gm\nqbaLG8YX/VAfqhPVd9AdT+hYnkgLeC+htJ/Ve5wE7A5rrtdmsbjLjxDF0qfw\nJymtRXFxzxMbkyUFoDKNjDCvU6TxSnpLrPiya8z5L54p2DDTM4k1ud2iAzb2\ndqWOATr9VQB08zIem8f0OT2hK7+hibSz5O+mLHak3bnREz84VO2mNiqu9Ywk\nO9b/4hV6Z9hcxkg+cuWnOQW2JsLcVuTo2QXVnPVsSN9TrExOUNJ8OzhXDsjp\nqMTIn4Kt2yMqpWUCWAEuDsNNoxGXDcTc3TW7Td47giGPTGDUkPLJA7y02x6c\nLBBQ4Dn2VQBmkGMePvgKgX80h44EIkAwZZHSss4yJ96OwHSZnMtpFIkQODu1\nJcW3\r\n=JdAd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID5dwGYAJb1xGKVDbAcRlamol6AeckgueqNGuJT3GpxgAiEA5qpv2WRqUiBxqgEMFi3u0twlDmBDw0f2XRpliBa4MiY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190508003011_1557275583072_0.33353496220721524"},"_hasShrinkwrap":false},"1.5.0-master.20190508172907":{"name":"@atomist/sdm","version":"1.5.0-master.20190508172907","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"068d78e4ec12be0fc09503445bf19bf8ecaa6446","_id":"@atomist/sdm@1.5.0-master.20190508172907","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-sJqJ55Kp8Mzt+l1HIU2gb3gTX6b3Veuc79ZDxpJdhIguIE2oYf2u1LH+Mn8cJy8i29DX8EPcuV2xahnZOsgecA==","shasum":"4e92c407e43becf9c2fe40c7ff930bf2d599f39a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190508172907.tgz","fileCount":741,"unpackedSize":3200825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc0xLwCRA9TVsSAnZWagAAUlIP/1AY9IhgJaYnG3oGliw3\ncNoOcGkOxQS/I4emf8kHqVuZe48riI/iermVBIxjGGuOXmPAjWZ3cfjIHNzS\nsdRP0wGtxK0tQuPPOHAJpV/spOkejoA+C9EDf39cTcfLkj4uj0OL3SU4lTrY\n6A44+qe4kdxkQZlJkbcseuvezXTehwJ+nmeeiO+o+QeXvBLHD6p2/LHjBUeF\nY1PsGbvGN8FkYKr5kY6T08qBL2pg4kltzK3+KbQH/d3PXU8EErbg6mjjBsB4\nrnKYlh9XNnsjCJA5AGDynAQeSdZK+WKvN+qFhbawmVHHR+J7C0VeQp7yR5wE\nCMd+Umnv88LeIe8LrBVaiQ2Vw0Lg+0FZddJp8boJxSWXU3VtQjBWUNWA5FmS\n9hoGtTFngThCQoX3SYGhYagpjwDS4MP66N3hKrNSQXHpNHpi9FUvgeNvPhZU\n9DO1eEAjJk7bcjRrUZ5DYPkqx5KyMwmmPnFzGUQMMPKyOfK8wfLvc/rqRWbR\nECpatu0txB/RbUyoj2Ltg8OV10hKRs/gnC9lfOEuE9m3zjK+BLVN3nQLxpx4\nEmLpBAdfp31TY1MORhCPEysJgvwLquT/X+ptDIPpup5fhKlFc7Qc2h34k7LC\nwlVHSkZNZgMEoPqF1LypNfb3rvgTcamGZFpU0wP2n49zr35oiNwzDyqm2NB7\nuJk4\r\n=yeE0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH81wBK1TNnw9W0vZHDotpydqRh/V8D/NN98pbIyaSL0AiAFCvHXf2Alb+Qi2sl6PuzhqpRtwfYo/l8OiswZbkiv1A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190508172907_1557336815947_0.505527405422634"},"_hasShrinkwrap":false},"1.5.0-master.20190511041707":{"name":"@atomist/sdm","version":"1.5.0-master.20190511041707","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f5d64518c34b7107125dd6759daa91cca0770a4","_id":"@atomist/sdm@1.5.0-master.20190511041707","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-3Ai2ZT1znTospOKkZm+oCwQQ2Q8pODfTljfOwvQDn0bU55Vt59wt9hFfHfFqMkKdrwJ4hkinGztw/WmFRm3XFQ==","shasum":"32d520c4b36b55d6aa5f1321db404cc6435bdcb1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190511041707.tgz","fileCount":741,"unpackedSize":3200891,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc1k2VCRA9TVsSAnZWagAAIUwP/090GZANt/ENyzlIsR+w\n6mo9EzukAWwKm0GpOrWoyiMQIGgaZyOsqbW6UoyMnDHOmkL9DialP27E32O1\nVueGQagMt/0nxuTvSwdx9JC5T/ji+qmQwPVlC12skQtem+zS1B6PhwA7DuZM\nWqAjgRAv5TdUDOkgf1S++eN/BUZwNv8UXKcfXi5/4vNeiA1MFmtzEV26Sfkt\nw/ddfrmEeHNBgowdhHf0d8yJxiaMOGKj92ktR+ovvdQbhjifYkZlpKmT3LHr\nm93hHmOi+qYSExwxUCdpevtMC1+EY9JBpVXNO+YWi4TLU4kQysblZYN6Di7/\nNG06lFBdV6eLmuBAsCQbwwNRgCpZZaVMGdRRZ2M+BnIR5rxCybxIEucA/PXf\n6BG6bkO/nYZQpS9Pd6TmdQhxpj/3yluqM+yKCYSEsrLo9FEvNMTkUtZHf+9d\npdgqv7GKvbDmRXc1xA9ZlLmyIeVKvMeNI+6PHjf+k4Ssj6zup3sh3e4S2yab\nqzRZvnN+/VbXw0KtlP8IpPYEWnkdhRfIDi1Pi1HYszUIItA3QFC8K4jZpYjm\nOGt17N7r7qyfvMuh17TfVrwwo1rmCUlfgkTgR0wepSg7TQ06OvMYP7HJSYuT\n4mx8wVWbli9dnbjvn4ILSEwePXLzbv2RTdD2a+wuTOYsKvgxYm9qdwpreKMp\nSxtv\r\n=D+WQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD6kH5MX4wVxXgZlUEkqMSjvszWw/nTcygzmE8aXBlmmAIhAL7SuckVnOsgY2avBg9Az6G+aSVaQTelkqDoGKrlzNWJ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190511041707_1557548436536_0.5798515981634975"},"_hasShrinkwrap":false},"1.5.0-master.20190511145424":{"name":"@atomist/sdm","version":"1.5.0-master.20190511145424","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"522fc12578f5704d5a1179490cae978bf387fb9a","_id":"@atomist/sdm@1.5.0-master.20190511145424","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-V0s3yQDf1/1uGMQjbQzOELZHdLif1nh8qsoPnEeHc+j/P96ex/cNFCVI7BuLAq4Yr1bnPIeMfWJSufY6XRVkVA==","shasum":"1c4611346edf40aff914f43fd5197ce15dff52ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190511145424.tgz","fileCount":741,"unpackedSize":3200963,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc1uLlCRA9TVsSAnZWagAAk2oQAI6AbEqKm73T65CbYpwv\nupH9rDxVgAaUMbC4IM8188i1j3EBuWwEcwVcoWo0x+gdWyME5zKvwlFqOuCk\nXDyKkApmL2iUVCMN7DGUJVGqu7dX/vFfXrGPjoyghi067wWJKtsrQu6APfNY\nfbe+ScUc+Gd0z/FXIKSSrUTbL1hGmEwiKBsnoz2OK2NVNC1NX4/SzErhfKEr\nbkVcDALGdxRrNsYLO1kqsiFNrESjp0jfbJQj9tqtTQS4hRz1KOZJr6aMpZuP\nl654lk8iA3xXgS8NBDzn9tPkLVOWzf/HDA6phmBzL2FD5ZrzxFAtlOHaOse1\nzJXtF8N2xKZznizunJhlswEKVT8OeAgkfxdZWKoazbl1SF9reWchaBHu+5zA\nmFrmGy21l22qk4abhw+cmTReuXHBejXMihY4xiHBYDxYjQpHMbNTlpHTLMtg\nJkP/SzLPJmnBCjkOqNOlmxZl5+zMW9PW/v8JIt/jMIgEaJXP5O693Aklkwgo\n+5gZjZNLxyUxBroIdoKTmAcmFOGy6wm40XZEEr31hDqlLNVYM203aCPus+mG\nbYdPXV4/Qm+hvebbQnGkyoFhhuLdM2ZiME0nMmt3mwhjendBmzSJq4OjBivG\nluf+//NtEx0rtTcd6381O9MDFOSXyAl/AjAGIId8OmVyNqpKKp7VPkv3uO6z\nUbXw\r\n=0Own\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDsNxInjyzi1Ru+cu4ZXv5ipwEbFrSV86vERQq5W8u2qAiB7encRngM7FftKt6jg261O5UGhs54eqM/VyPCfJyf0fw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190511145424_1557586661075_0.42182830483816613"},"_hasShrinkwrap":false},"1.5.0-master.20190511161506":{"name":"@atomist/sdm","version":"1.5.0-master.20190511161506","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4fa07059ac4b5fad94c2014cae4524c5c26d9a16","_id":"@atomist/sdm@1.5.0-master.20190511161506","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-H7L6r51I4kqI3NbTVWw7AunAueIQXNEvYkAKVmEpjh6iBntxRIVDSf1H9V5xreS+FGB6XahcMBIz/YrElx9bwA==","shasum":"a2152edd13abe0c78f6a3531d7eb1e4ea4508d6c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190511161506.tgz","fileCount":741,"unpackedSize":3201045,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc1vWsCRA9TVsSAnZWagAAjjUQAJp2aQ05RhRXQaCnMSRl\nu8Uxy+eUBNDiZNux0dm/MYkX9jLI18oHeN2wJRxL8b7cwvjQvQ5TyV19IVEd\nc2s/+GmtlWnAANUAZynbMN3KZVHEZee/MFeOieInyaydb2aIyWOjV/hAgeJb\n5S/7+9Lr4Q2tYMOe3XP5jud59ECoFsvh4xngOmacav4cDAPNJDVDKWypz7NN\ndPYHtIkDO7W890ibxIHUigCHk03n19D/JY/of1HroZHXrXAyTRS0Pm7m52E7\nUIp5mqR5pypjQQhQuesvCTUs4lRMV79/hFOMGlX1oAVtMXXo7YvSIRV0dsgc\nFIu2M47W2tpqoxEuqQlY4lEkPVA70Xsv9dkadOAavsUAhfoDh+ZW36RiPUXQ\nTIPT/mhv2SNbl+PWPlTCyYn/1qXjgO0UylFfXMyIoFKkRN3O+F5+nmUXDBme\nWoYvbXpbxe4+x/xuT3ubvUr5UqOcIjtPXhW+ze6l/QHXlW83ExZ/w+ouzaij\nWgP6M2YQUfUZ3nXkn3FeDwBOdSaC2Ph9x682MTtYhJuk0d/QMW+jfu8RJguW\nin8DCzPxGQXdPe2+5A7ibm7KtLVc9haBNlFsaGGDEAdOTU3ZS8Lrx0MKOUVN\nLfIN9ps3/5cv9zOMZiBTUpJ/nYpsVlb5/AARMg/YchUC4hGncawVlJU0dcYk\nVhE+\r\n=mnnj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE7jFsdQ9cxNaEtD8FkRgNFQZHnCzAhCG7HwlGtgixzrAiACy2lGq8RNLq4IcZYmlUut0BT+sjfV4EUPQh7ziaMPXg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190511161506_1557591467551_0.023701033395988613"},"_hasShrinkwrap":false},"1.5.0-mono.20190514194057":{"name":"@atomist/sdm","version":"1.5.0-mono.20190514194057","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cf1b0d9d6d8d7ee05f865a1880a173212337728e","_id":"@atomist/sdm@1.5.0-mono.20190514194057","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-4tCcJRqTEzh0smLJ/Lf5x7wKjGNV1XSgSIvvgTpXEnC54/kEsy42gpCDEsPYivX3K22IRCT9QTDbVimFuVEmrw==","shasum":"4e459513f95ea2d98bc2a7e8a415da318e03a7f3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-mono.20190514194057.tgz","fileCount":741,"unpackedSize":3201366,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc2xqZCRA9TVsSAnZWagAAHCoP/3r6nwdDGB7umveGx91/\nEKePShq9ci+ZfKVNSsyfU9JIPlGPXWiWvX7gPIJ/orveTvTfQInhRlC+1vaJ\nJqwxvkE0U4yZ6iTSPRCtYZeZ1aleIioDsb+XBvUvOInKNPBQ0g+KapT6nUlB\nTKTOztikjhmvr2gkqoBaCGyCM6u1j5LTRbrc/sqT49r8v0oa2s4ji4ianbol\nPQPGrzDw+DLUjs6Y6BW2rcAQkmhQTvhjoojyMeKuVQVrvgA6kv9qIwngyghf\npJqjkCy1G+h03M/gymbI+ZpyRZem/utpAcaCsBpS/oymHQwIK+osU0aJy3m9\nVVvx6qEHZ4tAtqGw5PJPJbfxyDbJMUWM5V8N7Mrn16vKD3fEW88DBPLq130H\npDkN3SgqZcUbjFEx8zEO7cUwqEK+JtscBRraVNWNPMSXsudGvg1FsV7onNgQ\nD4VINzPMIJRg8KBjv0agjGEQg/uVKTLZRyOLEzX3ePA7ePPUakdu9YRM/BZ+\nqIs67h7qqejMp4nI4uBFbItE6RmtybvvPF8uxWA6Vtlg+NlcjOpidbAywVOD\nacuZ6f5XuHsbTwd+KyR2KAe6DHuU8JNsrA6zHn3vIH1zqWYY8x9vibNshVbw\nw/GLEighRu8qGC6YrpyXLENouLbb4bp4Gt9RvhG2u+QyNofHQnFxnH6ehZZp\nQos4\r\n=QUBu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBkMZFIFfgpBMmBi5yaMNTN50nSpiQa7Mz2gUPBNHd2QIhAJzAR8K0e77BpHMu0gYNN6OfVYtxMQxWe1HycyGiOhsV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-mono.20190514194057_1557863064405_0.6183402736812549"},"_hasShrinkwrap":false},"1.5.0-master.20190515131109":{"name":"@atomist/sdm","version":"1.5.0-master.20190515131109","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ba6496a0177845f8ef51678a0718eafeea6e8509","_id":"@atomist/sdm@1.5.0-master.20190515131109","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-jT6YLFKxjxSR9r9U+U3dj9CmoAJFuNLB3Jv5CsCztSBCM4f7j9D+CbEkcqvyQDhC7Z2PnhA38v9MPdv0L+GZBg==","shasum":"9caadbe5767073ada6e8bd6210d3568dc5bc1aaa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190515131109.tgz","fileCount":741,"unpackedSize":3201882,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3BDMCRA9TVsSAnZWagAAIQgP/i0qGwWaPnVuSnUDs+2E\n+cE+STLulj5XD4Yo6HPZ1xHGFp6BVQwFKA7mxkxZsbSHYristPd7PZ+mg5pJ\nPIl585KsBjY6/dRS68sNaODeVQ8uORb3HQtB6qkrfqSRUHHq1IaEAwzpqMyC\nkyv/7O2+PBEDymE4Tl4nvOm0EQi893gyC0C1Cz0CqFNHJiPYxnUCx8fGBD9y\nFQXKrcTm1eVC8F0SZofMIhPrYgfBfrs8StbaoR50yZNYkD5L2f4KwCexhzqy\nzYNUQRjJYa7roFBAv3UzrWx3lvVToXqfO7kp1gv2G2PFErupWUr2+VD8UaYW\n4VeH26PBgp8S6ZKiEDSrXrFtE+Gym78H1ImNAA9vflQQKkKj2jvsCMlopmLX\nRkVYA+x3HVPaQgF5z7D8+xMYESgCr4Hpese01lKDfqSDtbG9hCpPlNyX6OSk\nQd2of5vu/CU2qG81i6MHPi00yScc8CLjBZy1vxKqad2odJjYHEZAX5BR+S33\n4nnplxBVsjtF8n4++/H/KESKItCSU/G3t8eC5OfYBB8fJ9VuY274x98a1qsh\nB9jgTbRB9+NU6Qel0k6tPxA5LCgu6K/4CvxMFXpr/nFs3ZOSWfkQDf5PgWPF\nM+7gfvZTTip8wSAklrzHsj7zZDolkDsvC96Gq8jrLs6Ye4yUFlSTiQol2RdJ\nApVi\r\n=jvZk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDqlWI1nvfTpcvOaGnhdaAIJ3tH/TVQ/XG9WjQqGU2+NgIgWh7w2Pvn3beUm0ussSFnvkaurIKTUKoze6K41PNcnvw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190515131109_1557926091009_0.6394567130464026"},"_hasShrinkwrap":false},"1.5.0-master.20190515140745":{"name":"@atomist/sdm","version":"1.5.0-master.20190515140745","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"07ca015bda5b83486e6845ce8d5009374a441aba","_id":"@atomist/sdm@1.5.0-master.20190515140745","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-AHtjG6EimKckQJfOFHO9/Lrw9Ue0hbnbOGKIpdUZL0NcLKB5N1dPbNyxlvVM+p8wW9hpB9qvX4/+HsWB2RgH1A==","shasum":"09d56b1aee3093afe34ec32a953af9f00cc1f7fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190515140745.tgz","fileCount":741,"unpackedSize":3201880,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3B4DCRA9TVsSAnZWagAAfJkQAJQrFBk2kUWKKNGDaSn9\nizkxyXChbIPVnqYI9N9A5d8rqChBdkpUJWwkVS4kQ7XMmgAVi8EcWFnAHA0b\n62SESZhaIXzP4PF997uc/SF5WFxOoGJqJmtKbCpLuYZYT6XmJDqdWCtx4BGF\nWLINv6sYD2URqb+fhkfrsVNHqlnbPBE687OvLbwFHbCiWOtljFJMvi1OB0TH\nOKRCEcS7o8qZtC0SdZLNKzmwAiIgAzpUT8T9xBQse/yL7+1Vp5MOFNNgxmqX\nK2vSs/VMT5cDMk4QIJ5nhtf+22/jYgy8NmpcAdMeOTm+Lh8BxOZ6BYSSkeO6\ngJkgStxXNw56K+OTurB/MXgEy0pXadBsjYXtR39zQ09kGw7Ep8TWfBPXFCVX\nQemJQ25w3rwmRXxzrY3r42Lu6EyIB3WXrqRwKYlVNakvW0ZDkuRSRBIGXrX9\nngk+2Qn2jnMU+io9a6W+/0+K3QvZtJCOAvB+mTdk+PKiH/IJ3KYsZgLmdUFf\nQM604pop5M04mb1c65r6x3R5omZr2vQyNxKVOLmUNUPc5fX8PXSnxai+3bLl\n3eef0rcACy2hgNTc+ZxQx9AyrgvYVkCjwRtnnsIZ3rh3ahEtddrI/G3yvzrI\nXtUk3khen/jHqka+FM58A2cS1K2dd6shPd3izTZoDT9tShHrR9A8j+yG6zS8\n+2Ay\r\n=Z0GD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCkPWf47ziXORTv4/XMkMYmBu21mO/0D5D6amQ2qFyMXgIhAJcRj8nKF81WpNTcNo4bFxEEaNGnoIs9/er2+UQazMiO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190515140745_1557929474371_0.27753863694644987"},"_hasShrinkwrap":false},"1.5.0-master.20190515212527":{"name":"@atomist/sdm","version":"1.5.0-master.20190515212527","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"db5cca029414f595e795732364b3e4626f1e15b5","_id":"@atomist/sdm@1.5.0-master.20190515212527","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-gze0LNLlXRgEDso8rZn6WmHjHNvVGiZskcwhHoabWHbiyj/FbOU4C0u+dy2401NXUwHOgmUOd7XkTaHDly1h1g==","shasum":"49d150324610cc9d7f26cda0e178ac00d28158ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190515212527.tgz","fileCount":744,"unpackedSize":3092714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3ITUCRA9TVsSAnZWagAAZBcP/ReOfkwGpa678B8D95/4\nyMbO00wKJXJyGLKzcx6Hzs9EdJqoNg4vxKw4r0ksqmtqbxqYxIduP6fiAiWo\nprbEtlOCgY/6osEYFh9WzyRHByYoT8i+zsI/gVg47NaJJDnIEnXxgliazQig\ncbgSwXxakrnL0dR5WxRVnB2rw1pqGZcZi3pUwCk50L9094KcMQ0dEG8hYC9l\nA8ewh2b5qIeCrEhFo6rgVY0sK/svM38lMtsKUAUrTNcZwZh2BQFWE3a6aWeu\nVoF3a1Hqhqag3rpw8+zo51sanBsnEj3NuFbywY26hOYHGTlDKFvDQGQB6ySY\nj2OuryIGoNLN6XdwXaiyrH65sjWzuIfRttbJFpNrEDO0ivMG/xlXPIBVFRi2\nwUFxfhz+8qadrijzZdUG0bGYHR8Qu/Hdv5Z9fUgaVfzwaogwUcHbyr5ciAkj\nJavn31TsLreINN0ir6EDctT/y/r2Ljie4gv16+M41X+KyZhGpoCPtPR51s/K\n6dFrOLVxQ8eCNYe7Ow3zIeZmyMLmbMtsltte9qtghKEpsg2mIzMWqCiiWRYJ\n7inW372HQ6f21U1xfuH/e4mkMJk9p85XEliGbuVuooui+B1CZMLWlZVNfpEb\nuW2zylYQvhmPWgSQJLm4TJUXgdjAN9/H8k9xc7xWKlBRtxjVmrjvE00jLoPN\n2wwP\r\n=3DqY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHSmP9Mna7bSYsm59j9S+axCOJScKyfV3cJFBswXd3g1AiEAsy4zj/UhF36CKNfqeK+WANP3Zd1lX9z8F3g4t7jkEKs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190515212527_1557955795191_0.5562411529207489"},"_hasShrinkwrap":false},"1.5.0-master.20190515212733":{"name":"@atomist/sdm","version":"1.5.0-master.20190515212733","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"db5cca029414f595e795732364b3e4626f1e15b5","_id":"@atomist/sdm@1.5.0-master.20190515212733","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-A61dT/aY6yS0xHtMCE9Qugqqu0ApiD4rUirT5MSQjYk3Sj5MKhrlkk0l5cIkg3NpZwpHHkeDooUoHmBZf0h2KA==","shasum":"21fe8d097b44a14725a247e9cec7ef60253c9ce5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190515212733.tgz","fileCount":744,"unpackedSize":3092714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3IUbCRA9TVsSAnZWagAAlaMP/0NM39Zv8E5AKkGWLji8\nL0FMr4WJnURAbDaMWJRY+v3pUXaMG4N4fgJUbLvr2EYtIGwU35Ms/42hynkH\nKePqf/a2lc9A36UbxiiY1DV7XwrmClUJHlazWxnge6m/xtJS2KLdHPb6Y+RW\njreEUhANTb4wK9ADpgE66N186hhEEmZvLW5S/D80sFIMTUAtdaLWx23+N0py\nbCih54K2elMO32MnOD5bvZqzTs6NhVMezwMvw1Ti5X4RgZQKsCh4tO2M3MHS\ncYX72Df04iJ46f2g+/hL/gExFV4eBlabhMdzkSwigBGetRwJkPryEnLXWpcm\nQmBlRWesj3YD6trH6gzPqSoL6phEYq2cCJc9Dxa35MZmA/N/cevQVuF2JmKs\nM1L3ll4+r1JQR45eeWkbEPYI7twd5q44zaOPh5l2TTVkAdTQCHX3GT/qjg2T\nw98RMK+Gb7vMwvGxN1rsLIwJXfXh29X1J2Seg8usASPyywCTznDhjmYXSzrj\ntkzW8v4gwR/QYAd70qINaXwX29uxcYmJ2vij+0uEBKG40vB3HVAGcK+RGukD\neHR19gX+HJW1YNU/0ota4nak0lcFi/F1ubJiZ8vYhp0W5DM88ED8LX/lzJVu\nz4zq90kbqIaNEyg4pbx0tLMy8xj6+hGSeHczz9OnpWYDWAq2vveWUYG7jeXJ\ncYLv\r\n=ZSOJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGi+r9ziHE4yfSfJ2hq/P/Y8yYefUvpmpAVBt6MfVlIUAiA2i98Qp4p2Z66wTlNgRhFHMv/orADebLc9IqiOlBykAA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190515212733_1557955866812_0.48302098346504985"},"_hasShrinkwrap":false},"1.5.0-master.20190516102822":{"name":"@atomist/sdm","version":"1.5.0-master.20190516102822","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d49b25612ed51f66c1b0471b86fb5840291e3a58","_id":"@atomist/sdm@1.5.0-master.20190516102822","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-zUgeL0FvbUsdLNKzdp9cNCJCtCXKJAQ5Tx1410MFTUDcNQ4LPyQ9O+U1M+SKrQ8HUwNQVo4sF/pJtQHBMhaULg==","shasum":"3aaca3be799844fda216e3e049dd97dd73defb09","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190516102822.tgz","fileCount":744,"unpackedSize":3092714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3Tv9CRA9TVsSAnZWagAA010P/1KyBTqNBur9oH07BR4h\n3Y2N0dQSUXUwOjFF6yCX+oukaZARooPx+33VSINMzJRupfEtQiPNdYtO8dkM\nVCKrVbduUX15Ii492XbWgW8eOKXEJz3mIj74E051tdhHEWYpYeXeTF7PSdUB\nZVPx1LOleZmpaQgtiwY9di33bNvaeIZ5j3db0v6xEvnItNVVrWniECo8lrF+\n87IlG1mOc5GFuYphsCUQPBbz7vo5EcpjxV5pfHI4FeeLRAEHyyMz5CIJhHN5\nVxjZkzyneUpmflVBd2QpT5chBXjfKXY5EGYM+1G4tVj/iEkFPKjpq6fCOWtx\nrecxVQxSwY2wQB101aXn80MaJDPZ74vp+QdLbTSni1EbdtbMR6RPANOIxc0h\nr2Yfz4EI1Ic99T4/5dLaNDCr/iH7q2TZtb1hmVtr3Xj8A3AHUZBMW/QA3XOY\nb0kuv8epxzQnJSEJyaj/6Gs/HBYAe8NpD+XE9cf4Cimq1XNM7c2VUxh06Y3B\nH7cp4H80ZPJJgOPcvMSN4xr8uwvQwr4M/Hz//VPFnEdzLXbxVW3Q7MbOCUt9\n4PgtuFiF1dYyRzc5hD4OYtXGbsQkgcHbENg5Co8XQmISJDYYpT7tlCHQGfbK\n+tOSXroY4dA4VUejT4iZOSYduTOpIGyqAlZCTi9Ps8z9a2eNvZX65VQ1aNPJ\n/sZf\r\n=UIOb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFaBz9x7iu5mx4gihIZ1Kr+N0xlG/jLZv33J6JgpdjdBAiB14TWZoPBax8PH+9DFxZ+t987elwH185vW/v6LAOlZCw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190516102822_1558002684845_0.1943236784395892"},"_hasShrinkwrap":false},"1.5.0-master.20190516103533":{"name":"@atomist/sdm","version":"1.5.0-master.20190516103533","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd1f6984ceda5954967434463d0456c423cef832","_id":"@atomist/sdm@1.5.0-master.20190516103533","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-v/ywJkLRTWuhw1KQt3YwjrRQh8ax33Rpe/8OQjSACptDJxWQ+Q+uy+PLVmwJYH26bnWZaqVHU4zcRjRuYOPp7w==","shasum":"1cc8a9e73e47305205dbffc6f1f809e5e6772738","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190516103533.tgz","fileCount":744,"unpackedSize":3092714,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3T2gCRA9TVsSAnZWagAAXiEQAIJCfLcrBXfpMYVZ8StT\nJYyARLmd1uCullz4ZE//1rxvOHovVH0pSUqZVdR2g9LrCpEJ3DOP+VQxM1Gd\npYmD2X3BdMX/tW6A+MonXqILkZWAGgKex2NBA4HIBTT8ISLGcPCf/KeAp9y7\nvF9zf2NX+IFZGGaeHmUyBJlP3tD49Rwf/ZExlVW1+n2NeTQByoimiAInGbka\nhPReWnCLc5eFUoBIzY1u7SzcDEzbCKiNZ2k5GKGjJI1u0i01JhJHQgTriFz/\nph99p1RRPCHJAVVz2BeKB72mdDgsVeWukI0xycpdk7PNSJ52zfEzhaThoJpZ\n8PuKMoZl4P8UXd0Vvdn9kihS6n0764qmmTQ+bXC5C9FTEXC7Pd+muZSNCXnq\n/dCCuBwv5MyrjAQ2IlAWPtmuckVG6k4M0z8S8djF4+0vYaUGICLSE1bUfv4w\nbso9A/o+8VQLEn2GbFhH686xj8i27JoSpVlk4j9U4jHcoO3iYzdJ7YgPKbJN\n2D7T/wkA0R6u6FHQXZgXxx6zVEB/N7BhkW0mVtJ98Rj0OX9ZQZT2XugoyvFS\nwqKkGQbMkDmaFTl62Uzrc1SGTXfRpjPOXJKVfY9iBh/IzXcvq1RG/Kt3Oa3z\nm5hTkD/Y3Im0qh4SaSrtnw9w5o75kO10yYoCe3WRC2G0CyZ47b7KoY/w+s8o\nY7oG\r\n=7TGb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAdVDQzHOZxvkydJ0xbrT/uRHHEAdGE5Q3eYV6oDL0aBAiAakqceFJ0M8vQDJiwVjl3ofP4rGgrZipMWamhHu7qTIA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190516103533_1558003103442_0.5026218899050396"},"_hasShrinkwrap":false},"1.5.0-master.20190517174921":{"name":"@atomist/sdm","version":"1.5.0-master.20190517174921","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"92044d20c667da31cc2b87755b32a223fed640fa","_id":"@atomist/sdm@1.5.0-master.20190517174921","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-h3xskTubgILVxPVm3ZveJ6++4+yWLJtxpqEoB6FuXLOvgpvP5kdCioLTHbMkHNOgg7xcBWP8XhtqspWR3VdnJQ==","shasum":"f4a8fa284ffd5ef93e75a2c792ce76a144d921fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190517174921.tgz","fileCount":744,"unpackedSize":3092643,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc3vS+CRA9TVsSAnZWagAA+dUP/2//6GRX59qc7NPwD2V+\niYCEGG2kuGPzLS5xp118nlhWI1W2zfbmGbkzhkNcCQAeDVwVuW/JtobVqph0\n+U/XNQzspKmn4nO5+qS6x0uuATgS1ToHJquX43hZszDDz1UyKc8esvhYPFhD\nFQTfi7NkkNi0GvRd+GNu6YMtRjaU3T5I+p3uOAyHHr+lj8ypj6Qad5jj7yxC\nd1/20xEaVgUSJ6eA5bepRusrSYtejJQALWnkLAYGTJL2uGWIpB0YDIoUz53Q\nuqoBwg9l8FZyQmdX2zYFzaDifpK9FG+DeAMzEhVmWm1X6gm+96tfyCk/2+LD\nHORhm80fcrX7mVNpGB3FEmHw1/H/8IvHafbGS2M53iuuVbToqRfrYX2aztXJ\nICNsiH3m1FjvvDx6c/PqTkNDxXH6tPgiwvK+fIRg6sWbbLpg0YVQoyVImexw\n7Io8KMvpUIyNsk1aOqzsLEmF9UlUkkypy/QLl34qnYP58QyROSFub1XlFdse\nKaX8CS8h2fMEVhWnPOB4TjULwkxh5dKhagrnEcYTQ1jcWa22zR9sLfK7JD/6\nIXmLSwamm3cia/MYy6EEJbRnfsAvey7HqEua20zVnPULPQPsJmd1+rm5+Q+8\nPsUcHKV2bfcbw8EEO0F3G9TDDtCwCq1zudAq5i3EFvIP+yaeMGKXM9pvYEFk\nAjhv\r\n=cxsr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCi1vi8eUC9lAuJhbTD4vO2SPW6aHsK0SzWB41Fp9tXfQIhAPbEFONbEVDMj08tUTFIf3EWn5IQSSpZtUbovc2kIDMX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190517174921_1558115517892_0.9494425712126267"},"_hasShrinkwrap":false},"1.5.0-more-progresslog.20190520124626":{"name":"@atomist/sdm","version":"1.5.0-more-progresslog.20190520124626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0dbd8e898f9240600d511edb9a8950f6e6c103bd","_id":"@atomist/sdm@1.5.0-more-progresslog.20190520124626","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-XJTnq+wtiT2u8aOl5BVWybzdymjbCW3wGMe85V9Mb3RFKyv7EGMFoAcF+ZCCnN8y91uOiuNTU9xwLUGexwuA1w==","shasum":"a0df7ecc915f53c143d8ce904459dab274cd4d77","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-more-progresslog.20190520124626.tgz","fileCount":744,"unpackedSize":3206749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4qJcCRA9TVsSAnZWagAANeUQAKQ+5dg14yFqgsN6sEBK\nAEfJXJZ4Gidsq56s2eGKcWmXUW+wE5SIoRkg23q3KGXS5FSkdMW4BjpK6xw1\nskKsAlXphTDzBSDUzph1ExXPu6HgfKa1l95/+320c9u+L0AMPjil4kP+iQPI\nDRduGYZ/HEvZezs+stoAPknuFPcTjHrLJm1X4Jn4pOfsQkeRIHFgt2Vpq3Tg\n40ZZ/KH3L+PBiAhtCVss8ZJvBnxcI7KyUd2KBfxbmpKhLWbvKStgnoidwtqJ\noo9LHVhQSq5fy79Rs73lmHb4pBgTeAXq1b8SkgpNWNLY3nZF4ZpjxNtwWxDj\nu7dlcNX1KC3Ksp2figLPJ/egZkghLWxQTT87JNqOoHX33RKVaC1Opl8B/WGA\nysfI78USaoEVhovdZuM1XeIpsAxQd50o6zcL/PiD6S4bC4BDXjJLRvgUvmWW\nAz4SyOgbpiDAFCJazAjD34ZzVMARcgUwDeEducSiFg8x3dPRJY9hJ847uSix\nxwGmSrYXIZRFkPG4++pYKwPECxPiejYI7Ig4Sy94C0plPpQjH+jus1uVQMN0\nkEqwYfbISps+T4Sby6T3NB+DiEqnOQoQ7d4Tc9c1QnQZHCaqz4PTKheaKoW3\n+3UN6nhk53Siaqcjo6QnJgbj8rmT/4fcdSG7Mj41hwShMiqRc8xIr8GZjF+U\n/mP+\r\n=CJdN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEYmyCfo3sbZeR/ryMisIPQLmat9B+rWRqeRXoxHJEvKAiEA5ob18qSOzjFyBH8Z7AgKYcvOGedu3SKqV/NahJrsuZo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-more-progresslog.20190520124626_1558356571389_0.8620767326445977"},"_hasShrinkwrap":false},"1.5.0-issue-728.20190520133606":{"name":"@atomist/sdm","version":"1.5.0-issue-728.20190520133606","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e6fd17862e454ee3b9eb8ec3b0e974e580ff45d8","_id":"@atomist/sdm@1.5.0-issue-728.20190520133606","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-tVhqm6gKKqjE05q6NlcvDXTFwGwcwYFF2mMnATlmb4vpQRBxvXByXUZzIfx0w/Z7yTFoPHoQw0WBpLDuvwv/Ig==","shasum":"d229109c6919301972ce5a7cf8b96e6b62affe95","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-728.20190520133606.tgz","fileCount":744,"unpackedSize":3206498,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4q5xCRA9TVsSAnZWagAAVOwP+QELnfuYF2GJmqZ2M2OP\nJ6kmshX58+GT+3lkIBp1IZNeAcHZX1UwuhdSSsld8noef4JmzHkTTejyqZMM\nrU/8g8Aj7FW8GOU6ucorUjrIdb1LsPT4wNdkpjL+yIm3Yc+T5czBhmAV0idE\nZXwNFgwAJS7WDpzjGcmBcjByxBPiV8KH406pIEAJKmhn380smywbJk2WWCFS\nyIIdbo2LBYSqrBmeCqYsjtwrc35CtsBkfHCls02d0Gww86LsxArIC27L3NyZ\n7rA0dXscsw0PBzrJiEMrjnfEX5/g0TWykI8kLareRbYipUu87GQTfkvfH35d\nsn1d0bohoY/9BqqfsIA60Q2XY3FOPdwMHC3Y7mcSyOi2bKyJ91ru2DByeSpU\nVkQgepjzIS1/Bg78ZkuN9jdj7au3NfptzWrrDjzx1Q4LnhzVkz+Gx4M+aDeH\nnHGmCaf1huDMZw5Pai0EseHsldXUbCTlArIn1s6ujdA1S86ag6fMRb830Mmc\n9YJgnLPuwLTwXuGRo6FuMFSRh/di3+12XLPbfgcsY10GbxrAZUm7DNJprSJS\nIGtlmGi5KTg0CROzIqbt2G3A1eD0rmNkAWhc6Oz0bmIJfZUae65y/ewnb1HG\nTIS+QboVIZoUdMZaBWxhZ0m6QzUFNVbd/qz6yG8eW3YljvDcJbGvSKyd8K6j\nlvTp\r\n=gcM8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDZRWvM2SIwyKUhxmu/IQCRqHyAfoAWH37XzlgSYbOHMAiEA/vTXPVXAgmXZotPon6SLVthlDbwF3gvz3pSNMDJSn9w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-728.20190520133606_1558359663911_0.8026347283500384"},"_hasShrinkwrap":false},"1.5.0-deprecate-deployer.20190520141744":{"name":"@atomist/sdm","version":"1.5.0-deprecate-deployer.20190520141744","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"529f4223b4a8569747b271218eb6e3551df1bdc8","_id":"@atomist/sdm@1.5.0-deprecate-deployer.20190520141744","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-A31kRhSLthtGsuBx6qTv9wDgqCrb0F/v3hCF430iE/vIYoSQkfajO8JOpyvT+Ggsh0yALuzFSxUZJQTiTjgCJw==","shasum":"36f66bdb040e2d0b580be0cda3a3d8fcd468c048","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-deprecate-deployer.20190520141744.tgz","fileCount":744,"unpackedSize":3093878,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4rfRCRA9TVsSAnZWagAAsVYQAIrjvzj+JOw+K+o6R00Q\n2MRRfeBD6yVEvwC1c5HwSt2zizUmNHf62kl7YTqchJ02JYyeVU+FP0vNAS3K\nGLphLQwLmuAgxqvbNJ+rD5c38bu/9FYUEb7VngvWgy4v+QcJS0WYbyPN1i2V\njrgvqzUrKbUu5X7/lyuN4jJ8+kU6oV5EiiAg4RnkRmuFtzmaoa2XYtuD3JbU\nJ+B0CKuMjYvvLvIg7Cn0jzl5pjYIyL+vsz2AEKhRcCAlbROU/IBNJtOA0TjX\n14fVTmjBbsbAdPLED17tnCiIhDpiWIu8NsFsdbzEjds1vZGUpx3k5eItniAL\ncCpm83jOO9Gfdl0U1m4zQaWSPDAWlSB9kffS7RLpPDvoVXoEqMXVB1LUyyYR\nqX6acXP7p8SOTwloF4j3Q0ndj5ar2lpbvsldQFonzWjsN5VDuWkVTxJBeI1g\nSmu5yxmCk2bbFkZptGBFjvwBQRSAs1Tsuc2pjYmxugQl/r9idTCBbN2OZwPg\nOSh80uFHt5pnTgCAILGdWl5RBZRGACHXbr3DBzo5bbHQnOZBcODhsJfOJlkj\n2C0U5oySyurq6TpPPpAWzPngJt7DRKb/1Ec5iLxOGn4d7czgXgFONtIz/Zje\nDbXllNmjQ1+jD71E7RHwZw6MZgACGj5SqTpo0YLiUbHFlchWqz4/gL1eBj2o\n5sLo\r\n=/OSe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFz2ys4ZqsRpzs6MNu/ulzTsVsM/uPUi22SmxrOI/rfGAiBhAMnKse2EnxvLadWnwT3is7bzD1qcQmF6/le094y1/g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-deprecate-deployer.20190520141744_1558362064362_0.5432640338915271"},"_hasShrinkwrap":false},"1.5.0-issue-728.20190520143009":{"name":"@atomist/sdm","version":"1.5.0-issue-728.20190520143009","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e9f966f3f94db37af2c2b559c1f748c0ba7fd4a","_id":"@atomist/sdm@1.5.0-issue-728.20190520143009","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-0V/BpEhSr0nIrgFhfyYOZtPTMSJqj/4KlFh13Lqa59oK4/1mwIYRuOeZrMObG1kKDHTFRmEFWGAutZpEfTN/Fg==","shasum":"3add124fd383ff7fbada740519094740954cfc56","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-issue-728.20190520143009.tgz","fileCount":744,"unpackedSize":3206546,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4rrACRA9TVsSAnZWagAActAP/iu0jxe4o4f5SrW5fZ06\nlBDROQfMfUslULGLI6CrZioh3at8U8amgttiADh34HtRO61EA7jU+jWbMXz3\nlmtIu0lD4uFA2C/ZAqeHDP/XcoISNBNPZITbI3K9p+hqxaMytclapuWpEgDg\nqICvrRtd+M2m4vXy1Yo+nqDDleu6ZBM+Z5aGwkSPoPQL9Npu138l+KNh21bA\nW5s4UkUonvcmYIBG49dkk0E5LT21FyhtCBX8NzKb9gGWY+POI8P8nb8oxSQ2\nuQ27/ZEphehkWAsHgKyqC3NTprzZaa9vGajGKSAk+6OrPcGmVQayGGCG1cGC\n1NdvwSQh/U5M+u5hr+7VaFTD2A1qqOStXBl4Es/TUmgTdhTq85oWJ0J87mEJ\nYyVKHCm6+xOZq94RLz3qaGj+YxIqajnTHRvM9Km/RUIdCipglKe38aCSLIW7\ncl2AnN6yaunKPBGFSKpz7RwLwXrydvUEh3fYDNc0SGTNQZkGB5iI0MdHr/Ft\nSMFXt9bDqu4BhI5NDapc93GhD0r/jOsqqjKyT8eTKkKSgrqsucT7sP5fs295\nP7SWeiG8oheD4uRkfZVcjYVMLBJflzn3U1tujsy4Km5hww/1KhLjmK6g2VdQ\nNgXu325uFCr0MTG0vCZfuhMYRVC4hJGNKvYos1xeVpFuysaiU8SivCz88sp6\n6tKY\r\n=PTFZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBWBoTht0qcFKFqPchT4f+9YHHUXO3qqoC/epJdK1EARAiEAgLO3fuGT7JV/qIX4BR6wfoVwgS8q8ziYQnjGZF82iug="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-issue-728.20190520143009_1558362816101_0.8014153577181145"},"_hasShrinkwrap":false},"1.5.0-master.20190520145854":{"name":"@atomist/sdm","version":"1.5.0-master.20190520145854","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"961d39059207b3b19559cf0280799dc72ae0d3b9","_id":"@atomist/sdm@1.5.0-master.20190520145854","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-V7hPdkcs6sR93RYnaNk6co9yjgAeZQykTz1nazIcYqNZ8vQhAptR9fMoohMnmpMWlfOqaD+c5m6sjvaTknWw3Q==","shasum":"a11eaf7becbc6de0927b9b800bacd367ef81b346","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520145854.tgz","fileCount":744,"unpackedSize":3094986,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4sFxCRA9TVsSAnZWagAAxqQP/2/rlKVfn7+BgfZUmlg/\n+Uivfb+Zikg7FtOsXX9w1Y+dgdlK16JeSjljazP1RfL3ojw5eTR3O0cT2cjJ\nHRp87q2nXicX56uflw59jKAAapAIRkGdCzz8F52NtXJv5tlMI713sFsqslTZ\n6ak+gbZbZIQoKV20TNIALpooKklJa7WyzDA+DIi708SGA7wQQfNG6gwRV5iZ\no1peumzIDo1msokPhJsZY7WJ+XQk1B6WxJjrUwIQF3ForDJiwS7Qnwi+D2NY\nLQ0ZSPlWGbxBmbRQh5DoNML1MriiIDQT+aYnQZBPousWyUHJ+iKE8S/YF+n0\nzG1vro0MpCjVZmhYtjovQhShTkpZQP2oQTjNjpsWWNkvsQYYEAHc/c8z2CZ2\n+R778sUjAJ/JvfMnqirEbkrUSeHG1/VJim4AfpE66QQIhfIi1XqHwHaAn6UK\nROEKRDYCzq6Gn4dwhmcSf3DzUKUY9YksbBP+oswx6MpdazEyKIgGxmW3nNLn\nILndAdGC7aff5bT0o4O9+bi8fLXMBuKEz9/aPs2EGmigJal1SHG2u4BFaC5G\nonp5r1yZkBXf33vCN8nZGZ5jqGIn2tQlTddrNZGvJRil9fM++YJuWllhM16D\nTW6Hvf9ek5MhNKvGJcFEZN0fkL4lm9PcsrpvvtcqqvsCa6OI1vXLmibGODYa\nUGaW\r\n=oYa+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCsg5I7aSP4NM8cxaxaCIP3HFnpOMImrNf8L2ijG+fZSwIgRixR5HpOcAkJucbMSxEtZY73iyvLDLbXKAJ4EAnhFdI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520145854_1558364529073_0.2925868649221317"},"_hasShrinkwrap":false},"1.5.0-master.20190520150051":{"name":"@atomist/sdm","version":"1.5.0-master.20190520150051","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"961d39059207b3b19559cf0280799dc72ae0d3b9","_id":"@atomist/sdm@1.5.0-master.20190520150051","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-X3klb6aHKzO1oLl+kO/oioFQKrZIYh9ANdDMnm3hg7SIY8w/G9U5jzA13/ycpgaHfo/6DLel1IrrBH2nJHAclQ==","shasum":"28d1edfd88a872d8b4df650ac7195db3f090011d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520150051.tgz","fileCount":744,"unpackedSize":3094986,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4sHgCRA9TVsSAnZWagAAmjYP/Ra2n/QG0GZu254R979O\nU2ii1hbr9ZRokH0TnfD5xsZqI61dqXV7FzevsGrzikGwyns4kB9qqJQuC3jt\nbTdse+Jgu9DQdWPa06lguVwrtI3zN8Tb/vd2iZGEgvQ342BoT5SUyC+E9lZE\nuOBCn65XsV8Od8tK+bm0CR+klOtaniWoiadIkW/nDz02pyfq/y6GeiaJDkWf\nsh7Li/4miC2VsHrDT88e2Eq14X0vS/OoumC4ltXGFSC6WUVObXa6xNXQo9b+\nttGp+UI+9QcGRBqPTmJ9p66Z0fLw3TJ76W3rvFlIS31auQSmvG8wJuya5cUR\ng3p4MjBsfdnYxdK1xdnQzsN0M0hyPMdDoo1F2RMxf34N5DY2ILo2kgc5QV1p\niyjJ4L4XZcborz95CSLCrjP6NoRG4pHI3NVCYXQs2I+c04BZhugSP2BqQPqX\nk/8ZmpbBJvK0iUkWJXPxlV7RtWRf4abelA7ei5UEGwWSjc2P1ZUr3yY3TPFE\nfthoZk2UwxlWQtwGkklkdBcXwfkrfvS4jSJZa/zejQ+bNwfdBbKVElPuBPVS\n02937TUUqEYblBkme6YUtElo9znOP2nIdLMUK/SxwIV+0M/lvexkSM7QYlvl\nZHQKmh/w68zoquB1F+lhP3P+8H1ws4VOFSQyca3fCA8Nx7mwJBhMziKnR1TZ\n5h5q\r\n=rhpK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF6XT/hiuWM1vmzKkvJgDc3RxHb9hN7/gtNczUaT1GysAiBSZltw2pffaWZMMmZYly8AHPDYtHldooU3fBiY9xcpdQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520150051_1558364640124_0.6394112750054692"},"_hasShrinkwrap":false},"1.5.0-master.20190520150716":{"name":"@atomist/sdm","version":"1.5.0-master.20190520150716","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7158d1015e15ac7053880de58911249ace70ee71","_id":"@atomist/sdm@1.5.0-master.20190520150716","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-5fsTc1htwmsv+hZR255UhRUq8nix2+J+wBc+wCwM6LRVDB1hk5MEVKErSWxsMea3TweONEO7iyfTdF5Hfy5Jdw==","shasum":"1e7f71ea103c40e315e874f3160078bc140077c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520150716.tgz","fileCount":744,"unpackedSize":3096359,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4sN9CRA9TVsSAnZWagAAmloP+wSYUmp5mFYQG3eXzHiD\nq73x0veb4qQltYwpuL2FgrMSALnNN9rT51LJbnaCSrH2EtE2U3wl2ijHYmdG\nvPUntzHPu6eHlKR/6dGtah3iWkP4eYXGtPutRLzzlg0ezeDkLXp/DTqQEofl\nHOwZzrOntK5bF+Jxwd/+P5tx8RXheKksQpzgBPwStGDIBb/4zqPPlTN3IUrW\nWB9yCQQFMVMAAthkFNMqZwmO/fguyiAfmIML5ERGWLaxU2z9zHISQc65UyKW\nWqF4o1ZLd2PB2YEiLHd9/d3SUEkVf4lf8E+ADv0QD/5YovT9MLb4v74Hhd+f\nSk5VmxBrTaSkRR/JbukUfbc6+uXOVBl/Pb57O1m4+eGV0YF8N1cA14xGQIwZ\nrQ2ODLnPHE0VCzYumbWqOeyjT8nQqwAV4vQlkKOfkVsrYLaJhRPJ9zIVQlsW\nUEFicZAvoR9JEbK7BEhI+iUC8vGRnGVv4UM+tCv6/5uri1AwjpXcXBTFLifp\nxdIfbHGO25Ic22S8TRa0yZUgJdooS6oioM1HEHEL649hTK62yoR/0hSjn0mn\nHBz+aoLMQWsV0yflIb+KTfK5yGMJeQAe1BLWIFJPeAvxU/2BsuYnQ9OaHwmz\nEQmicAyDOgPiS+PRFzKJGVzUVBHqLela+Ly85WC4vPaiWBTKaTEdxqAhPQnh\nKGzz\r\n=NWgV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBOO6/ksRU/OWs6FilFHMrVV6htKWkA6nsUZ9FPWpNc5AiEAqu6ELP6J8OO7YqGtY3dv6rtH4lmLWAki+DmYlqMpIE0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520150716_1558365052927_0.7814952508575199"},"_hasShrinkwrap":false},"1.5.0-more-progresslog.20190520150914":{"name":"@atomist/sdm","version":"1.5.0-more-progresslog.20190520150914","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"debbc293bcf816acdd06f452c042483800e28605","_id":"@atomist/sdm@1.5.0-more-progresslog.20190520150914","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-29S49/kgq+PfxgrPueT893fMdXkTRVwvygOj86J6LZCZGdA4b1kcAzz0ncog6f0Teoqc+xJeYbTGT0LZCk1mqQ==","shasum":"0ee755c4cfa0bf3295eb442be9ba51723ecbf586","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-more-progresslog.20190520150914.tgz","fileCount":744,"unpackedSize":3206749,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4sPiCRA9TVsSAnZWagAAMQIP/Az3M8vPpJV8SVBEXv1Q\nCnCsOXYWPYLSjSPktpvpFvsLEEG/Cx/SQVvsM3ozNXBsifglfCI5jU0qEgea\nzvP5wCpgw23Ht+WtChg8YSjHu3Xv6Mnea4d44dZsYyP5hpzBs+RbHfDykK+a\nnJbFDfuCXtq6fE/HaZQ5BleTxPvGmDl1GYxOfk283AuEo+hBIN1DbBcYubxg\nCI+VLEf6pKqJk0F0r499r7PfM6p4bzzaE/xfs2wUYdo6NAzh1Htr0iQ/70+G\nxnNUICw+uJokQdEdlp2xP/cPnGQ7dqVqqiUJT2nJCshJt0mgujaAFJjJERXJ\n7FIveT8vy1dtNcg4yfutvpU2K/1CVrLTGTS+wvSQmVMCuLo3gRTiIVujj9eO\nSTgodjrA6Mf/U97J0NpuRBT4d+eCa/y10gXAJm94GaLxkgU+kdKs85rheTNF\nvNCN5YQKqwpRZmUmQmUecaGoC3iJz4Wlg+QtG3qMwaj8SvdHBxH8RDI6BYWP\nZpXfARKMiIjhNfFHSMul9sJvAjqy09YRbSFa/B8mGka/poBzEUrmr4ef5JI2\nOKV5Xs4/nll7pkt5JV3vPzwHOh11l9dK/2vnbWXhLOWmns2/ZSP3Eb83D4Bo\nBOrgSMyHlPAjf1DOdP8VMGUBXNJpe/k1Z5Fk5KkjbLpXFzf1FOCPuBCdTclb\nJIC2\r\n=NOxW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAVfOxSVVGZltGvPuZ5M2TMbwmaAQeSnoNkAu364MTjWAiEAnR6GvF6te6wED+RbFIh3oxaup+BRUuHURZRZWSqlNHQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-more-progresslog.20190520150914_1558365153346_0.6259004921396283"},"_hasShrinkwrap":false},"1.5.0-master.20190520180214":{"name":"@atomist/sdm","version":"1.5.0-master.20190520180214","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9c1aacc7151bcd1b7a49295897481bdc7023fdd0","_id":"@atomist/sdm@1.5.0-master.20190520180214","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-dV1CaajFtlSEuO/ohZEZyMdwPfJXmiDxnZjSE30LJuDMxqGGtHzgFSL2YMQUQoXZE3MTxEX8BJrAilZFsHUT8w==","shasum":"bfbe846dc3e882622c37f84cf832a240b2ffc7f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520180214.tgz","fileCount":744,"unpackedSize":3097207,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4ux1CRA9TVsSAnZWagAAvXIP/2KuQjJS1WsAbE/KGgSp\nl81xEXXuEDkT4hBYeyZL+zNZvbCot7Uqs9s0dq2hP+rZZJ3Cu8UQCzV4AEPn\nLgpaAPpc587lsXJr6mWNCw1be3emMeUpSiUCkR1pE063I6VtgI0UrsNphIrl\nzqq86ppH7jJ6zYBkNtSIj+A586frvaCa0VLDJNgbFUQeJIb1xJ4dzbWqGX2O\nUsHrpazBSK61XBa3+pBUDg6Fzw2MGBm9LFMe84h+Fp2rNZLl/5gERdHU71fF\n1EtyqNoMjzshS+2bbN7n68ygG+zw46Nti67s+ebDAs8xffgrk1yFGh9vchLm\ni5+1L1Q+rlBJH9wJiN+yBVpNSSYYEaXlcbMt2DrLXkYZYw7WI828+yk3QgpY\nGMeHt9+sicITOaK9Kwp3M0T9+PnBdmAj7QEYMgBV2pU//H5rGUksspK+sC2+\n095Grrhx679uuWEOWtpvgRqOSvag13c2C/j/LPqXlwE8Tsn9cP9dA1xYOxV1\ntM0+mttxeWGDNGxf9Mtusb78XDcxHi9v/IfdbrX2d/O57c2jv9N/4TxGw1J6\nhMEDKjmoR7KJtKv5jBkPhYa+YQm/uMxp5AmyfXrkR+xwpKHJm6o6+C0NNKpd\nTru5v5F/8Qt3jkD3dIdUpf+x9Fcuu09ATWNi+d7jQBfL7PTf1vk6CywRXtV/\nxp1m\r\n=ORrT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDoUb8Glp6OX8xR99x9w++QfqLX4ULx8cJoxdoDxvz3AiA3HO+ntFWPab64y3LUF+ZaCK0bfYzdAWsBodP5lDGvtw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520180214_1558375541005_0.40396452414563644"},"_hasShrinkwrap":false},"1.5.0-master.20190520191309":{"name":"@atomist/sdm","version":"1.5.0-master.20190520191309","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0b4dd5556646f4943379b032dc6ed0888dbcb7f8","_id":"@atomist/sdm@1.5.0-master.20190520191309","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-2jUgxWjSCYgFpbuOW1GwsG1XQYN8RzuApyL5Jn1zzYN+t6DD9WlaYad8TWSuC4oO9EPJ6WxzVES7TMfeRSWfZA==","shasum":"0f3e31d99751197acf53926744b451ed674b04eb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520191309.tgz","fileCount":744,"unpackedSize":3097818,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4vz3CRA9TVsSAnZWagAAWmEQAIh0cEvMpVCxcvhcBQkY\n2KeN2n7cm2YCrSqjYIvIz9Al0rrO2RiKNaGS3iVleVuuXZgwTyfXlkVqlELA\nqpXBCcx+5bBhSU6PxqUrG7JH+WEiMdpv6GtoSIPQMQz9ThgpGYjqadODFO6Z\noA9yAMGzVYYpGGNjPqbzedfCpl0Ew+IaAun6edt43m51z2lFclfqs7hl4cLP\nGj7OlzEvqtxyb6WEU5a5MzgNdgK+IMZttSoKB6dx99fBY1cjihqD+68m5K0k\nYd0BPeYlQLUiMQfRpm2OR0vncyhlY1J5Rqk86K4U0K8ZqFz2FxnalfewSaI7\nsnMEliHKUaLEB4sjrbXEi3WzI87pSgACwFH3VLbHvev/pY4BZSmBz3jYIdyw\nbW0KrQP2p9+hQVe5/DpjfuKCNVGnlSF/1zclJTVO02z6KQ3JRDdFVbp+3ZOv\n0vI69nyjS7fOs0SZKrs1A0yVG1ZVqvM7q4Hwmln2GiEGF2QsiU3ELMa2+HUD\nXWJqhkRHNE8OMpIo5vJuNpqXsqUq0Y58WahvWGOH9B2ScNJzyElTxNILkEJ7\nruCK54BS8hC+7Lz1gxG5CEEevT/7+wzDH1PmnVS+LRv2MHx/Nl6C/iIUDKQ2\no1zGZWrtQ5y6QeGBtHwMd0ea5hKw63zybQI9yVTh2MV/3Tw7LD2M8tMaryZc\nXlto\r\n=+8V4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDaJzrzetZcpjgjEnp6J+p8aUn63uL+lvE8H/J/SvaK+AiBaVr+NMDjSyRiGMB0EGo6TbIe9HZPbusJylL7q6GZ+NA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520191309_1558379766833_0.08103259737478496"},"_hasShrinkwrap":false},"1.5.0-master.20190520195042":{"name":"@atomist/sdm","version":"1.5.0-master.20190520195042","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7d5e3acc3d716decf21cdc30b7f0f7d4a1fd19e9","_id":"@atomist/sdm@1.5.0-master.20190520195042","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-/YT0bftQyj1DWPDoVYO+49a1B0MgwVW/ncGpOxWmamanCpvncWIl4uVi8S1gBmtJgCkyvI+ZswyzKoIsuKXJSQ==","shasum":"e2f58fb23e1eef78c41805adbddd015a100656fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190520195042.tgz","fileCount":744,"unpackedSize":3099442,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc4wXCCRA9TVsSAnZWagAAs64P/202wBJvvlK4psKptKCb\nB5IgHxfHBQjiYOKySUvLhNSWd0lLCxvFWYRts/jlmMFYaAC0982nXdNJsOtt\nSzBv0ktJ/vLLOXt6/zATyZPkFcKXwH8TwohtWLPymJnqYkLMrpQT0sdIQI5E\n95GqEyU/mYtAAbjPfOB+3KP0eI0n93Es4Arkq+hFNrapixjglstHDqtvkrhs\nQnOC6EtHbac9NeOTIX3QN5Q5G1XIH4ZyftU/CM1QbxUrYQKCJfzelD+96Nc7\nXfpOWRPul+wprGFINzAyzbSwA43XOtOIfCD/mMBxQeRDlNzX9e4D4NtzJmwt\nTthcHg7DMi6nEdUWW9OuTBGJN23GPw58NYFCjISFfm6tPlGXBVjnddgeuAmk\ngVMRvJZRrxhg9GaZ/pTOZLaPdTIVRco2HBzYCmDuist6YZPmRTT3JYh2mUGz\nH8SpZLCpQ9feEqbEITDe0X1iRtLZPxUbZ0zVWI2j7mTPQ2cB71ngkVxB3iFR\nDzDWqcexvOxYPj0NX7y3pOivPiErX+z/ABxVw7igyLHJdRK8KCUZ967XswWl\nEOM3Xs/EaMhJgveWtOJtPQpnZFGUxhoVv6YD7jMwYeY6OamsJEZn31vgYijK\nyrKK1vPl3mr4+BCi42FFPdWsMoie9acVSKHQN8qXNKIbmVfjd+aKuEd45ZEI\nR+iC\r\n=oQD3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDGt6wRsNihf8j6KiXTpUyUdPOzjrSxJLSBqOQP37yuvQIgchtdFt+mbGpgVRc4HrJ6R/IuzCnpfX1K/+q5ZMyaAvQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190520195042_1558382017741_0.8928183587691034"},"_hasShrinkwrap":false},"1.5.0-more-progresslog.20190521095429":{"name":"@atomist/sdm","version":"1.5.0-more-progresslog.20190521095429","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d3e3dbbff26c10a4852193a925461803bf472881","_id":"@atomist/sdm@1.5.0-more-progresslog.20190521095429","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-zUAMvRPUp+Fu9UBKVPMs5eE9PRW/GdZTGwLkQdrivGb87hzCOueiS0t5oXWm8vZvz3NzLVxhsjZRyrvYd1jUZA==","shasum":"5cd84a64ba60e261981baf3f7622ae6d577fd453","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-more-progresslog.20190521095429.tgz","fileCount":744,"unpackedSize":3101165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc48vcCRA9TVsSAnZWagAAaoIP/1XdEAfIEeMzC+5bRlc2\niarJI+7GzValMtoEAQ/E6aAlpA9f9ugoZPPJ6soFcMwTbEXXOJClehpwmozq\n+y3MrUKTUoeEVggezx1KevZRIWjJU62d7DBdpmhI86m5aInup48fzXHjVBE/\nMGhOL6DFW61RoGBkba0qtGjegs6zfClPutEmlWzgs0YhX3n0CaXjKgWgfnnI\nPssBch/USc+L32XAstfT5L9MLIYNDSuS4jehk+alsJVpg3+/9FIkEqsL/s9j\npLWYi3NoZ+Xpn+cHbnsT3Xx9s9qc8Du14zqbdpzAv8UzOJRA1zRjUuLG8v2m\nVcmrJ/UxZ/86H8gg02lXJzrPQLpZReCPUys7lhs6ue5DBDnlbKHRH+QMvbiT\n4ps5gSxDRcWKMJ2dxYOJ/Dg0vUSbAFM7Vhhj03Fu82NOncIT5G+XPwUVqIU6\n0czylNs2oopTQocanjoHVHeqIqYwMNb0VgshLCznuV0hM9aO6yxE9e+4O0kA\nJawKqUT7jwNVpzzB3pRhDkP2d0plhAA8jwtURYxi7atBmTYUzjVEhvanhgKv\nDQXO3WpaymBOsnsS+nWXJmtdvLIi3TGE6YWpGhtZo6Aeqvxn1apk1Rq1oaV1\ndaaXQZqe4gKf2y8sV8dRqsgQNiSR/kBU4Inzs1gcOfJQQG57DeB51kNxIBF7\npyJG\r\n=3zPj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDduydEedTPB/Lygu+JwhyA9PMK6MK/bKBuBJ8rBH3E7AIgfc2GjWc0k4A3YkzrVgPQ3VHNPKFlbvqgBjTGbwMR01o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-more-progresslog.20190521095429_1558432731434_0.7849034399751682"},"_hasShrinkwrap":false},"1.5.0-master.20190521095948":{"name":"@atomist/sdm","version":"1.5.0-master.20190521095948","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8fc717a36f43e07f3efb3be5fa4d212684cc7e23","_id":"@atomist/sdm@1.5.0-master.20190521095948","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-66/UZAY6M4APxO/hEf5jOFrrv+fazShMWGjgrKHdpI7L+wJ/nIXv/lgKTVLzpPrrAa18pv0NNb4YC1uLdDJPTg==","shasum":"92981cf26844bc20ef725981410574b8aab236c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190521095948.tgz","fileCount":744,"unpackedSize":3213009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc480TCRA9TVsSAnZWagAA1cAP/A/8NSin070E5YZBpDc9\nNhw+Oel5WKik0cGDn8e2TzkEDeh449f00GFUPYmMLMxDnuAgGFAy46BkVx9I\np0joln0VUzEAgaqwO4ZbBmDZg++czGlQXKieYVds8fsoBTtXU6FamvEk9/dk\nR8cIgPXfyEXfGUkkmr3g8HF+VUuH+5Q3DlP+yyuk/x3nyYRUN5hSm7ySUV88\nwIjHLHQwAOlX/5qzevy0KrX7YiEE0xQ0JLZOdN05QURvMLNjeOn8NMCwM2Ep\ndDvc9p7vuOiTAgBY++RT7FXDcfA94ApiUKp4kdtZSLZ6SAJ5K/DncaUTBKk0\n46xIjk/e+ksoSpdSmjXXMpcLi5CK37o3vXdqvaQPz31YaXPaquNeru0GF8GZ\n8S6+XDkTPZ7SJqcKb0zskl0DumQb/vKhBNGxW0Oz1pGaW4CFr0X/7q0MWEXF\niVj+RqufTxT0ZcQtvgII/7TDSee0ITPZjySFxzsKZuXN+2Bs1eTXotCYXYJd\nViMie2gE5A7JR1Z/IwnRz6dL2CYbuHPD7qEuNop4MuFLQ/s28UqokqRsQOQi\nojwmG+Me/Apv9hsp1Q5eZRCt82QkM6j4kZnVjt0dSddD+wqvOxwiBCvn2NcI\n5LHmIAQFQPc2kI+av9Oi4nj+zohDC4fVt54p+6eC8HzbnV6cJG0CHDQ5QnBT\naU0Y\r\n=V3WI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE/17Fdu/QtCsVW9kfchRL81Q2dFZ+eRprtQtGn27YziAiBoSaXFt+EMW/fJZfjy6AOKAIqHurvOeBjYUcdQw7gspQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190521095948_1558433043038_0.9707658787139206"},"_hasShrinkwrap":false},"1.5.0-master.20190521100119":{"name":"@atomist/sdm","version":"1.5.0-master.20190521100119","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8fc717a36f43e07f3efb3be5fa4d212684cc7e23","_id":"@atomist/sdm@1.5.0-master.20190521100119","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-V6MpliGut+5/ailzfFC/q54ppcWemHuwZd7r/kssfUo50cdStx1Kqk4ll19QVx+8lei6X6Li4L3VRkltXLVGpw==","shasum":"26c258537364f7d618ecc34b23692a3db40de4e0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190521100119.tgz","fileCount":744,"unpackedSize":3213009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc481eCRA9TVsSAnZWagAA2qEQAJQnTf0uqSSC+d3Bf7OV\nsf1AOB1Qe+Sxdih6HEbTtE8VqbgOzoX58jsZW/D4QaOPMp3gTmMvp9Y+x9qk\n2ZfgzdlOJ05QQTuRUcf5QhSwDPrn47HS0E5vIVUX/l7wUhusLSSk0++9Y++k\nQ6tvQzkf3+Ojv0Y87DtZiPFqj6cryxrIHCTghEKUfm3y0/lm5aI4GgRHaB0w\nZLcnsfmYT9A72Ri8W4v4PttxgQexDTkod4zyT+SKb7fb+wpdC+QpA/6u5wu/\nJZQej5C86O00E7BfcclnLv+BV593VR0AmsnnUnGkfwSXuYiO8C7sO1d4Xqud\nSGcbXLVU8ucA9dw/YCEgROtw9zr8AsKSZqeqfA5045f2o7/G5pi5zdDw4YS5\nUAkjqmL7owEnI63f5DHI9+NCv2kFfsLMy1Seek8rPQ59HZoT7zmtDf8mB2/N\nx57SFJnffYzi1Ss8NuEbktglErkBwbkIGLml6y7T12STOy9TUJBHfl6RZRoy\nkLsmjkZ5MZKFSwnDvzey6Yvj+GbwqNqz1WAiOB99lxwMEGh+ljGgkBJt91bc\n9SO8XkcaNrG3C12dEnUCVeEhaff7MufRNF+Ffrb6OHZR9HJ2JC3gWQBqTjK7\nwCU2FA2QRum72FESmbmQg1wfRzq2+TYvLs5ShOgC1OzApsAAC2/bMVm/JoTN\n8xUM\r\n=Uq8O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5LP6nEUfv6KwYjp4Zn32QpDpScE/EmCGzGL06ZmGGpwIgAjakZPxvjLFnCTv6Ajf5S2KCVTBEOCCxvyR1YvMgczM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190521100119_1558433117608_0.619444058037157"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190522124319":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190522124319","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-question-style.20190522122820","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5c3d8f0f64513a3b6ae49d51cc0c34e4cc5893dd","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190522124319","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-wbVoE+axND1nVe13g38baxufI2hVWp5ani12zaicMLZD17K2NBjrUFi3e2XU1zeWAzX16oHPKMaVGCI5Dth22w==","shasum":"070bed01d88488ec0bf17baf4d5ebedb62a2a7df","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190522124319.tgz","fileCount":744,"unpackedSize":3215790,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5USbCRA9TVsSAnZWagAAHqgQAJf0yZzOMoP7PP8PKUOA\n3RG/0QJqKtjiK07vHceJ2Q0npvXdQR8nnClzut4UdwY5a9J8RlTyuckLjQin\n2PDu26kpCuV5vNJCVcOBfazPbPM/vmGZ+pCjoqev8GNFE/BPz506+kq40c30\ny5b9kP05R3YYsoZ+XDbcz+GpzeIUtgz4ldkrjE284OnPywSINdRMKdW4L76O\nHm7fl5l/21oOyJSKAIG+ks+eMur9XKwmkY445xaHlzdz30kQHRCBdtXMl60z\nlt/x32pAsSpJO1mmWiP7tWxZ/npYOEHEH3PlK0pIUA1SHaCsXulcW103BlPg\n08jJKG6MdWY+KJM+5/9t4GbIxCcooK5iiVXZcbrHucRB3b9b+mfXYTFJqteb\nzKCmJgXsPDjekV27PF4RYOK2y11KHI/fHPfPGCGU9wxtqV4/CIfv+bWxLBcT\noxNATnUk8FWc6ANapkvbimxJfeKYLOB1z5zTUAvvY2ODLegQJS55dDkEj3vl\n7ng5aXT+4nQXLxQQhAeap0ThzZS08v4rAJOm61lF9jObG2JtwE9tIaDhtsVe\nRtISFFxOE6Qp+kyZl+fRKK7/O7WWbIEnrQHSXco9Gn65Pl0LL1yazrHKH6QD\ndowXVsW8UeMLu9+8y1HwwdHJ69AX91rXze6FFAX0jPcz70pwjHaRav9Jn+EV\npvnO\r\n=tXaP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDuNVhM6rAn/1gNlrvI58mN0Rgh3cl1OYqUySWKW3H4rAIhAOXrlZi263tPeYPQKwJo9IkT07bOENw0iXwBw2oPVLZ7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190522124319_1558529179122_0.17483233343835858"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190522124937":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190522124937","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-question-style.20190522122820","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8070dfc0821f8ac8b88c1a35f07b1bdb0a20fe33","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190522124937","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-p/Xog9aA7H3Dyl5gtARPWTEinZ83IZY5wcNdzhwHOmzTT7uRU3aeW49F8UnSWsY0zy0OValW1yX8+V3Yphmogw==","shasum":"fc06bdbd32bcbe44555928e5051d8ea2f817c8ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190522124937.tgz","fileCount":744,"unpackedSize":3215786,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5UYVCRA9TVsSAnZWagAAT5kQAJ8mi1U+1K/eIC+uUoR3\n80ua4e9RHnD5MqT2JiKYAffqFK6JktFc1UhkJMkGgUUpR2GZg3w0f/Zl/8JW\nzlTfJd0e92XgfNGdZaI9eJuCn8qquOYPK1jEO4M/Wc8MB1vs4YtUNzqJB5wR\nU9sJubSsjkizEGKcjgFMwLyGyesaGtOc7hR08KfmZoOuR92z+Z+ISGNRrkd4\n/VhI6FLS7qdgxC37Jry9pCdUibO/cNC8B0gKSUZxWyGCrO8d87GxVfbXos9e\nxQaEVS/LwGxrCMw69aaivSlEm0tJEDlUXyYbLpgZcfxLypb1+ZbpI2CaVMoT\nbuWy4bac4mjjIHVuSv6fzEFxH7oUBy7l1hr1KmZOxgUVCldhXIGsLpGfrfOg\nwRZ1wLkki1aAdn9tVmPhcAcTzr+HP0ePciTNHaVSmPC4NRlt5PcIDtt9TC1H\nA0b2VQQhi+QPEreencKm6pWMvUnB1pSU+7AviztUo90LFZK6HZcVOmSJ554n\nSwn7nb7y/gPgXpNDeI9uykECAHUhniijIMmHTmcurU6omWnooCJrEKkgC4ex\nZbBwHMfcJGjU1ea8grJ8GqjGWMqyiY0ksH+QWtE1nQJSxRorP6wbo+lJnCzC\nXkfm41R5rvh1flclYHEBDsqVKU1AMs3z6qntIum7Oveuje6UI/SdH2BC7xHe\n3oNW\r\n=CgzG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPEJ6NcfR3Zg2TajL3gNTYXNvW2M6+1VHypZSB5zq09QIgIvaXtTUIo0j6L5bXoCFJBNSzBqn7Q//pU6t0KAvlLRQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190522124937_1558529557059_0.5609964973316526"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190523202129":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190523202129","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-question-style.20190523200738","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b5eb912555612031d3d584627859204cba3a04ac","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190523202129","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-9hyoWoSPFvt2sDezHrteGBlIEuxH5mDhzA2VI74n1sj8E3yq5P0SRFHHkFChxA4ZUGoXZUocFstmrlxKETMLKg==","shasum":"cae87a46a21c173eac5d5c561ef6f557ac5efe2f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190523202129.tgz","fileCount":744,"unpackedSize":3216635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5wF7CRA9TVsSAnZWagAAZuUP/Rq3mIGaZ0TCNaRdLFiI\nj6UZDx0rWZeCVdYXwxcgN5vpF9koS1LyaSndCubU3PiXhrot5MUumJqpmWg/\nPinJNw1hrCaIwnVp2UVefieX6aIVXrH0iVyfuTGtYOixUV5rD4xWPX8ZAtYE\nChvkNEvu9czRTq8DgIyf1Z91Alw94+i7cSBBUo0gkCWc6K44u2C6yZRUUj07\nkGVZHY8HUoBHSGwKEoMi08E4U4ExSEpenUp2HTVaM65ngYpdaylzwUeq3JWR\nFcADAnKQ10MvokBpTmDyl+IaxFarwx8fGhBcJ6ATJzqucUC4BfYdJtjazL6c\njSMso0mU8Zf0cGGf16WO8fZJmvKA3fVwMDETvkB1Izwxe08tY3R3pt6L6He1\nFzx98Qg0djt3EXrTkYQdzqylmtdeYqVQQxn5IsHeagNwUtgUfaTK6DCrI2XO\nNYPw2mQS05DZovUPT2jquAI7gmYIlVdi2oCuPRAJPLXjYnCJ7tcfI74D2G7G\nM4GZ7au69eFkSsbYvHeaUIBtDNc/TlA3z6yweW/oGVYoNlasJEbGcYt2OPyq\n/3k+21IOmtd45z6E2VYAXrbODvwVwwroEvWYGyhnX7hjbkFMhanEmUsT7ZN7\n9OSG8qW3RbDleRoYRHbxlltl99mP3ZME18/pvnFAXYNaijKXhIYf8MPhaqBF\ng3c+\r\n=zFTD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB2jGcyoSln2fivMzo+WUvrOim8jtmRLTW3lDL611h9WAiEA46Gd9oabSNG74g1RRixbjhPt+s94B0Vx7qyYrU12N00="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190523202129_1558643066795_0.7197612910889073"},"_hasShrinkwrap":false},"1.5.0-master.20190524065739":{"name":"@atomist/sdm","version":"1.5.0-master.20190524065739","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.6.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^5.0.5","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.123","@types/minimatch":"^3.0.3","@types/node":"^11.11.3","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.0","dateformat":"^3.0.3","find-up":"^3.0.0","fs-extra":"^7.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.3.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.3.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.1","mocha":"^6.0.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.16.4","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.0.3","tslint":"^5.14.0","typedoc":"^0.14.2","typescript":"^3.3.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"985a24412b12be6ae683e56c1a4f7d351d84b989","_id":"@atomist/sdm@1.5.0-master.20190524065739","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-RcCTI5ssdQvblqYEISF214NfW2jpxgWeC3D3MuUmnSxTl1dah0YqZrc3HhXj9HI0Z2QDZe5p21R/zXEgU0wYbQ==","shasum":"33d7e76229ca3b99648e7a3da9ec43f7f7b92aa0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190524065739.tgz","fileCount":744,"unpackedSize":3216964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc55aoCRA9TVsSAnZWagAAj0QP/3Y1wy3pS9mReiSK7XT5\nuW50KO2xdyAtCFqKIA8TmX43pQb45bwG9n2EuGVQqmwxpOIiHqksIFGY1erP\n62t81wmtLjdh8UJsvuRJpXOlwFkCilbqx5NnfVXQLwp18aB1fruJweeUxNvV\nsHb0ScleIzc+TetphIraK0gBydHqkwlm5boXY2WOwrVbzy1EG3eeKSaHzw3J\n2mJjbvmesueQvmYU4oyhmuCKCPdQAC/7Z1wP7yFIYtXuUeSig6p7+SCcHJh8\n0Rc0i2NawBy5PKVBgzQlg/5WUeZyT9H+vWv8We4YhrmByyMOCjxf7NJe9bTD\nVWa6VmBEqTPHEce2H/KrcK3+jUo1AxmVyX8OIem8KdCgBrZMh7zu+4w3CkIG\nqFgCfIwPsVqaSelkgIGnCQGyP+3aHmqVxWVY5/heskZsP7bDu+UtgwHF9yTu\ngzOuhkB7biZS+VPQL64rLGw6KfzX2XqhG1/hgZStbJfahlSRh0t2zNvaxkK4\nDAUwc6cr0JxpyIt1NR8Hfxu1qEq9g4VKdmAJEw2eGcPJ+6KUA/W+Wxayr49k\naibXPfXHENxcH1x149M9Hr60Ni6R5zdY9QWJJcxoBLwUpztxzu9NIRWhA2bF\nUlZ6sjVrKpxLb3K3Rfd1LXrHDdpAMFYGcjS9zEe+g05/MSQlBtbKdnbrxSGw\n9Lbt\r\n=xxjU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC7k6bcYfzIczgaQM3S9kclAkuaI2bovJYQMMN+bYuOBAiEA4tqEm8BQbxc+zMUBxpm8Sxz+I3Ymet2/npycVDLgQo8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190524065739_1558681255185_0.09066815037904186"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190524082655":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190524082655","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-question-style.20190524081145","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f05e303ccf2d58f2d9baf253b6a27cd57ce5b51a","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190524082655","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-NQPZrZAi8hpWr5RMyz1I+vrravj1Fw27KK12x9WgPRwJD/JVe9ORSR6Jv4CPygH8c/lpghBQemoDhVyIk5P8dA==","shasum":"cccd532b90703a5ede5438f9fff05c0fbf444709","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190524082655.tgz","fileCount":744,"unpackedSize":3216635,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc56vMCRA9TVsSAnZWagAA/HcP/iAZaATKTAOXcKfixzzl\n5slrwTgQOt7QgxfmEbLL7f4Rw1pW4a4SAUeXk18KA/l5ycHUHXCexh4VLlUl\nVFHF7tZQ9PLrFnr4BemV3u5l8wCkVlxVVBcDrfuUz6UmJhDMgAH+8504QZ/P\nnsiIOEf122vGLqhM0KKk9Ep24EtzPA1OixkeswqW8avbahxm0Zw+svn+re97\n2oDwbTu4ZLLBwnUzIW8CMH9i2AI7HCkNhJP4u3j1H9370F16tZeyTOYU1x2Y\niwpgv+XQinmf9am2Bbhg5OEesKFvO9NKCVJS88lBC1qpbGZu7dgwRVlQUA4r\nMUV+P0PuLEdETEbIZ9uOyP7HXUci4KGJyK3kcxaHMhLniGjqSKlpPX0RGg41\nr1rEmajcAy/Nw7kRy6l7QIEg/h1KP/EuVLXsJEvFrf/nPR02ATFxK9vPa6HG\nKg5zVgzzFYvMJX4fUiRl07WmH98vr1U4crLGSZPszE6kBUimXyT5JqQ6Wj/k\nJihcwbh5Uhcerl6klWzMovRUQ2CbS6qU4xUmeuobRTwjsvY+pWbQA/R7RO5u\nkxtf98Q/ywAKDC/sSpPtznOL2f45dldSw/T7a2eXzuENStijYpyXEGqGhweR\nA2GVPp42hTx7F80am5OUvKN4yzU+0aJ/rrKNXV05Ibuih13Kbm8LYB9bLMlu\nOG/Y\r\n=REXv\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICZI15Pyy5QjRy0WEOVVGfmMraQNq9hVmf/WwqItbxwVAiBZbzhVkreZOxfUinf7jgkVAOXbODEeld2IdoYQE5TK9Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190524082655_1558686665817_0.00022728346057765414"},"_hasShrinkwrap":false},"1.5.0-thread-ts-promptFor.20190524123617":{"name":"@atomist/sdm","version":"1.5.0-thread-ts-promptFor.20190524123617","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8f63499ce374b1844ea8d60f62f12d43b6c9ebef","_id":"@atomist/sdm@1.5.0-thread-ts-promptFor.20190524123617","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-O4/JBnGCtNOa57u5YZe5/niHMsbORfjLCG776FS9NUHP7eWbZ5rLnbLN7edSjY83uEl9vhXfJNJTJQPAU1kDGQ==","shasum":"efece87341776263ce5fb73e1117d03857b25b67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-thread-ts-promptFor.20190524123617.tgz","fileCount":744,"unpackedSize":3216627,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5+X6CRA9TVsSAnZWagAAX4oP/RYKucoZzh1DZUubRoCl\n8cRB/uUmoPExQ/r/zKRHF7KrfabkisluOaZSu5LxRoKla8Z83QaE4QvWGar1\nrlwyVxgdX6BU8rGUMZj+umqBGXj/OgyfI+HDROemDdDyic2oVO2ZgtDAtqlr\nqc1Ahchusv6ZJ4tYw7XbeAX4EaxIfdt95F7+VuAhSNL34ielRCSGMO2uDCjU\nZeAWiq8Hn2VxpgC5JiVfw0xku74OKa7yI2aOIhqJHUNohRpomXQDd8z6I/nV\n1ZCDhP+97Svwy1agaUSwH4vPfcwKamPVpQ+mXKjTD2YXyo2uP7zSSvr6lUyY\n8IxRfLzVP6vHfDlulwdyi0Tt8UtG0nwU4KiRqwOkqDqQNDTHmQt62YQsunHW\nV0Biidu65oEGxwy0nCAPPgmQ7dg7WUy7vLyIQ/sygYjnEjOWPnEYXNM58piL\nS0GOEAFeF7ieLjOShgwX5LukSVsjRbaA0meCvVtDLrAGdMVohH00W4H5ZMeb\nw1rinwWh31NN947RgFYPE5IRXQb5B9K75DuH6GDWvii6JY5pbVbdeCsbdKOK\nTdCkhvpfs52p6unOPEY7xv4OoU7M/stUPG8ZEv0Hhy6B4tClOwt+RtxlDFWC\n1KYDf8A/1JTgsOVwEpAcEGT2IqLjkzG5j+nUivQLvv9uBGbsLhckxPdBRFOx\nvTAi\r\n=BilN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCcPKgXMe74cxT9IgcI9ZdMgm5EX/Ehr1OWvPIwhPbZigIgQGDD4dcyksnCCeRK2Tal8w6Y4NOvDcn/4uC4GWZmvFs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-thread-ts-promptFor.20190524123617_1558701561277_0.30895374320099345"},"_hasShrinkwrap":false},"1.5.0-master.20190524125535":{"name":"@atomist/sdm","version":"1.5.0-master.20190524125535","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"54c4b17d2793857ebc392038a4aedf434e409e36","_id":"@atomist/sdm@1.5.0-master.20190524125535","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-ck9q60HCzwfw2R3A4z0DnXoUCm9NVHKrMZD2ys0+LeJUccCgPSNhj+uK+RFnrJR6OcaVbv/kc69GBLPSEdhk8w==","shasum":"a46d5d34105558c64c6f1c42f6b5741b1ccb58ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190524125535.tgz","fileCount":744,"unpackedSize":3218459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5+rsCRA9TVsSAnZWagAAWaUP/j+ZWE/p+G5g7DFWjz00\ns9Cc9Hlmd2f2mLMRo4hrORJi4MsNBESUDI56jIcbYs0pC1hljAmcd3xRP5v8\nYu+ZkwiS8whak9JDXhXR8g40Ak95/Qzhe8kaYzU+Wci2ZwEeHBM7erUVmofm\nYmyqjGDp8QtuJFmyG/0fQBK5Aw+MbZnf1e9UAG0jVEh1zGnwjw7sYMeg5XPj\nP3sjFZip5M3PWoQP+xPFd//ZS7M4yd/Xsa474kClRB7IPISyB2OVgbhkkK3/\nsJJepK1c6UCBinbCnHSt2qWb8nu4PqgbcFV0dhMoDzEGzgOg1Qgv/QKNs8LH\nOFf6AkA1WyhF77BJSnjOU10Nrpk4B9MGbhmEpADlLFhGB83MRiyLObYZulP3\n/R6244FLIM5KCKksh2mT231DgpJq9pWaqgPv+G+UxPejF+/1fYSWwznlGamZ\nd2FxNApFea+yFGLhazMFcpUIGLyIa8FjjIm1jFgXW11ZBd0/1SvFObvFT6y5\ngl02GfTvgNuNB4h9AUNjvfXhjFx0jFMHeVhrDUa3pKL1HXRr6j1CKsqqs2IF\nsFcqC4VVPgHO6q8+stgrTDHHyghqmqwZAoQvOQ+XYLT4hpZfe0LO+NA/Clfl\n+hs/UeqW9OEajA3kdJYSvKdYwBHyE23n4b+p0M3AtSuJcgRdkfcHQr9QYDib\nLWVB\r\n=k8Ns\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHmbadDk5cPq/welCAo76wxypjsh8lyOWBVpE8uZhT2jAiEAw1DzduYm8KSoKnrCXbWbJ+VxtbuH8pDjh9uu2xSkSgY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190524125535_1558702827810_0.6640235082272177"},"_hasShrinkwrap":false},"1.5.0-master.20190524125746":{"name":"@atomist/sdm","version":"1.5.0-master.20190524125746","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"54c4b17d2793857ebc392038a4aedf434e409e36","_id":"@atomist/sdm@1.5.0-master.20190524125746","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-FjEyad4dV217glzIiPqQlfG496bLaZYYS+fO+aUxIsfVmnwgmKeGVNgLTqhUmzRcYFi8lbIbAZBUVAt2Pl8FyA==","shasum":"41f239bd009398b6a2b9bc84c32bda777bf5c890","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190524125746.tgz","fileCount":744,"unpackedSize":3218459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc5+saCRA9TVsSAnZWagAA+zgP/A9m3s0VlonWUJF9c2HC\ns37cKGTzRgm1W8y31cw+SaSP3VtYMJZ6KYyMne5bIDuHUpIRVi8Vht2Vi1HS\nkoeneGsO0cW0XvndLFzFyls6z144/C8XK/BYZAy7GqHJH2LzYz3lZSUeytyO\nOogq22xb0MZWjU4PL5DBHo2CxINgWVfIELPCsf2iHk0jthsKvf2zIXJbCZ7r\nGwIXoQX7ZVG0CgDSSkcPoWCvkCc+ozUx7A/0W6XCUIb53LjK/duMikGkFD7A\nDmM64gELvA1fm+lNLZOQun/Hv2FQus+vzlSUwP54GA0qDe/Efehtl1mcIhno\nIN0qe4I0wcN6GArTISdSBep93Kj4fjjrbojCTgBWfNoabNFDryJ9J+oelHh+\n+OGtktieS600zhN73bdo4Gfke2nMdKuVJ+/S3AkLSvQpk4BrKBtevi4gUtUG\nNg5St2hWWdmXfL7JKnLeL/O3MbAcOBxTtcxGYqIoy62opxzG1IyXJjzuZoUT\nSM74++PJ+3bjsi8+Ubj0TrB0XLY3XF1mirCxd1ExJj00VofyK96IhgaAgc6q\nkuqiYG77VJHWSd5HWtJ7O1bFCjNQWHBKibO4IXLeNDXyb9S9ZQuhSWW1KQoC\nvD3UWqEWarTsyFK3DmwXS0PggKcvSW1kaTRrURGSHUSad39liHYZewTwys6x\nfOZ9\r\n=IMRV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAgBLLu2/ZlcM366VbzaOrHDvkp1djLuYXo3fMXkfjfbAiEA96wiZUtsD+HluF42ANlmu0hyHmpUSLsxgzbEK5fvspg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190524125746_1558702873489_0.9391193679319156"},"_hasShrinkwrap":false},"1.5.0-master.20190525114317":{"name":"@atomist/sdm","version":"1.5.0-master.20190525114317","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0dbde57a6556c3cb68fba3fb3811064f0d317bf3","_id":"@atomist/sdm@1.5.0-master.20190525114317","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-20Ze2iDhpoHkzztoKxCDpKdPdh3GFBoC15TJcis8KGow5GZcemkLV8BS1FOHLoFjjCZt6mp5G0Gmyh3b1tWQag==","shasum":"f5b0543bcdecad6809768524983cafa92790ac62","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190525114317.tgz","fileCount":744,"unpackedSize":3218733,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6SsGCRA9TVsSAnZWagAAi0YQAJWnOyT3wcHCPbNeMNDD\n08Dsy0sYE0fBmDRD87iMLApqA8BpB825Tbca3CFt0hg2xXoMS0VivAJ8l0RX\nAI5ZE897fnQwtePKKfH+ipXap5WOggv9CF1SBcc53R3A6DZ9RtmhgZ5UmZvf\nTce9QTwM11rQ08TqvaUqUeLmeUDZIDW81EXeKpWTauzi+lLNSAcZ71UGPHfp\npkGDi5Nxmj2g0vPKkqc6yDuw8HLUEaC1Ob69SLTNrmLSdibpWpiZb9AVOqeY\nR22FbhM4/MftIZQZZsQvIuARUGBO//dQdlzZJWwg4iqaeKKtlMOTWcy3lIvi\n91ZnnhPpW+3uLHCY0VR2Z051e4A+blIAs/2R2jIbQe7sytK4nvbPcf37821r\neoVfkKqrJLecseFop3OOoZUw+ulo8Yh4OvokbzGDcrXgGshudLCQvubj6mF5\nrag7HGZG9ngke1B2tMwxxViORZDAG/Q24LLfHeofzO/qbzkcTnd271kYFh1q\ntSO4909eWvuepSu6fNJfjxVXYz/83xzDAupG6MKJ4kBZbLfa48LID+UfFXBL\nOw/10OVDh9yf+nRwMtuuLjRkxCPZy9kbTS+Tt7FsKr39kuBul9t8p/Gk/AYu\nY+xZYiIZmFNq+ueILxY4RIYwQ2mazubj9gsgtT0OGkSPhg4ezFpThDVe0YQT\nJZmh\r\n=q3Ev\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBSPXOahgKrbs1kKzxiAwTupggWunViOzO9mtTwGjdW/AiAzWOuDGJESoAju844TiaFMLfhXoZy4rx921ApN0+NNCw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190525114317_1558784772402_0.24694608594628908"},"_hasShrinkwrap":false},"1.5.0-master.20190525114357":{"name":"@atomist/sdm","version":"1.5.0-master.20190525114357","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0dbde57a6556c3cb68fba3fb3811064f0d317bf3","_id":"@atomist/sdm@1.5.0-master.20190525114357","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-hQzAHsFauUWj+ZixFS8tCxj/5xwKpTyL807PMLZKWadjZMI9FwJHB4os0FTP886xF42zr2I773gRBwhMVd6+jQ==","shasum":"d00916b8bea04f944fca2c1d4d1b91cce692723b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190525114357.tgz","fileCount":744,"unpackedSize":3218733,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6SsjCRA9TVsSAnZWagAAA6YP+wY5IanoT+1h0HTCKn4i\n1NfEcwE9etTN1cftWqcQYXIeOM1QJA3NSKSf6mMhILW7zOedulV6f8iDu+FE\nMIQPdbHXS1//sSE2VGpHN/sUDGoODxBH6ba+dUFyUjbP3kL2DtOvmAh9CdPC\nDgrvqi0PebLrJrRC2xaErfntBQjSEJYCp1lj6eOTJi0TtxoM62uZLIalgwGU\nV7PZvbvHevbl5P4MsPM6UZdMmU3+KlPz6RKY6xVbvMIZagpWNlvqgHbul1oZ\nk4tvlwCEknbGVArbxF63/2sHoROx5FrjsGsob3pN6UsoNab3/vpCcMRASZ1P\nR277dmwciV3L8JPxrXmPpOvziNaIg+14yuKQX/p42++hgLVCw4u4ObbnFVaV\nVx4vud3YNrrduguaDqCSX6Sp80o/wR7kc1eM/cyjxFwUdlK0Jb5qzQ8dm5Yl\nYjZEuqLsc3Exy+aGCt6BYE0342L/l6LZRv8VYX4eTkm39Hw6LmeYDek3PVBY\nlWedVfy4+kF09zesbJjj0GYbYIVPeTo6BmljmXXImfSEMa5gVRLPmRmYzqxG\ntZ4w6XNVxkwB19DFVUAfy/J/gtuy1Yxqv94vjubYVaEZJ7R8DwGybxZM9QxE\ngZ9wmjlWCNcy97spBpYijvxQOj+oN9uH5INGZo+5rwKyKTdx6ynkPCSWogc9\n3oh6\r\n=07Zh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4v2OoaZIRGWE+kx8zFa0cuESsVusyEQZcqQEgM5p8zwIgSXoKjOVocwh7ClrLrz47tO20qVWuohpuL/xu1X3m5Zk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190525114357_1558784802358_0.37471964502225896"},"_hasShrinkwrap":false},"1.5.0-master.20190525161944":{"name":"@atomist/sdm","version":"1.5.0-master.20190525161944","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d7857607d0ea4ed11ac7c78869f60e538d1cd213","_id":"@atomist/sdm@1.5.0-master.20190525161944","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-5YAj2DyxrdwYFVbzFrVvVSIfSQie6dBHybVb5Qc85sNX/wZE8G2gDMDnUJTQgfcnBwp+yMfjMiiqC4IWrPUIZw==","shasum":"cfefaf828cb4a301ae921c37ca588887badbf65e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190525161944.tgz","fileCount":747,"unpackedSize":3221518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6WvTCRA9TVsSAnZWagAAIV0P/1T5kwW1rzQer00tYH2T\nsvds24V+Qnn2RCm4CY5F9QCpvqJ+FHpE/o0Z07BuhYD/lnoYwQJe67OgDr33\n+liGNvGTFDaxxpksaPwoHqTAqqQXAsIBd0T7aNhRmrKF6fevdWIunoOIBRiK\n0NJA+yRkTreauuPyAUKYWjmWGIITuJimjj2lTZ/kVJgaJG9WG+dTTvTm1Vbq\nXONV1McdzfLimuKxEkUWR9Z8DbRXGT18TEIysZFQFxPNgMPOEuCu/c0f3ott\nW1QO7rdgE1k1SQlJUMzT2I/ot2rHlldc7vh9RPupQmR1RVukrlhcD8JSJw63\n4zWj9KtTuc3SXNBGuu/1HJfBcCs+8skND078n5MRE/u3x2KfaEhQcTSmdeZd\nJzXv8vhblb3UHfBOx1hwNPfEEraiJhg1sixbuUa/OL7qnDUN7nU7hzTOQ4F5\nQT8/lz5c439kX551cDhGwnHTUFWeZPcozm7/AM0csEjGOv6ZfYLRSn/mtbw8\n5V0PXtcojTu+t/jl/8CyjUnNMDnZNVrgmrYSxDqDdFANsIhP25QFNQ7KlFaV\n5ZGkt8EZdhXHQjjXlLcXhyFFgNvfDDqlL8PMYpDdjkyw6ghgku4VlVY1rM7i\nzpSNCL9AVUWOaEwQg2vpazzsTME9VM4CTxb+1iMaNZY5qsTybk0J4aHVktI6\nxHrv\r\n=zq/U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDsey03lLthzKPAO32vb12RTm6xtE74nLCpVJmn2k/5sAiB8M7mgXfVKfORGi2waQinv/ppQpj5htoogCKyZ0yZtDQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190525161944_1558801362639_0.591590792965637"},"_hasShrinkwrap":false},"1.5.0-master.20190526082545":{"name":"@atomist/sdm","version":"1.5.0-master.20190526082545","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5648811130e53b4d32688c55beecdc7718544516","_id":"@atomist/sdm@1.5.0-master.20190526082545","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-uXYRy0sasOK7OwklSN79RQJ5SImZVvJDfphgR4ku3muoX90fE6L00YCd3cbrP+YYJIiPuaqtMiu50yljRlVUaA==","shasum":"4dbd45ec2258317993784bfc179bb895028fa2b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190526082545.tgz","fileCount":747,"unpackedSize":3221632,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc6k4sCRA9TVsSAnZWagAAKTwP/jI7+pUfM+/VGHUgPKB8\nzZLtwTEZoutjQkv1dHWWx8RjECYwoIIrMaSscvVfBtmSXpviMjo1S0UVA16C\nKBuX675O+mGO4VMUIAtSWqUk+fl0eIpUioHpryqACr8rv5ZpavmS9Bv7GPq4\nTCKjGLPgd5LQ5OS6UFrVCvEPnrNHvk7BL2J6lkVF3ecDzM5l9ye2A7sTKRvr\n+DMo1aovc1RCOrxwQUOOjpD2+RFtYsrdOEViU2vkcFoT/j5nTE1yv3L72WHs\nGwGK8yBw5n4SrKmliijge4lmp+Vdfg+tt0RtHW91PTWE+ee7g3oD6JLxx/Tj\nxbD1rjIMKA62FLASOEdKO8w32VJ0LaizXewvmNdw7kuyZh3+lNk2Tk+pcID+\nqi2BDxqG2dE0Rx5DelSlrm9GQgiDJToO1Mg0KfQdjhQ3CoUQqo9g8N5cE4ZD\nNnuE8KxL9kPJflMhB212VgBVQCjsq1MAU6/YAtvhABtqrY9N9HvkjfEFY/Bd\n4fCYtyZcQGX+2lU64qPDWePUrB8qeymrXu19y2LVGy8OV7Jl/TjXQTdjCLAs\n7GxAE6vg1/SE0nVdLX9xeiqm8iXadZ+p5YeU7YL/StgKFrQ3steH9iriBteM\nWou79lGaalPFWOAtYanFpIPyy4nEGgiNg/JmhVqPGXDuum9KMOba9mSLC3P5\nZ5Vr\r\n=aWJs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHiUHJjkNqV15vQBIl0/6fRZV+HF6KmokJRgLILFQ3I5AiEAzF+brFYFNKCQvl4GqRQNE5pAlPFBMlVfm3NEUMBS49E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190526082545_1558859307883_0.8805044391469714"},"_hasShrinkwrap":false},"1.5.0-master.20190527173300":{"name":"@atomist/sdm","version":"1.5.0-master.20190527173300","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c6db0e526675ee6c621a70f6c12269e615720da9","_id":"@atomist/sdm@1.5.0-master.20190527173300","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Pi8y4klXhFDll9KZ8XaU5M8TifWDw9pB0GkRou2rTiLiJ9AlEBKv3tPmY82AZ5ahyBPKq/kymKQ1qH2jW6eNhg==","shasum":"abb435838665ed168231ad46ca551911dd5eab31","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190527173300.tgz","fileCount":747,"unpackedSize":3221633,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7B/tCRA9TVsSAnZWagAA7VEP/1OeyyIbgfBt3HQPMZ5C\ngq7QR0ov20KfJur7C3K+dByKjWen5sHwLISWaIJH0cAHiq7hhPjIMrrmH88I\nV78OirHKyx4nV9e+QKlKFxDixT9zVlpis29qwZB214Y4PVkj0724fIRoAoOe\nitow6kvAWfjPntwSWFMraGY8ATTriIyUCdtSmlGnck+oMWHZnvWiKd4tE/HB\nLtOqCT3RKFqE3rm3EsfFols7zYTGm4nQOBXO+qSXyo5j9BvWNE3qrSkJvX5J\nTs8uXigTvFBvcl09fphwEld5kdGY/j/y7MnMZZ4grlf5U7ppCByto4Q8wVwk\nL73dXbaUD8PrcqoJWBSOVHETcwQrrgUxyfohnaEsZ6SZqiVTy9xZY4ggBgpa\nYhBIx3Ho1UFCR7+LO/DgkeItXZP0mMCaZmcibf0QO9Pu9AWR8olKUxqm4Om1\nTtht3xrd0ntKgA+9R+4ihkv32fxsV5X6R6NruQqn/WlnbY4bkuYgt0M66/du\nulWS6kgzJD1rzq4G10go6jw8IoNU9fxf6z/vPczAZa2d9XqRU0edP5HsMpJ1\n5c/WWehfD5LKhk6LfPJ64cVNU3hN3LEUZQ0oOQ+vDmSGA9/tfmqv95ouWogn\n6YdbrqJri/ikYRFOdkmy1GLp6mkVHVz2Bf5Spz85LpJ67/FgHf86MzCNW+oG\n/2+b\r\n=pZIM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpJwJZzzQx417gEWUDxjaMogf9TuUDykLb5GSw8uXatQIgIkZJF+HxUbf4U+ARuaXBKp3w0XPMaXTUvRNSQSLLJZ4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190527173300_1558978541145_0.06180027019048384"},"_hasShrinkwrap":false},"1.5.0-master.20190527203034":{"name":"@atomist/sdm","version":"1.5.0-master.20190527203034","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.0-master.20190524114428","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ff20369cfe0388dfc178b20636651b6b317fca9f","_id":"@atomist/sdm@1.5.0-master.20190527203034","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-HNYFMzzXiEBuha7qzh5AL66u2TsbsM5sTrgH4HIHklwtIJpOXprp5k0FjhjpXsXRzsJN7a3O/D7VfbyFWpKwCQ==","shasum":"836579dec5d64332c17546e8e0929a0ac00f6d83","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190527203034.tgz","fileCount":747,"unpackedSize":3221676,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7EndCRA9TVsSAnZWagAAl9UP+wfctYCT2CkMvjIHydD/\nKQtoMNxfZM9I37AzfvVcGxQwrxvYeRadb34NYrcomKpUWa2SzNby4NAa8yzY\nB/79IhHH9G3mC8C4uKrPOonEFJPd+t7GCkm4RApGDajsPS0YIjsU+H+UuHZM\ndxpIyo81ftrzIZYozwSX23y4SZF6sb/7HRhh1EG/NM5Rr0KxmWdKOmlzw592\nY4rZcUjAjrk3Jqkq9JEXZ49mt+hpEBRMx0y9JkZ8O3Z7RdXMHZiNkAa/zgtE\n875z/3rWq6SqyY3sQc1lmiGXr6HXx9aGPh6f0azHUzanKmAD6pKPgiBky/l1\nSPdYDTSp3k/a2eaE37p/0Hd/XQlXrFC9RbDQtPjhjlNomPzo6X9RHP1qIsWk\nglwpLL6z9FWJHsPDYd5qr8nJypFwIPU5XRL+WBx0L1WrlaTd+2I7Dzv9NqxM\nAlD0QhMqg7hTxxhXJtzevMNw7NiKrWKiZc2D8iBk9tqwVTNL27Ev/du+ccfe\nQrzWBoBj16LPmG8Y/bl/tCwTCYvrBG+miz/+3iLtqA53mCUAJqGtz7SHQWJZ\nbOoSWbQLxQPbb3teMWWmJcxYYcWkIC2WfF91HQ49NVTj2hgTArwk4Ii40a/h\n88kzqEdvrH1vwU+PFkUIkVgC9Qg8W0kTosZCxo6Fv0gG8FyKt+RXSSpXkgN5\n2PeO\r\n=qvdo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICmqNFmDCkD6RBVZRWlOJKgcdYHTvpypUm0jrWXGssHNAiB1m818dCGSYVmhK1WSTLpKm8SRoM3pQ94bkOwNAuB7lQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190527203034_1558989276909_0.22665288919074977"},"_hasShrinkwrap":false},"1.5.0-master.20190527210711":{"name":"@atomist/sdm","version":"1.5.0-master.20190527210711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"36e8045d8fc69eb284c50e52db960adde65518c3","_id":"@atomist/sdm@1.5.0-master.20190527210711","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-lAteyp+b6LAy7Nc/u5LSY8wGVQjAjJvUgqHvkAmUCbs8q2AGxFad00npveYcln5dmmyPHSxzsrsJEsgMWjbS3w==","shasum":"aedad6279caaff87b530558a22b617a50c5b0327","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0-master.20190527210711.tgz","fileCount":747,"unpackedSize":3220975,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7FIzCRA9TVsSAnZWagAAMbQP/2qg3f4nohvH2dWhrsQ2\nqFx8o+SpeRJPfPwJs/0yMWJfCVRHxStk/cgndLNWWyDzGbaLc+2h8pRikyeQ\nINyuEtj8jmlqes4GlE5PnkExROxMAhnewbpzKzFZoouEE/noSmeizZ6bbyFJ\nuDT0k+4DRS5wQsK7Hom/wy7+vGaX7Gn5/x9y1OhcQzJ4+6W4VqMZa/gIGO0g\nEc8lwih3Xn2n3igJOf55a3QvkxSzTeoXD8W2Z0iD1meTrWkH4Q9zgqcDFF6D\n/u6Y5eKjzvD3K0xuUKFqhmPZAYmwxbM9l9tZZNpjuzrthZeMor3aPax2yYPW\nJfNnxxjO1P6AaaTvJHJxteMtcRFGXGo2T/8efrEaHINeYN67LNHf1ELBmQqH\nptAoNmThOus3m2TNnJwFepua+Lew92e6zaiEBtBpbvlghwLww/W+SAozRs7k\noq7YO5Fp3ad9nmvnJTAI7TvznH+2lJ/EGV1NDmQ2Kiyjlvs9rJe10gjn0bZv\nCPRHb59KzFh2IlRqAMQVjrTcmwDyhO9ShqGrSXAL37VurTR9npJipQgba5jV\ngZlH/qY8jAUv37GAbj0M2R/ML5YVZg7Q+1ghewsoNBLvddCZY34AMIIqNw/s\nXiNXmwkUq3+AJRoQ5vwNpANCZElyA5fF5I4TAi/A+x7geJeNpPpGAGL9sU+v\nYgzy\r\n=W/Qb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC84hMHPqn2dgte1MLEsXBK9Uid2sNbtZTlOMQv8TZCrQIgOwA+F+0R5xzwcrvCLdpkWVbGhL/qAD/AvicsEPVuK54="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0-master.20190527210711_1558991410604_0.2247942211137126"},"_hasShrinkwrap":false},"1.5.0":{"name":"@atomist/sdm","version":"1.5.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"e2a7e4bc9b32409397763788626b15d3d9f4c53c","_id":"@atomist/sdm@1.5.0","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-/DcuYrrsW6QMYk8ofVdjFLRukHvVAv9GCiHzKC1gLFw0Hjd+2fLNGYJK7eOzirj3DU33nhn9ptxbPTnHpQdexw==","shasum":"895898283480ee8a923fb44c1ebdaf8003667ea8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.0.tgz","fileCount":747,"unpackedSize":3220953,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7FJ4CRA9TVsSAnZWagAANqgP/2MTRYkKfbXCL51NNtqH\nkIsAcZ0J6bhbUOM2/ECjjMC1scpFTmACGEZHPnHzvRELN2y5TaRsd/J/8HOD\n+66MQzKwbwfyYMA543eWjVtK6Xynln4FtSxvfFp4Q2GVoq6JCo/IULatNxBQ\nSGWxnmH5DpE52LQiPl+rU8mFGkAHXEj8hv6lI/N/j6RzLt/sFYBZrBExmERK\nMBRJSLP1KB/GOL54Cu6Y32H20kJdmUZdWd9+qyBnCkK2KAKVRBS3KGEwdAJ8\ni0q9Ukuyny9dTtxkDOkEMxKTm7uBmer++7ySthwcRfDBOYap0xUSf0v5DexZ\nEp41OVjnlOwUN5dgA/aYhKdOeIkyFfSwHwCHaEBSZLLn4GpJB9nbdV79uc/U\nYaS2tZcNZHQ2srUdvWoy7NzHmtuyRjQLvPd+K9xHrtxhoKWIrtkxO+s6/nFF\nV7CoHvsfBwqVUd5qnNT57EqzXahNkkyl0AuXrbFaiABnk3XTHoZfhlOjl626\nk3/2/lLAWvsfoYp+703h1Bma5LHq/Dc4Ze/kO0fNKpReK3bzV5WEVHH5IwuV\nf3E18PbGJtTDWIvJIyOyRz0evGdy1ExcTxxq1jlSbjSsmuoQpLIsFouOveBy\n4Cc5e+lG6jKbYC0kqFhDPc3NUic7KRxM4x6q46rfjRFHHz2YxEFJXeuRI54Q\nUoJu\r\n=ifda\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAydOTYgEdegrkWyF2oEeQ51cuckHGkW+YWZhaN9ZyCxAiEAre0Nxz8/81Hq+vlnMrUlSxJ1MUU068huqUJBoQcFgjg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.0_1558991479551_0.15256300839208414"},"_hasShrinkwrap":false},"1.5.1-master.20190528101111":{"name":"@atomist/sdm","version":"1.5.1-master.20190528101111","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.0","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fb4266fe813aa1ddd9f08c4e57df6e3abb98270d","_id":"@atomist/sdm@1.5.1-master.20190528101111","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-O5NJUD9WiBUBVhPTymeWdpjeC0iFQ1PO9NM9LYKrMWjezWyEDygU5oWhDqZ6DZqOxN9ORIovifVW5HSqNUHjxg==","shasum":"ea311eae14677ab530667cd3eda8cca58bb4aa27","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190528101111.tgz","fileCount":747,"unpackedSize":3221201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7QoPCRA9TVsSAnZWagAAM18QAJq7pqV2AuXJL0cOxwAv\nxnHqFGpsXxwwqiLe7sP8B78ENy+Hb5hzlJyBb3PttMoW5ULUiPp7jWkvy3rD\noeMD1f1JDb4I047S0uzNYUYRmu3mqARc4hyyEc2M6vXftbW8sAxQt7hyKeeR\nA4bG1Qkkf5SrnPNTm0VtL55naK0LbPWYuj+zMcHuB7Vw5gtVd3GyqmQWEfPw\n5i0xxy5JHVUB9NX1XuLBSgd1pCuJbTsdSwUepQ3eAlcLrRIzbIwnuhPvph+o\nAvGL0ezNmwIA0wa73ryGdHHtceE3TgPLeSqwC48IR3QTs/i4NLg1vj4hG04X\nLVCIhKUReWcXfzfAEktmAlEAQaC8mtOo/GzJy4jgS8gJR4mw3V058RahUIRy\n3xH3xwCfmfcw1dv3R/ZOQbFwncfAwJPbTo7Gr4E+ema+pU3jCiHytiel9ZC3\nnSSebYdSCNghMPBQy8XEpeQXHiiF1EeACiVR7joVWNL5MOhuQpcptQyo60yK\nHAY02IBx3FPKRrbt+tlqDHGVL1PznQ5UdPda0XeJCn5vbwyXrYivSSYG8ZGt\nT564mAxjYMOUgQle7pXisAwjZKiaAfWjp9FrOeyd6NoCDONc5KaLJhOfn0/9\n4uEByN+8RLdVsty4dAc6ubUQAuKme68FYt21N7LSPiUfNKCBKwp5dvYXcpRy\nRxaS\r\n=zZ1S\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDyNwgEyzfsUWmSwBeud2G12VPIVlboIEYYaUqAqlzwXAiBhTMzInFDloOBpDkLjelzQ0FtDc5EzbcpkmqBeMylgFg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190528101111_1559038478046_0.5444354675722396"},"_hasShrinkwrap":false},"1.5.1-master.20190528154220":{"name":"@atomist/sdm","version":"1.5.1-master.20190528154220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.1-master.20190528152631","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"193515643fab26c6ab6ea8a536d23bcc10855939","_id":"@atomist/sdm@1.5.1-master.20190528154220","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Av59HJUC68xXV6NUdjlljoLJBMWKSRE88Rv8tL3tgBNvIaWGsjocPZ7Hkd8+BAoqb/ZzVqjp0j87mNBlePPiig==","shasum":"908d23f1523b3fd132ff9e44197f278a09f44d38","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190528154220.tgz","fileCount":747,"unpackedSize":3221444,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7Ve7CRA9TVsSAnZWagAAMpIP/3twz5w4ZeNO/YncGzKf\nC/+1vHdpgEmMLJ3tTDm8yu2GkKDdvcB765C+DN7FJhR6tjixu8OasjLn/EsI\nAMS+guXZkZqnn+uhR08CZPbR8vtuz89R/fmC5DLTGZZ6pc+ThtikfAzuInyS\nwS6/GdeRqgGiGc0BUYsDvVLLexvnxx7Vs1txDqUEsAXhE9bix6WzAnWoMDix\n0PxJKK8cW+83DtJ1vfI3ejTuTpEFqSNmrxGxwySEpMNbRt1tXAjNyehfp0GP\ncjqNd+lvemy92E59y0NqiJwFRZGflGIe0I6aIvUKpJrCZ7nyt05Z5El6RlzO\ntnUDq/A6Kmg2rxEyU7wuTQ9Cr+/bqnuCUy5KwcBVvX5SSXvtxRZx3twSBFlJ\nqeobTfcpqz6TcXnVMOVg94bnd19supMp5BBrkgsneetgYQfPk7d/WIvCogD4\niLq2jeTt9PVPxsePodeFALeEbD0EeddJo035m6lJDxIQ2RF3qtzpPXiwzYdc\n23mwUQuDg7mQ38AXOP73Y8e7qydc78oPA2B4bwkEJTtdI11/VyFV38uVR7DA\nPypx4YU/BLjNEVruZ4JI1cbNakyRZ8BWjjw5PysuO6e1dH+lKWPWA2l0YzLA\nGZ3uGOAhGoZ6t3BVvtkDxGb+SP+PmPST+H4SFUf+ST/dpAJLBqtKCH4yAgR3\nKHTF\r\n=jsXh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFDfvcXRnym6tY7shmxtarNtSyPSG5On5jb14FOqDQLoAiBpXj0lDCCjFdvmNbaWzZ//QkUwjtgvG5Tw6KIyONLLhA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190528154220_1559058362932_0.9241272486862535"},"_hasShrinkwrap":false},"1.5.1-master.20190528195240":{"name":"@atomist/sdm","version":"1.5.1-master.20190528195240","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.5.1-master.20190528152631","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2d63ef92380c75dee9ca1f65d48a2f48b0384546","_id":"@atomist/sdm@1.5.1-master.20190528195240","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-tZC2iH0Fcm3vxGW+iuwCY36BQha3bB7NHMjb9PjwohOvI8e7O4IwO9D5vPMHvjPEOLZkRgY/M0Y1kw7Se2jZpw==","shasum":"2d75edc2323fb1bd6fbfa441ff24791f3da7cd78","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190528195240.tgz","fileCount":747,"unpackedSize":3221502,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7ZJTCRA9TVsSAnZWagAAdgUQAJlLMCG/4uopNtmXME7q\nCz1sqSXA4pPyZVeMgyMGZJ83PEPLdn4qwkohr09vEHF+ouDVWgj2U10e0LBc\naurCFmcsEc7h2ea4f/MC7URDXfS80kaL/TdhAFF8nWkJJo+l51w44i+H/CWe\nWGRcFTxRj7LyWPJQkjQcMkWtFImyw3ZXtld2PWcmBdNNAxKtzl30Mm9ub2Yq\n+HDbpuzaCD+sHcloZLOz7vmMFAijWhgdFj5SY9WF+4I0txMDHD48dr5RDJ0v\n6urGa3Uv6AKTa22P0WoCuOgT8zUpwjpWuSU06m+sNk6xWs6OJLJ/HRQulroV\nteRbySOA1Z6hblGcUyDpt5KF9TNBzC2NASEN65UNZ5buQWB/ykwo9s5bALzc\nBnrPQR+FT57YhnT4JgQsaCqMHy9xmytSnFKE9VF61l4GdFfWLsggko7G0Gpi\nsxlbwi8yHjX/AQjEA3+fCnjUvVP7htLprjhqL9zfjB36M62gemEKczV1FJJA\nyVTWg4irEAdzXJG6d059fOY0xxx9UbXxNd/CHdLTeDpPPhbxz03ZVpCJd/aw\n9NFF8XS8OUVUmRgozz8wdXlFTdRfFQGOXNoEH28NIYjIQYpC87wR+y2pJXjq\nkA3+oknZcxU03CpGCKBXSzToidaoFnI4XNsIiqa0yk5LgmbIu7ZGTHxnkv6e\njPNS\r\n=vXsp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDV8eZoTLi5RbCMNXV+L0bEdbjkJMMxI/KjGpBAJpTXVwIhAJN0k9VvK6CACQodYv6JMU0X7FVsic6xxnDQZ/e7YIDV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190528195240_1559073362280_0.7148188238979356"},"_hasShrinkwrap":false},"1.5.1-master.20190529121551":{"name":"@atomist/sdm","version":"1.5.1-master.20190529121551","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.1","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.1","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"42800e03f48c3ef6e8fe7497154faefc6b8c3fe9","_id":"@atomist/sdm@1.5.1-master.20190529121551","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Cn8oL6xPxk8uOg1VdXD/BA3wiABiMM3vDzFHzH0WE8q7ark4yEx+81zAnIEyE6m4dINHrukS/qS+93uYwVCnRw==","shasum":"cf42d86154c9a512933fbf9fd0f41b3b9b0ed235","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190529121551.tgz","fileCount":747,"unpackedSize":3221459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7nixCRA9TVsSAnZWagAAtJMQAIL7pxGsCHSEOMrtbGrA\n3AIR4BboLVC/8ltxbenffyYTnog2Da86McAHg7YaCGi802FDLyy0bVV4MyJ2\nk+ugslwyadBcg2fFJIjBNeDsnHspgUerw1tX2KwHnS0Pqcx2HOWqIPXBiyz1\nIVaa5x8re/vP0uhlwp/evUm4zLlUr9KPLixuwylvf5DT16P4E50/5NQnBzE5\nIXQGB2QML+5CBfHBuEBozZ8/035KqrihzLkJSPQDt5a3pTId1OaHZmwoNkiz\nxIqzc3G867r5g1tRjdUlyu75NTDyN8QkJbh75b1DtWZ/fv1UHDfzCfvxxlqd\n3SfrIEKRpEitAr3EO2lN1MhpIuztbh+WID8TGx0N6zHpzLFh/AtGCawbzxPV\nSky6WWoCm+IOQvuNMImh2bI5491OJqBAy6yXIozDgKi4lA5cxMakE+nKLkkO\nqM9Dw4WLAeBI1COXYbrqoGkksqxmgEtALbkpKMUnsfKY3jYDKE6+GEvuOZKr\nT/ylUAWCFoiMfFA/RRybJrW1V3W4bCdOYJk1Im4mdasD5W7WhpwDs+BYbtBy\nIFqdkcB1oFaEK1lEJBPv+CUVvKSOiwYGj5ymsMaihNy+vPDh1hw5fFqHyTNx\nJ29RH7EMCrGYbKXoU5CJkWXYOqIfSf2w/ql4iAYNg16FGz1iccLHZsUKWE59\nVte8\r\n=3GBU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDcAjKuhu2l1R3e5agdSiUws+Mg97QTwAbN8yV3Tnj+bwIgJJHYxuqfg20vwtrJtIKZze+wN/olYRicsSXlaAxLP14="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190529121551_1559132336328_0.39467448982550146"},"_hasShrinkwrap":false},"1.5.1-master.20190529203520":{"name":"@atomist/sdm","version":"1.5.1-master.20190529203520","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.1","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.1","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e4c6410899f6bb23d31678e17355ead87aa09c98","_id":"@atomist/sdm@1.5.1-master.20190529203520","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-R3yW5TyDwDMnqJteZSMfE3RJLpFL+KjHfS6kHr5Uc27AYbM+wIVP2mEUdbjOujjB0DuldGQjv4gHXc5wMzcEvA==","shasum":"9d5aafd3227e68d4192512598926fa3e3630cd5f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190529203520.tgz","fileCount":747,"unpackedSize":3221723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7u4XCRA9TVsSAnZWagAAr9oP/1jf565amFyh7k4lDCds\ndJtlM3xn/O6pJz2aP9vxlZKGJXyoFfF9aiqjbHIFKomh7gw0JK78uqOEC6+B\nlm61GUCC4BZUajQGJn5LgXwVlyGDEQgAiMv0jtaf2E+JeaEHJR9p0fRRhq5V\nlXqLN8J5/lhgRoHGnkgbKEqSX6xkdGDZHq0aa77gYnQHhM7LeHlx5UcHZHFf\ngqa8v3sEzSwqzGpdAo8Ot9k3YQoVUWBE2CzaGsKS2g9ZIWC9whh4SERherKE\n8D8vsZvzXGsR1xUE2sSMlG6i/Vv+UzzOcoU9YlZTZc4dGPSMdMA1wO+ga1m2\nUlcfEq76c1kO15WOq8AjzkVbRlFmTVX5FKZYTihPLrjPS0EYsBMGaPWW8EHi\n35yKtW8MtUIdO7XHVeXitOEtIfE02Zj0eOgGvDQwZqvGLgrtnaeHsaWalGYn\nsjuufdspUI70sNMTw6hdHX7+p3feIP8PWimNoMLvYkPq4AfYke5GVR5GFYhF\nzWaGVBbXRjYLRftWmjtG8d4XiQatfH9Hic+rnRi154bOtStukklkc/WDrS6W\nGZCdHTH/ZpEfFLjaWwVHqHJ9oJxC87RQVSPJVHM4ZWFbPwnUpUFQ7lJPQjam\nG2bPZb5dMWm3M6OBp5eI9ObkpZS+c4YTkgJp9WW8egTA3ebTv3Zyorflgxv6\nzDZs\r\n=7aMQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCgjT3asu7vQtnekW5R75k6feKQYx1S02aqFDlm9cHwSAIgEDBZLF/OtFxVzQAezo61fkzVKAVz+rXrAD2jJ5Dmkks="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190529203520_1559162390204_0.45536747294376867"},"_hasShrinkwrap":false},"1.5.1-master.20190529213155":{"name":"@atomist/sdm","version":"1.5.1-master.20190529213155","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.1","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.1","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e4c6410899f6bb23d31678e17355ead87aa09c98","_id":"@atomist/sdm@1.5.1-master.20190529213155","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-wsBC6VuER7VoUKtQZmdwh/YLe5eZ/+ra3BLrAi3h9OHy171OnFEZTCScimySKOSO/reYsPDu/5wyaXcualHbEA==","shasum":"68a8c6f3b82e1e6ccf9680fdff9952cdf0a9d117","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190529213155.tgz","fileCount":747,"unpackedSize":3221723,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc7vtECRA9TVsSAnZWagAAQTAP/R6YEzXrGkXDjxruvSr+\njfXTd9iZu8vRu4vlQOPU+E5I+Z4NFHRVY24zXBsDK516FTtN4vaI/SDoq/mp\ns0LivyGkj/5ycOQIpwUV3jzeXd1CByMLmQQokg1mbCKb+A2Y/uosO7pIvSSO\nv3h47dg5rwR6lPklXZZhSngphxM52ztc6dg8TE/nxRkHXBOgfOcydSDS1lv6\nRLegPKRfVExL2G6HMeMiXhCHJdqEEhw1oxjlNfIncCd+m3I/hPIDFjTf/kNy\ndPdGGErwXkCEB7dws0nZpEZU5BJPx0TD9FFlVdxnNeYmF1X7cGJ5/ZF1Uhya\nN+OR4czqvVb4CyC72H+EeGa2tSA34vVFlvQWfsk1eH7ujyTbUA4zoxGl7eg/\nAkWHTnhCFQdgbs8n8gOEzvny7icMnh2isaTm6rCdTqKQD6zcQEdVAMW5xrJf\nQPKDBnlv+a3Gc5LeAjK3IxibQ9F5OnHJr+ijZfwWuf3XH0d2cJkyDakGWuMt\njl18nnkjqHnGxXHXFoTgci9g/7RKEEB1Ntn/w0YEjYK0DD/cdKtrqtVPD8Gh\nVX3UD5o56vxrekrdhzY6JCyHgt+4vpvB0pBIQ+eC1CB44xhnLiuEYeYJmxhJ\n23wvqKlg8CXvQuIlYsLw3jMRj1sNnaSIqOKI2jJ3KGg0jgfX4IkuMLKWpft3\nzG68\r\n=nl1N\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFnnHSvkduyksunMCtpsZ5lXSsOMUACMF10x1D87iJm3AiEAyKeLMACrjSr9pCglikMXngUP8ng7q+q02PmidvlhcK8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190529213155_1559165763461_0.1512027216954146"},"_hasShrinkwrap":false},"1.5.1-master.20190530113228":{"name":"@atomist/sdm","version":"1.5.1-master.20190530113228","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.1","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.1","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"760cedfce2827c0176502291b7aff671fb839367","_id":"@atomist/sdm@1.5.1-master.20190530113228","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-3Mq0RG2HIM2LvUkTOqtjjhBaR9KSGTjUm58BdEbTLekBrQU9r/CpuNOPaoBrWycivhiOpE8xEnpYFeM7Hyzn9w==","shasum":"364ec7c1c36a0c62d160f24048aaaa186fd2dbda","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190530113228.tgz","fileCount":748,"unpackedSize":3222816,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc77/pCRA9TVsSAnZWagAAw3AQAJFkgK+QpURwdBgAdBew\nkRRHrHMKwUl1c25BTFFBPVBMHgSisWP0jAcNiCOGCte0xQUETMgxUQq7bie+\nowswBC84C4Ydf/96HIMoND7iw+D1IbCHGHDfBxF1So7GLgmE2c3koqTGcPim\nhAYc9jL/U9yu3/+qb3P4qXkzLpAA1Fc9AEg1feQk5o7cFaygLKjG4Jtv4Wg1\nnrjWtkOXmV2Ux3KR59gvspDUGrfAzf6Qh2MrXh3CXCfPSwhpwqdPyIQb9H7E\nRqjoOjxio2ht64BXeFjpmsjces36NOMLd4CvT70x/GDJOf2b3a9jJJND5yaL\nvKTBJTfOp3IKoVOpaa7Jsi3oYSPlz44cQFu5fAYLu2EFc+UtQ9+Iduu1lLwi\n+LRmxoOAtb1r+B30D40/6WMZk95jW+1/pBShPeGwLcQj2vlGt9DSRDTJuS+a\noY/vVu3LdQneNdKgvaPRuHTNQoQuzLjhr9caC9bECUP6Om6NWlFxjhNHhlWE\nEr26RwoAIh4ZoYgBx/pcazwspnshVX0947nJ7KDWkJvCsqYz6uRi9JhMSw8e\nHr3APqCC+w2i4iKImgh3n0qgbqitTIZX5HcubtyGZqZw78CIoSdtgDxG8AoP\nrbKCbrl/0X0b4TOUTAp2C0ZHmrcz4yToQc4ci4zsGbAZzw2Vto3iNfnNnPB8\nXxXr\r\n=zzRs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCg7KTjDRG5dcqWGwyOeSfEGGLjs1UX+Eyf1mwHFFo/zwIgYAKsEXlJ7pJQIql1U4ln3CEZ4qrOkKU9a77F13BNW88="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190530113228_1559216105076_0.9534135800877908"},"_hasShrinkwrap":false},"1.5.1-master.20190530205831":{"name":"@atomist/sdm","version":"1.5.1-master.20190530205831","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0-beta.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bfe744d0141fb0259caa035947b12ea22f7fc146","_id":"@atomist/sdm@1.5.1-master.20190530205831","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-ZBRyCShR6CbUCnO2viY4F508s9AqsXtaqKCJaFDrEMuT7wgUbeBFGav/eCroLGN6FSkZYKLI4B35KYNqb9297A==","shasum":"c3c11f33fc3016556601b631bc7ac7bc34097fb6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190530205831.tgz","fileCount":748,"unpackedSize":3222816,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8EVHCRA9TVsSAnZWagAAlEoQAJekcM6JndVF3HrBj3Eq\nu2fcJc7uQgfUEBXBQ7YC7RJVaxVPyQCuRUQZpuLnmVLMv4fFzuD4KeZHB9e2\nQ1GyC25K2zvwSl5ZlAhMJwW0Il3vsdeGe9ul7/Z6wgYjsJdCEkuxp3mQaqCg\nXNRi9zrwEMMNFDiMtBi17lNRar3SAyBawHMh3Z5bF3QNxnXJ0NS5KO0Ew6SA\n9BJo3ATNwuQkggnb5FVFGlbcMY/+hwFOx/RojMb8pjLHr4wAVwQWRAbFdwz1\ntm4r4Ed02QljaK8bFe7HdLKaj+kIoFgdpbFSKoh18X9JArXCjD0aXXptLkJn\nyEBe2MTFSjRSRWLzBEOhVKeKOcGHk7Xx66u4GlH9t2fuDjS6xH/YHzpJf+w4\nUEgMMGczZ7x+XH8ugAzcaPnorMEL9WwfodF0itnsgPxH3G97mht2t38o8Gv4\nlw89XM/TS5yrIZ2j7GHtZnLeHs481QYNRA71im6Iv6Irzk8BgWGFzCndYlQl\ne0w+dh7JwPSkh0Lbypug+MbWqyWRKdqNRMCgBVTQs2XSfJ9+Y/ihm4ux7WM1\nEW8ga+fW5hfwf7BRwJKz/fIYB5R9UUA8GnkAeYBazFFch/eoZvigfnA+PB9J\nWGqJOLlbFbWo1Mt5t+/9Rkh6PALIhgJsz1E/Aa2YEeaZfL7ujwYEhJDGX1TV\n8ntZ\r\n=x37p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDqZj2IHELmaNetx0QVt5z//DMNrWk7DaDuLNETrT7ruAiAnmBJV1VSAsINsmdseAcGsLS8zx/uUo8YjdJuQI4oanA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190530205831_1559250246623_0.26769607123343886"},"_hasShrinkwrap":false},"1.5.1-atomist-update-latest-20190530224936.20190531064640":{"name":"@atomist/sdm","version":"1.5.1-atomist-update-latest-20190530224936.20190531064640","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1884a542eb4154b80b7bbdaa7d83a7b80f00da6d","_id":"@atomist/sdm@1.5.1-atomist-update-latest-20190530224936.20190531064640","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-11/ap9fEZemXzmwNHI5Pv+C3yqvrHt8tTmh9FuLHhaSpXOPk+xvaRDTTcRzDGQ8aDal1KNCxboMHELeumpNRXA==","shasum":"8042b5bbe2f3231b8fba84d11e697fa4827d0992","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-atomist-update-latest-20190530224936.20190531064640.tgz","fileCount":748,"unpackedSize":3222696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8M6LCRA9TVsSAnZWagAAAyMP/R8G19R4//JqNlvUIFob\nFPyKV5f24Dcv7aA6CoK/+4d5yueSOxjVEdTaUN8TIl8yEapQWLWLkcbOx9m1\nT3E7k0QLn68pvhqlMYHzMDO4pVFM+0Qfx9tL33oAcHQs6p7fCDy7cp19l+py\nnzlvFhyLrOc9TF+uy5xbqcC3f3bchsg+uPCw8GzVdPAk5liUl+0csxjx5fdK\njI0u8Uar5WWv9H5XmG2RWD7f6xLODkOWHSi8Mbj8or0EW3CJHdyoR8o7zE0T\n+Q6uncDEDbHul3YWvfA9RMIoJt6NcCo7gJOTHlT6bzFaM0ZHvdoTu7xd/SGm\nQMObd4pgfA6efl/ENu9ec6Baqf4LP7ScXmpN2c2D8pL6XY6Y+RJtcj6pxuu3\ncto6QOX14OujNRak+9Q4ae+zqB14EI2CblRooAwD9iz6r97Ck8veDVpO6wd0\neFyGqKLtohqWfB3qmUtRhXqb2NT/Se17hZ3/Rr3WpGgkIuF4batmQgJPSAUx\nXZXezCoH0outzPww+bnoLHF5ajbVnIq5vvS1iAeCNMBWa5NBS71R6xE21ayt\n3Wjp1M/DEJSDMh9Z0HoJ3zoIiIMtrQNefRxs8blIogWJRuObfRlfwDB9zUbr\n3fuYP0LiAz/2qlfLAurb1qStXhERSlgWVTg06h2zUtQwtwefsp0IjDCGZvEs\n7/O1\r\n=lWpn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAo75OUS8i4QqOnrHu5NmyZK31zMnVmr4sYWv8woKDxUAiBysWhKmqDZAlp0/ixHe5f61fWmIQFkaII8RP0XprH+6A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-atomist-update-latest-20190530224936.20190531064640_1559285386353_0.6731367190275193"},"_hasShrinkwrap":false},"1.5.1-master.20190531065738":{"name":"@atomist/sdm","version":"1.5.1-master.20190531065738","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.6","@types/power-assert":"^1.5.0","barrelsby":"^1.0.2","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.1.0","tslint":"^5.16.0","typedoc":"^0.14.2","typescript":"^3.4.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9183567aa31664ca6c687a3b095221cafed83121","_id":"@atomist/sdm@1.5.1-master.20190531065738","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-iA9MAVMo5r20hNEduWJiP/RKCgCN/Crck7AsEz7oW8u6CEKoSy6usjgCXqqH0wG+SxqwnDhExtekc+YR7uWH/A==","shasum":"e9300da918f994242e4efda65eb42a204cc20fd0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190531065738.tgz","fileCount":748,"unpackedSize":3222443,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8NEiCRA9TVsSAnZWagAAKskP/0VeyL78ncdQvu41cFr+\nleXaEcFyoLkrY/ptvnA85+A9ZF8p50A7ejjdBCOUQ158Sr4IcLdUVlkxgtVY\nVv1d4+i8xBjLv3rWTOQneuk0uR9N56ZHlHgDFhDuaukS5fc0NKm9jbOsKQp2\numrNOVaK8CE6o4jS0/DMwDxFN86cz4fdcXGpgH1cO+FpUqZqs5MabsxF9b2/\nj09bCytVAGecD2OxlBD+a6SIZstJqA9LG028KTSuffLBVdJ5ZP+armKIoYdL\nvPK0QnvNtzaqf7hG2A6T60loGwwBMVILjwgFSJcde3TTeXRYOAOwiAqC0mlk\n1uoZsDdke7najPnIOLKMyN6AInyhvL7ZhQG5R0X4Ns7RA27X4N17T0ZuYGnU\n9c4Wjl5vNN9E9bXV04BoAYo5EvMUzlgLzYbLdqW1V7HvlMTl8KURxPv72B7k\niadVSKukyk+7vln/sjNaLH0My0mQZDdQL+DZkTgNUhD8Nwyko1NP/rcL2aWc\neih3W9IXlRkETpGFh247QM7VRs1DW6Ympf1iDgfku5NOqd1znpGfMr0oS71e\nXOM5BYyCPI7S1A7cZoHnfjI6nmcEXxP8iioQCKxrePEjhocYXuQP1b6SaGZ2\n58Fb96PmfBIZ8UrQ1XrGoZK2AgfKhySAHIf1QLfSbvQ2SbSwDRKpaKQrJ6Aq\nSyar\r\n=4K49\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQClD3CtSEXbTSm0t7vdLGvAyqYWDjQz03gIJ6DJytJe6QIhAPHvZYz6/hkSlIDas8O8+wWmUiaATINWGyWkE9xaInEt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190531065738_1559286049986_0.44453008847473185"},"_hasShrinkwrap":false},"1.5.1-master.20190531070736":{"name":"@atomist/sdm","version":"1.5.1-master.20190531070736","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f726d0d8ecc4ce3d5096e5c0b1d04e589ed795e1","_id":"@atomist/sdm@1.5.1-master.20190531070736","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-1lbMAtjmDalt5jAyNmFAoce8fFDBtHuLmLnVN1mKABl/IS4F5vl67PkflfcLkCrRbcfyCKzgCmjze5S7IyPvdQ==","shasum":"5dd93a056b7673eaca8831b7d1839be64c78113f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190531070736.tgz","fileCount":748,"unpackedSize":3222564,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8NOqCRA9TVsSAnZWagAA7GMP/0Gs1QDZSNZLg1gcEHa4\n6n1BzbclCC2YLFuPFNiLUBJuq454+3Fj1TQmK6Afm2qXhFue0+kAkf4j+Zpt\nzn5M8KTIz8TKTK1UZk/atvbr4DDGKDKAVPXJxb5vAeDi7ydDfUpPBmPLxrOO\neMxXt7teZL3jmZrYNMLE7f0YcMqg3uhMhUvznpHYqqR7rl0adTYZLQUxv16R\nQNKgx6+s4zEn++yTFB2zna6u5fibpoRLfOHcEtyr2X5bIoM2PdIH6A5HZ79k\nt+bGXylvUFsRd1ZfNPGCmDXElhBCrxzsGlYuo1yHHL0HhKMJBMpQlLr7MAuj\n1Tjn2ffsr4Ck32Lo9I/MrpTEU/KIa3L5/VmeKBmIC9KKLDTEa150Ig2iaH+C\ndKmAmmOZvbQ84M7YEnHEya8UQnIh7BWR+tNiwAT3j81IsPK+LB2aie3XzfKm\n6VwZcazf7D3BMO4uJC3FmSAx0YALb4UNxWFJLnefRG3pJXK4ebAHJ8DadJwJ\nkiHVJYpi+6Q4aeWyVEsgc7Clr+rWS4P5Lr/+JyE8iAfecEbOPnnd0WHhHN/+\nNi9jfsGcpKjSQ0+drfjeo6+cs58scamzBbzJBprr6PhSuwHFZnqcXnJsC5jB\n942bUtTIVeGhlzwidyMASJ2hxUYfUnqRf/AxT1YTRB68nTAFbV5PBlPyT+0P\nByc9\r\n=nras\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQZViw1yMFTcmsga4l4AHbZrz/PsAu9RujvhaNrC3LjgIgRDTu2otZtS2R8ZF+YTlFQPYwFgwxk5mUvtMyRK3IyA0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190531070736_1559286697667_0.6535366924788097"},"_hasShrinkwrap":false},"1.5.1-project-contributions.20190531194459":{"name":"@atomist/sdm","version":"1.5.1-project-contributions.20190531194459","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5315a9dfa2f58a68a61b34a24c9a7b8e393623fc","_id":"@atomist/sdm@1.5.1-project-contributions.20190531194459","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-DKEaNw4yub4zgGd+Z8bslilSUC2bd1ynXKYA+2gZjhI8AAZoZfJ7Ye0yK7zEQz0a3Gzvh3Sh/6QjFH53aH0dCQ==","shasum":"b91d167e2930927020237f46675e503e42e5c9a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-project-contributions.20190531194459.tgz","fileCount":751,"unpackedSize":3228585,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8YT7CRA9TVsSAnZWagAANSwP/2bm6DtetSmDniRyAnSc\nSeU7D0uuzgzWroqPE6BrgJPnNfDZMabzTmiRTiGFOogSzBhiFoObXFQhbLGy\nBx4DQTxA7EBw47ootP9A4Uu/7oTgWN8PqnM0YFF4WRNk/DYHJtYE9YoLULkL\nJZcWRWevOqqabJnuApDSosmcV5UQwJ+RIf84VRwXxXLmsakpiMOUl8OxJPA1\n6LKTGm0AeS8Cm1IhcqNl72p/tcsvfpplr/FOaYe/cxQKKqkNDvINyT8VhYGQ\nSl4ozmZUqR0YZNWiAennOPrVOn9Y411CFVNjGyOPZ3YrjvWq2KTbdwRYKgcU\n8Pxr4VWosQzJPUgn7aTvAa5NsG8R2m8hsO5Kwu0CMfjhZdR0dpvum6ogqgKI\n2co7bWy/SFB9YapffC6/rIUQEeTg2RoMp9nsXl7NUalwXxObwLUZp9k3hjCW\nXqSF4fsksPZjOiuVSN9w5NVPzR7OlTiTT8t4gyATpvuJp8dGNmoDfzGYZWfv\nRAUNDvMrDIH+nHXno/Yhm8Ar5B3csvxodsxKZwFc7WqxukPUGAJhNvCFwnFG\nPBHKMguJVxBgNj7hmrO1XOXXWEcMoNeGcNcGvPxtxENXDvSsJHjKyAZ3fipX\nFPpYSYdS9FsJ9UTSllJ36MDHgOtWt+kCXtrCSmaN5XD6gWADVi85VQI1PwHg\nIUwG\r\n=0/4X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFwV7txOb0+JOF75rhyhADwu3QNikyECwoPz0ahxr2lJAiEAxm+iLP3fGZIagc9V/IjNPmND38DfwnmcMUJuLxFFP/4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-project-contributions.20190531194459_1559332090479_0.4089273325305145"},"_hasShrinkwrap":false},"1.5.1-master.20190601211624":{"name":"@atomist/sdm","version":"1.5.1-master.20190601211624","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.2","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.2","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ea2ce5cd3e0dc0b7cf940bed8e974c0405387aed","_id":"@atomist/sdm@1.5.1-master.20190601211624","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-ttoXDU3OGzIc+5eNqo0fgD4eJfF9/Y3wKAOIrevGw++rAQDXYsAoa8JKzJleE4Jr7hz5dx9NAPoTGTWuMUWURA==","shasum":"a3d416ab49d3db12a7ef6bf85f08868c8569c297","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190601211624.tgz","fileCount":748,"unpackedSize":3222569,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc8uw5CRA9TVsSAnZWagAAxsYP/Ru1nne0CcPiZhKMgiCN\nlPnxerhE9s+oBRmYURxXc3rTSG5Eq8nsThsZDjKkhiSq5rD2ZB/e1BJnHooI\nWMc3+tLNaYz23lRY141FFEWgl6SjkLS6b8Ep7y/0OfOhceeX/jha18Pfq6PA\nkx5NISMxREgfzmxz5mYx38wCDqtkJSx+PDz3l+F5r8VukH9w4uJD+r33FumU\nDodxhYhg6hyWQ25LEPzOlOp57ME/zy8+PtabpfKsmUOZuB8pD3iK/AlI5Rh6\n0g78Sd5/DXLrhCESbFDAxpYSR9qwZHSt02Qqg+WXGXZicvQRvw5VLuZgb8OT\nfutVwvIo3+7eeUZ1IIRhmNYwmSpPQebDKtP/d4xmZBUE8oOjIcGPiQCD2JuA\nvy2LR6d2mK51vRvY8MW5LJdwesVVbLr2nxUbEnUweJqNJqKBpep6BtJmW2JT\nSXjvtUbXMbmqPSTTb/VeRKOlSOtigixYQNv8WpxHjziVv3z5Ku2MYHo9goR7\nwXOb+tH84hg5DB9X5Si9iHxUvlB+fvWWUPC4dNWmirYOQrGBWWLrKULaXluU\n2Po+rGAyGcfg0s4daqOfUQDxpbZUIm/MmZULeV6kVzLcKJJ3+Sfs3Yln7+sc\nf6w4XaEEYx1xJAu8V1bl8rcv+eXsne1LNjsbDjglH5vffDIvIfA3YHe5/LM2\nVCAE\r\n=yq0W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDnW+1kr8oK4jzVS/WMydhzQ2BZV5ra6cWWn1g4m98KXgIgGZWlouxTQzbLLUIMc8VCplzqeKb2ZoftX842QYBlaag="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190601211624_1559424057025_0.8786540225211024"},"_hasShrinkwrap":false},"1.5.1-master.20190604145220":{"name":"@atomist/sdm","version":"1.5.1-master.20190604145220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8f5d2dacf8836446fc87fc46798c89a69f758a4a","_id":"@atomist/sdm@1.5.1-master.20190604145220","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-2if0VvweDL4SXEj1ohCsaasKK0EdWT+H1WB3TGpWxy+o5KiGOHK3aFtX2+6xMEG8CF4iiGXwaaLl+g43FTAO/w==","shasum":"15764c81473c0f1be8eee7ac2c973226463c4a63","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1-master.20190604145220.tgz","fileCount":748,"unpackedSize":3222976,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc9oZoCRA9TVsSAnZWagAArOgP/A1Rk0zHlTb1X8YII2AG\njqJhyBsBN6Gp0BZCX7XO2+VjbT5Podom9LzYuaA4iDfy2QuuHIfJtzm1haRp\nRyjcWyU8yrRtgJjdK9fYntO0lNL5udIOME0zF9n+oiZ3pMjDriCob5JTbMnP\nA7J/h+ZZ8w3qurs08rmxu36WHXD7kF5GfAeJSv9uXuUoXzUYLItlqZy6r5SF\nCURevbSAcF9r+LCKdZdZ8163bc072FhKlR2dbXyl41mkaYi5Mnuw8C8Z5kpC\nU7H2c3pUsVBkD+ca1Q7k3SYA9IjA4SNgiRb7JtPTQi1dgaWwdIJrwJCD6U0J\nspasgSYHQb6ZCLS76LtwLktdfIf1K0PYMN/l2rNgwImXVQ+4gTk4VRCVPnV5\nU8Lk8Nr9JCpD7G/Ncu6N+O49ANozGsZ+LHdxwjpq5eTo1IJZgpeNBN7F95nf\njOKH2OFL4rveRuR1cYToCodExkYuMIXgVUt5SN17fLu2HLQHphVtAHGWm/ed\nGh1PhWd98EtiJI4oxF9HuEerry6ha85DfS32wZ5mjJnXOQ4tLMGBesXVJLt2\n6LdEONosE3LMQGQtV6I0ARcJ701CJe2qE7Es0VVFp4Y1CHAnfbROXxDgkxQX\nsG51O3Eo1FAEHS1Nz57gh2BX9zIhQ/yrdVRMpuH96BFJc9IgPWWHWM5YkqZA\n7005\r\n=yuW5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC5BpPeC3rTvOfM9PZba7IFghabJfOLMlsqYECpTOEIzgIgNLVXKS2ie9QkhZCyXeFTPgL6dkGhayaPQDhOlF1+xrQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1-master.20190604145220_1559660135411_0.8066252631796"},"_hasShrinkwrap":false},"1.5.1":{"name":"@atomist/sdm","version":"1.5.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"8f5d2dacf8836446fc87fc46798c89a69f758a4a","_id":"@atomist/sdm@1.5.1","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-/6SIWA35UCOuuoy9XuLpo/8m6sHTkRWx0MmJVVLSbhid9AMfKcpXT2sOv0LpZuyWklYBCuUuUvMrLsrDUhRYeg==","shasum":"d6859c4f7d0473f75f9cb318f2f9e2281ac33866","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.1.tgz","fileCount":748,"unpackedSize":3222954,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc9oejCRA9TVsSAnZWagAA6M0P/32CWjzWPWSYH732aMwY\nOYNkGKgx/GrOvQyKo6CVyWvbn+NWEsF/wdQebPrBE/BoSg6bOb1a5GgVEX5l\nXx7aLCefFUTsKrOJk21GiwIQncbv3bcc2iluaTJY0pZ+sgj9NXJ2BoDpC2/L\n3WzvdTXOfFdTT7n4baiCoAxrVrm3YKgrSP3+W2Jkm7DxbjwS9YBAxANJ7iC3\nrASHRZUGKubHZTmaryrWcrCC5Y/9aR3oDamzUQAuuGa6veyhbJsX2wpL/7US\np4SuXGKwl5hQaFg2J6oc9sHuPRMziA5HtaO6RJh+oXgBFB/KQ+CORNb29S5N\nnsdoZfHwpBuUi/VKD4bNLw1CZ8NYyp9jrGSwHWvEzvWYNI3ahy/jiZTGpAPO\nVu5hU40vNWQIhn0TiHCOsMlC1FZLlpqFGooiwMSckpsBtqHBea7JsJaSlKfG\nNgcacTnD5W4kkigDvbs7y+rCs/CVFmp9hyZ6vwLQgsvj1WrKVriGPwJIuvvx\nPIhoH8T2pfmlZ6CXHg3CGzEHnY9ZmVx7LuNfB4OUqvLroVxZ9z0wgmcDEAUJ\nWdBeVbeygvTd9uE++j4wNhmsV80RGkMXf1iII8HpkrhbeMzyvx0r/hxe5uKg\nLaaSjbyEv3bUSGFbd4zmGqdmqM0dhsq2dWK1TTwYoTuGf7NX6KZSf8xHD+Jv\ndPGF\r\n=/Oaa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHnsoyk1gxA2LrLWypqoGsEBGIYFksxsAi1JBKCe5ZAXAiAPogH5BElA1RmX0qshGWpCNpWPrWj/j2I0TjPb/9CBwQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.1_1559660451101_0.1498631760029685"},"_hasShrinkwrap":false},"1.5.2-master.20190606192339":{"name":"@atomist/sdm","version":"1.5.2-master.20190606192339","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"425a86793d72630fa98f84534382a677f0eef2f5","_id":"@atomist/sdm@1.5.2-master.20190606192339","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-lVHegC1oHGjM0xwck+4/QkMG2F2k3ECJHdjEowE0BBAu9ldMKafO0HSpGKwbv3WmqnKV+zXJ3sv/HkKUykpXQA==","shasum":"5f8af6c0c5930f60c07a26b89047f62a0de015a0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190606192339.tgz","fileCount":748,"unpackedSize":3222829,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc+WkDCRA9TVsSAnZWagAAsbUP/2ztN6dS9lQJr04qxjul\ndOycJ7KM0DTUkf/RaQIP4fq4yZF87hRJBOhqF25xI8V6JmlQK/2Gvid1SY5R\nWGE7Q1hRZ2/4lPDeIOyvjoCpPjtgHTxEEoO0a6wpI+Bmlprp+xHrvSD1b6sG\nQ1C5AAIWX4l4p27d6Z4aCzIcLS38cJs13BHDKsxSrYmeHCiYA21idjReNA0r\n/edsmu6JwPuruwYo2I84C1HXgV3SPN7SuXhki3dnRPL336AoBTyrthDAKFCb\nfNYfgDaitzZQg4h3fTRFLMcD5ZzJhi1EVk6ZXFdAh2k30EMXZGkh1HMprgxL\nyCH/SQ8pNhB1H79aNMBwkX4XUT3UZOIG7rxDkI4oBl3YBcnGKkO/ADK+Wgpt\nz7KM8MZZIUDNNFO+32FrX+bVr9Z1iZm0qv6sM+PXAlrehSMqBLpIg7D2ujiT\nLQ2OYZMy/8PxbTwxqS8wBwJn7N0P+uDH8Idhrl/AxtBbv9IAdxa1Ukp0cWFL\nFNyfC2+sMJzm7gCYUyGwLi7wsmJrkEZHp/sEBgyqyN91ZU/oUSn+blWIQsoY\njMz1+XQAD9yzvwn5bvT4ZoHY8+sYzvUwLhIaqPFtMPGbp/74WzACgKd0WpVE\nYWjqbrKmqjZwZiZrhkWCKVjTydPQH+hhH84SYd1O0wX/OqDysipyE7yYvZUQ\nX86y\r\n=5mtu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrGVS7Lf0z/i0BwIRIERaE0jhpMXNQEqo/kJfFh3sx1wIgH1EYycSH2P/a2+WASEAkencqizQ5HXcb0lOoLOPANfQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190606192339_1559849219047_0.39135825542253144"},"_hasShrinkwrap":false},"1.5.2-master.20190611171316":{"name":"@atomist/sdm","version":"1.5.2-master.20190611171316","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e1f79a07bdb4f29f332a9e9fb0cb9f064ef94f57","_id":"@atomist/sdm@1.5.2-master.20190611171316","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-OJW2obbdKStgomBb9CuFvCyVSZ0l6nFf+gMejnRafwp59oWbs622L5mccml2w5Yji7HxCxBonefcpxYcUbhQHg==","shasum":"136f8c6cc743ad89eb26463ec5db81a813a6a838","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190611171316.tgz","fileCount":752,"unpackedSize":3339405,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJc/+ISCRA9TVsSAnZWagAArmEP+wZUBEMgv5nm1mfSvrfs\nAPD4NBXG1uZztGQfVvgKDKF1oJxM61ahIrySkrpUegHvvbAxfdqKz7Y2SU8J\nrovHNAvo/AujUsrQdmaKW6Snxj3efb7hcZ1amkomv7Dq4gdhvh98qI1geXO3\nVLxQ1itRCGiouJYC/goOoC9n54bF7pkfteTgTCeEdZGAinPO2XwbpKebQZnQ\n1+3dnWh865GZP7Eeo6vTw+X8kz43q2wj+1JIGgfaRosSYaA7ClWOE4HU5k/c\nW7L5IgQmvB2m2AW7QsPLfkoYqBa3MdDydk0iDiHpa3XUFBTAi6v2FaWveMoA\nIgV/dIXyVB2NRNHjBBSmWJf6PXXhGkxXPxz/BnmtkSyg2M2iZ+q+ju5OQxyY\n71MoX2RIwVh5GmQctiYzRGQ3hiomn6pFSZmFFYzUmCosTG9s77vIZXXIEgN8\niRfuc7VXHw/ZVluDeCKnleCOt+01ZSEiAalwrkqy+9E1wzoH+QrJwURRNgvl\nfUx8xFz34H05kyRuKOwEzQe2tFs6cko9CvUb8xO7C95jaBTm+o3TvYYTudKT\nwwB3tZhCRFgGqwulIT7TZdT9Z3/MfLkfDu87p+x/NJusT7ZeqahLVMcoraXh\nSoBxcX7phwkaHXJKpAlUHLvh3q0Mfy7EnznxsG/Aw/21ich7kDcJbJg4mE2J\nVkx3\r\n=l2GB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDsOu8dqfg78sB/7HAjGwsmOnT/5NbRX8KIvn9vvwFzwIhALwU/MIZLxmdh9QsjhzVmi7J+VZn7Mdu2y3PwYhFUPHe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190611171316_1560273425768_0.7406799820604026"},"_hasShrinkwrap":false},"1.5.2-master.20190611205206":{"name":"@atomist/sdm","version":"1.5.2-master.20190611205206","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b6455772651e38899dc392fb1f07615bde7777c3","_id":"@atomist/sdm@1.5.2-master.20190611205206","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-yaxUY7NJUjoP+5+mXi4ikePycA7MfAob39Ajscu1lypFIGCkbNpVifqi/FPBczOFJo4lPEGqpObwoca7WbTn+g==","shasum":"011e8ca3e2f358d5967d995257a028dddc1a5f67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190611205206.tgz","fileCount":752,"unpackedSize":3340825,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdABUtCRA9TVsSAnZWagAAHmwQAJaeA2oOc6EAYZb9iElU\nvVBfTqaoDh+ieJ5ZoOcCe9evn9lxFN2syjq9JfARSUP2DljyXLkiWkFA43cS\nP+EjKWBK6JEmrSA27hc9wGQdvbSinrZ+nTwuf+dcP9dfRFyK2SMuJFEH1nLb\n9OJMGytfL2nD09/v7eN4LQlYSbLOxPvc3vi1avytBDkSf50xUr1cYgRVxtGf\n+IDOiTKvL3q2mDHTFL0e04nEXrbYsct9D3JcLiwABnY/N+inbSlGxxIGfsVb\ne0WWm3y4YUrLCqdIHRvI7xYR1vR6yKlS44m0Qu7Os08vSKc3fw1J8AYWXLBP\nGijzMB7Z0ZTODwDNg1JAhA8keGu77IVjwa/Y3bK57N6oNtPJqWRf+Vv5Tdxp\n947D6/cUO0LbFwOHGmARuwgt64ePBlzoXWacEKQDSyL2d/IoqQUeIHHmrn7M\nMeKpajVoRNkfHktOtNMPooACNVPlWh9ykWKKme35gUglacXrjmn6D8Dlnm4n\ndet7JSqkn5LEHlSpX7gXfKBZQ42Ow1W3uvdUgw+PG/g5BrdJjR7tcp5pDabk\nD+npvFm/qyURKV7m1xpN77C/WAcGI/y3aIlKa+QjXH61XKFonQzefUOfLUAj\nEP4sYNvXmYHtjOSd+vHB/e3js2oQ/ZPt6JRKiqnEnw/tg7yMqI+kRbTbZ40E\nujV8\r\n=qJMj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG7UvrexwmlRSiNhI8oOiwwZIiGD/ic/GqNbgXE7WaeAAiAHesbhogVOS2QZtyv9Jw+Q6qrhedmm0tAfkcEgKKk6nA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190611205206_1560286508989_0.15113305665237742"},"_hasShrinkwrap":false},"1.5.2-master.20190612101932":{"name":"@atomist/sdm","version":"1.5.2-master.20190612101932","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"43744e521c9dc00e2d5e39e78114ff26c6789801","_id":"@atomist/sdm@1.5.2-master.20190612101932","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-lZ5TkqTwST1upwI+ww77EbiAYGpBMhuQskrINod/xPLca79hgoZzbBE7IWgJRZAzP2G3irACq/vNSbj5O/McgQ==","shasum":"a8945ee60688a8fe031fec7a8d932fd352bc6d23","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190612101932.tgz","fileCount":752,"unpackedSize":3341125,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdANLJCRA9TVsSAnZWagAAI9UP/Rqsgi7vaI02M72KL8ox\nZPKfva0bNhpscxWY4xeHFlxBScXRyKQ7/Zg9y3OokUYaQw0PI/n5I62vJU/R\n4jCLq1v/OSzyIS/MUBlx5PtKwzu2kpmgFD3qyeps2bKzOBGPQNohN3TOpTEF\nP7wZGdNcsXfvOMcst88qsBgiYiZj5GGswsdbGdjsglzu/bl1dJW1UDi7K2BL\npNtW1SMtqDsmiim4N8kYBsybr+Pt2txFPyxMnHdf+E2Q4QQQW723VY+3mNOi\ncbQMXmGBzOxTqZnNctKX5ddiu/3/YKhGjfnTvw8RWyLMV9M0iGnwB6p0QbCO\nR8f5/dF/x3rdErQoTXTtf2gxfIP1CA0TQbw6BXWR0fY/reL/glS9UUH3tcrE\nMz5G8kjR0gzL3qH7Sp6QbICePFQbEkj3uOjFEhst58wAZjdyRZu1z0N2uMUR\nx8kg0htEQSKsKU5v8V/1R9ww3nXNAoZkM2VAWYsfKbuKvS9E01Dc3q4gt16r\nBi2IMJNGVqjiIFzaiHUvLygmiS8kdKCS2ujtqr1qyGMk9i6QzFFF5SIWrLXc\n3qyxBzeeR3bO1NYY1LWDZSaGZyxj49vWNrxVvR0Tp15yhKV5vpaURIO6HGWS\nMqzAiaJKEgubxI0AFRlgm2G43uTd0AheWYqrH7cC0Yyx8+Zo0VHIu/uRWW69\njUIa\r\n=kqDG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDFdHthaCNBsPg/xxWid0fVGBb+PvfXO/8DSZAPMILjfwIhAPWUNyUbjpEOKsYmu9ONfhyGO3V3NbvhL29Ek7C/T/bG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190612101932_1560335048324_0.7155580895659215"},"_hasShrinkwrap":false},"1.5.2-master.20190612102726":{"name":"@atomist/sdm","version":"1.5.2-master.20190612102726","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"808eddb6016a45091e6d53f12ab8ca2d1cd7fb3e","_id":"@atomist/sdm@1.5.2-master.20190612102726","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-8xIwa3S4XDKGnuhWiHrlMBZJZ1Gh4RFvogApQBUM3P+sxaWKqUgGIIOLg+hxIIZuB+lJW0JNq7DwDcLYZTgYjQ==","shasum":"8bdbf07c3e7b96be895a0efa875d39cb60f26b19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190612102726.tgz","fileCount":752,"unpackedSize":3341124,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdANROCRA9TVsSAnZWagAAXTYP/1h77kw6B2EnDouq3Un1\nRdzv6tcbqbTL+VYNkxZuTtxZ+S/tNylVtjntXB4SEz08E2n40iVjUx+7RMOe\n0sXeiLb2ifVnD2/ttazyJZbXAZ9EBXc9tkP0ogTdls46R+prwTONqqKen6Fz\npBJsdovW6of3GGodlKh1drmFi2wst/Bm2r862dpOqkQUToI/05UWvtvNrbkI\niifvjYzknQHQfHWUXFcnjk8+/QgeFnY5pUO+b2M/0POMBF43WaxK2XZibELP\npyHji/aHb+oMYWZ/Iqs1lPtj7VtK2uMM3IotKEtwTsQthNFYWSypBwzKAk5f\n0cTJmr4vbErGqPYyKz19GRNSoME+p8cfjp5hyNramnXtyqqBDbD9JYD8LJ+W\nCyJUfQZtopBR/aM9lBS+jG1MzZCjNmmJyGvT1WETHWSndZSXB6VMAhN3YWlN\nRfIaPdPM3uyu5AfrENeNpbTxB7cXU/7SunEubNFoUBOfXNAxl43vvb1OwLQm\n2zqMq2dt5MYqUW++Y23iKDa7MyCrV3lxpdVV9WgOX6mI2W45Cl6H8tdRV+BW\n2eqlcvU7j1akJBzGNs0awPsfybcv6VbpHvzEnUKCZkTdDP4pJiHnXZHxI/+h\nZ2qmsbHGSot+ouO4O+K6yEaQQ2U5t+wTCPCmz1c581MGJkgUEgC9Zl8Fx6Rp\nQ+MF\r\n=fkKh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD7YSztM6yf2a5Fv65a/UFYdhsWNDH+PRkLBEXiS94yNQIhAKqbSYPjOKN4Tr/gdpNHKWClUtRwX2B/BqS86+P83Lde"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190612102726_1560335437896_0.4944855780192816"},"_hasShrinkwrap":false},"1.5.2-master.20190612110323":{"name":"@atomist/sdm","version":"1.5.2-master.20190612110323","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"33eb4fe78251b58b664ec7da0a12e1a957125e19","_id":"@atomist/sdm@1.5.2-master.20190612110323","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-UEiOESbu18fUjYgzf/gslL+sRi1zQFyxI0qO7bf+GWKhj4frTmk/uag4x/pSrel5q0lBW/ev+Obnia0QskXxMg==","shasum":"01a6bd3e7eb4547ca2f1e3a7181a21c6accf7df0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190612110323.tgz","fileCount":752,"unpackedSize":3341141,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdANyjCRA9TVsSAnZWagAAwzkP/iVZiSwY/9wCK1hitD5t\nYfYXdlhdJCZ9g3ZKHg9oHjsOx9nm0ojrNieSFOPyDhPHEc8q2purdWMXGcUO\nkLHo460XBaGwuAHhnn5WMbb2/KQ6a2FzGtjKZflzvLb6Hted0jV3dFU7LW4C\nAA+WLk2HA/W7ljs4r5e7b2tP6+/EPkG+LaR71dv4+/TDxXLTpqbiDJYB/6Tf\n/uqg96Xei32meubqppyizxvtUgYQ6AiKoB8S9b3bIFTaf9Uby3u3tCQ4xcF7\nBX12d2FrwXDWm+gZe88F6FJTNmTAj2NRJ6paVEwLr3r0SvZX5I5D9jJE5dBl\nqawy7OP2eG8GAAF5YnPUX0xXrOoNM8nmiNyBfnF4V6y3TpXbbbirgm+9qKMP\nWcPJhDYESOfVPmUaiRfOS9uuhKS3GxYu9iiCZyF+gNMQ4+iH3BdieGarsqYE\n0To+Hw38BmWTF2+lMLG5bWkn5dqd9RR9+6F4uRFjuzhVfI4tDDtH/FQ9WGZQ\nKqOBCw74qPSlwjs3GJiNlliGZKx3DlfjciGMZXQTxqoLyTCOvjW2fjQHMw3q\nRvgQFWpfPq18mIHUQiW791WO4Q5VK1vi535WE98CGozCa/UxXSccxu/+xU8R\nSev0/n9x6JMDY3XUX1Z2Ou03dsxFfDMBBDPiSrbEBkGPbdO3rsxvMwrhDMV+\n7uBG\r\n=S3Wd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDjD+J0I3mZimBgdzUJ59d5IsYe8BE7nBX+e+8sxWFypAIhAN7tDrHjgdGrs/hpMeB7TssTj9ZM0Hs2J5b5oQc4tAy7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190612110323_1560337570725_0.3910281394648638"},"_hasShrinkwrap":false},"1.5.2-master.20190612123215":{"name":"@atomist/sdm","version":"1.5.2-master.20190612123215","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a95c0ff59c09fd45f65950631c0d77337cc3969e","_id":"@atomist/sdm@1.5.2-master.20190612123215","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-CLM4yjzm7M2LCUz+MYtm3NRvUp8AOit8RyUc7/JrCMMuddRID3sv6txxhJzuTQoGf3AmcbNQx52fh1ddY5qq9A==","shasum":"d7653e8d9aa0db4d9b96f777c1e284aadf10301b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190612123215.tgz","fileCount":752,"unpackedSize":3232685,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdAPIUCRA9TVsSAnZWagAA1U0P/0sW2982xKU+r+gaVh73\nIf1vT9MZ68vz4xOw6/2w8FshW21OREQyWYCiMqFIALJs8i5ZHRpUjn0P15qx\nXunmcopzKy9Ow24qU3GZwmE6n++MFkVBPylHhHtkItQYM+kofewPjQnyHez5\nxf3Gfg179qA2fhur50DlaCaQmn/fczGUQv6qP4zkXknR7fbHLAfZiMA5UCXK\nhr5vqde3BxFPlmX5yVFWKqMFjbUZmCqaRKJMUe5ueEkrGZVb1Ay7iit6gi/e\nDBk50xgj8J1HgTrUWb7BspmSzufSLHbBkWDd5WO29jBAf1ueIBllBD6hSG8Q\nmz75T43gPAHceCrsX8Qz0dR5XFFJREdvTtQQRiOb8tS29SXv3vWAtWXP52yN\ncXwfdQoqd6FbwOI7vCHAZEicB2irnGy/85CqkncYywgfYM8mekFf2MlpqmDt\nriNOv1OeEL6SGK+j8SGtVuiUMhvOjqFMIXx1XOmnavMvzqm0VPrGR6mwVYha\nmYZUKEoHjxu/xfJ3U4eEmosEj7Dai+FeXlWgWPnlSKSwu2z5NZOtuuURdold\nfsvxEAuqmLFu9asdpVPyDa4dZWV1R58iD4Dbz5yLg0s+8I9AgamEYmMGgSp7\nESWd1kLK8MV5AN4rC4Frnz7CWYO5f38zYvcU0V+pWanG2H2GA81+cLsWUtNB\nStxr\r\n=ldKI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG3ByncG8/KYQ3Zu61IelVXwUvH1DXkmAb1vkIlVO5SpAiA1UyQCPwuI4C5qayf91pwo5rtAr/tTQPcIxwu+6I9Tlw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190612123215_1560343059214_0.052697589018404045"},"_hasShrinkwrap":false},"1.5.2-nortissej.definiton.20190616125658":{"name":"@atomist/sdm","version":"1.5.2-nortissej.definiton.20190616125658","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2209fe4a1e9eceaaec031702f7d3b6cf49e9ac67","_id":"@atomist/sdm@1.5.2-nortissej.definiton.20190616125658","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-O9A5QHekwi2EAeSe2DsmHOPy4Du7f0pViLIETrxb7SWKmyiK9WTHiW+g4l3oV66hHc9dOk5fAn20PlOlzSzbVg==","shasum":"bf5bedaff7d32c0647be6c5ac911f8d897968721","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-nortissej.definiton.20190616125658.tgz","fileCount":752,"unpackedSize":3233101,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBj15CRA9TVsSAnZWagAA5xEP/1AyHo2QEIc8Nqn6eOl4\nV6blnJ3b5iGQeHoe4nQg/N0RQoKMAi8RM+0opmaLxyHVND42cmEmWpbaVO7P\ncL3pGToh8Te6uxxzurAw0EJXskrhQ39E5Q2D5z9G6PXsqvYdRIzS9+wXtkny\nOsF9eJ2mQcXM978DhJRvYdq/GcWpPCz0wAC2aKSMit6gsBnnFSpr7Ey8K61T\nTAlLrvo7lyfDASOobgObnVXH8St53oVaaGVlm4WPNp+IwnTQGWuV5+ycrf0l\npeNuIcQSgMVWac9p9gb2D+m/m6CksHGqtVDYVc5McryAbDcp+/TjxLFSG9VG\nx2Enz1VzpESBX4RKykvKmxa9JonGVfVW3TyIGSFEYtp9GSscLHRyGfCLEWuo\noj9W2rWwfjpyWGtgJcaphXHB4Oqw1tENvzprdUBOOmAej4LXcnYnlKuQsKT7\nnA19/qH9n9byVIyyTme4ySGhqeCr/XgLYz2qMnQVivofx+gHkRpNSLlY14gH\nGspwqlNYHTqE9HxbK/Jt5EjXZMspX3LI6+9hS8W2D3dOnNF1LEYRZvdKiRxY\nutKHSQiS3oYzE5FTvrGoyfwOGlVJgXi0l2z3rNDiQZSpPB4flLVAS3n5PcmP\nlB6bJqndaWM9aE9B6VwoLrU9oZnjbiC5nvNR0tbo89vVyFYFJ4mzvslsRsHL\nb1K8\r\n=QA6Q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIElnB0PilObL94z1ySue4aOPOlxYpDFb7SEdmGp+BxQXAiB0YRE6RyVBRHJEdFzzGZeqir7+zkjbskFGsTENesvSbg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-nortissej.definiton.20190616125658_1560690041134_0.9528774368598827"},"_hasShrinkwrap":false},"1.5.2-nortissej.autofix-location.20190616160706":{"name":"@atomist/sdm","version":"1.5.2-nortissej.autofix-location.20190616160706","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4cd5ae3a0c74fefd95a09f545b349524b68317e9","_id":"@atomist/sdm@1.5.2-nortissej.autofix-location.20190616160706","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Cht13pXcySVfJPPaxAqHbQBXV5RflmtoUEfSceCuJtaiKuTGrXskT37RX5GDBdZ6PuxbMNaGY1VQY7JmQdsgCA==","shasum":"23534010699cae808368bd30dd3acbb03b9be7e3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-nortissej.autofix-location.20190616160706.tgz","fileCount":755,"unpackedSize":3239537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBmotCRA9TVsSAnZWagAAwU4P/2oGy2Z9H1hIGc+rKGKb\nvfgy5U4aR5ym8fLPXt7QyQSJ7oJ3oyHva0F8uVnZFq5ofzaR2tDjHP89ohdf\nPZonFLa1nWpN+vDjTQIWxAv9bsomHkS1T6vY/1QZkIJ/h6KpqIY5DTcVhZic\nREYNj2wFWLi/PA52M+f4c4uglN9FjyJ92NgfTttJ4Ny/2tZaa3lb6K2bFNAE\nMuOyu1cnWWh3pfyNHpafYu3vVk7Ogxkk0YSPvxp9fi4FRbFQ9AzeSS2RxJlM\nEWO9ZcFDa9b0VMp55owcSkXtQmI2iylxhrMnbeYZldCwSMGmDZUoneHmmG63\nO8UJ/1cg8P3eRqQjusvOG227xB1gxX3VNvp9dGoo8wG7coJqmwmn7HHcaOyt\nEh2drtvBDP+cOKjDcxsykYCb3GFNMDss0bX1JM10plFzW+GFs38SExXBuF2r\noZMKm8HH7PSEhDWwxkvXlMcXaBYAcVY0p999B0bo8sobSX5s9kyAIB1tkSdn\nWXhrk+PKczsYkiyTyZcveP1UHziQWWMgVjDuz2uuRDP2hKfh4hdGi3+on/qe\nuc612A8RRRwuNIgGMl+9zOXl5VTrTumanHK6Cps8xsCsNLMFYpqt103f3E2+\nL3s+w2EUG0wMH934nCv6fZbsOM+CgUjWTsBz0Ksu3HELlEC43l5A1fXKfqhd\nhmih\r\n=3VUS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOHBzx2NEigICzI1essV3HXvq5QsFZSz92L0YkCZI89gIgMA6/jrEX+quVcTfopyHfocH1q4s6jIFb6AoayUZ2Qvw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-nortissej.autofix-location.20190616160706_1560701484777_0.6403279875122305"},"_hasShrinkwrap":false},"1.5.2-nortissej.autofix-location.20190616161721":{"name":"@atomist/sdm","version":"1.5.2-nortissej.autofix-location.20190616161721","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"099555d78a11d841f68dd2c3e3f70a732badc7e0","_id":"@atomist/sdm@1.5.2-nortissej.autofix-location.20190616161721","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-U1fYVr/eZbtcA2wMn4BuPx9pShoEy4VWd+M9vVLQgx9nKun3St0ccUvsQ5Hj+Fz4DW2zz6jtZqfjvXLNHQovUw==","shasum":"1306f4d72906269240b17667ab11c94351cefaec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-nortissej.autofix-location.20190616161721.tgz","fileCount":755,"unpackedSize":3239385,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBmxGCRA9TVsSAnZWagAAlPYP/1QladEeRP85B3piCpqE\nxl5Hv2T2hticojqBvoB1DKnws4ivTpOe/aZPKrmgDiol8QPpck2yvqCmQxHk\nqvAl0eS1hvxq0HwWnkly2mBdDCn84TOlrnQLYyiidmTn9Ltis5UeFUaEHUGn\n+GfN4+CqZcS58aK8jv0+0V0C/3hqWGSt6rcDUBL7eYE055pSRxolO+0mCIdF\n7maADKv2L47Ozjs2bRRbIEV1bWZRvQaUXgb5sxN/SswxsgzFjCFKy+nzM3Q7\n0Gc0iJJbuHiBfbHkXWC+o8OUTDNTuK9G0aBCNCyz7EcVoU92NAylE6dC8E+0\npEhIjSilHJJhWgsdvQ9lLykUNZUBg/A8LQXAadMbvNkPE/NwzZLp33SynAA2\nTeOXcB9YnEbfbRbrzSEAADYaT3g0eLofTL+eWWtBZIQJTcr6T97GVBCsXRPR\nhTqoB//v+OquZFxO9Q5/q37ZhbsCuj8IeXcRSIb+Lbhp0yzmvBz0JthWQ53+\nGplxHEQyteqK9TNOLBf3I3cUwve5kUVOphjqr2cQ4+vQiNxywE7QS03xjpEJ\nt7sCQ9sHkl/1M3w18aV3BTWNIj/x6ZNDBxPHXI34sc9wyhhN6zI1/huCeU+v\nxSqJ9LztU8jP5JeRwtrzehs5CB8Z72SJ2H/vF9ds4w6oaUYdhBIz22bWfczP\nM4WS\r\n=r5y+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE7e5LykoaJLsIjuFoCKNjIbI/eO69AZq9OFxIoqtdqqAiEA4yga8lvfVMOyVFg28PxqoVVyyArHaXWtpyQqd8Ev5/Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-nortissej.autofix-location.20190616161721_1560702021331_0.8954339553172008"},"_hasShrinkwrap":false},"1.5.2-nortissej.autofix-location.20190616162212":{"name":"@atomist/sdm","version":"1.5.2-nortissej.autofix-location.20190616162212","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"02974f1a6b1f4aa1aea939bf36c2f411e6b26e1e","_id":"@atomist/sdm@1.5.2-nortissej.autofix-location.20190616162212","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-w2FfuqktY57wAnNdyQ2+wgr3MQ6QrIZIZjAuxV7Ofk8+joJu8tXWM4WA1ORNRlS7ChZc2356uCZdo2MmhGCDQA==","shasum":"57af61a2ec4eb3fafc69f39f6b0c17f353e2a5ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-nortissej.autofix-location.20190616162212.tgz","fileCount":755,"unpackedSize":3239385,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBm12CRA9TVsSAnZWagAApioP/3yqZTmF1wtmqDxY3qDq\n83lCmckpNpvTdiQZoToNIh70B+7Q/UG7piT1WuY4fA4LEXGN1JmgWYQYjYYM\nlF8WR/8v5bcLQW2wadUEPrll4Hc9slN+LOBE/JDdbHuymOyEdyk9zb1tobV4\ncGE0xs/p1qlfOvoGINMroPa+AoFuUrIHtvQMLAoNLySMkwZpR3IhieyYDRdr\nf21/qP5pXfV6LPif2jlhCz7PZta7OGVvELiQbAgnwZVoVJtewhCCORIt0xrS\njGqjH7SaU2UFjMMwErju0Pv4KxzRzCy4p0k6vp76CbUvI0yPV6ZyrCRh0kpY\nuZH+cRPBlIWQBOsWnsaKgTU7lOHBl5Ll+1dd/z3YBjpvdExMlLb+C4zVDQH+\nqmAqgUfcFh0Ydyn41G9S3vZG5yDOY/sqxh+yCMHPmDgeOt+aYRVvQ+xTi1L4\nQCryB4r3CvN89hAp+wRlaABS8S/zBqEacbzmTlUOrI3lpfoh8RORc3F7yByj\nEa9dPDDedBYhjmq3HHFC6aG5bN9IGXrrz0meJQ0neUdyFhAz2CJ6YVus6Dqa\npCvb48i24KeGWIas8MpAqppujGoRh/512V2Y5+aczkAMcBnR3B/sfgS+A0W7\navN7AFi0PVsvNsn4zM4GJE/MqLEZhp1BwtnDYmlfz5TEgSL+XEjxHX/sjKVv\nnTdv\r\n=VhM1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpQYYoPCItQi5uWei+iaz/p1e5v2F3YuXE7OrRRbUBjgIgY1AlB3Yhbnb5dQv9IsRQnmFEV9hb/V6hFvo/FveLX8M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-nortissej.autofix-location.20190616162212_1560702325789_0.3379532877160498"},"_hasShrinkwrap":false},"1.5.2-master.20190616203149":{"name":"@atomist/sdm","version":"1.5.2-master.20190616203149","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3bf82261fc8606add8d075231b91aa77a7c23aea","_id":"@atomist/sdm@1.5.2-master.20190616203149","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Dl3KgFm+GqlVdufreBVpbccWZ1a6eaub+w1Qo1WHWl40teJNj6uijh+59fUOxBkCvhjxko2d0oKudBXn9RAyZA==","shasum":"7cfc92ef5df3591c2b6945d0c46882a755c8606f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190616203149.tgz","fileCount":752,"unpackedSize":3341527,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBqgVCRA9TVsSAnZWagAAJVYP/jsliRpurTGxoMLFV+gH\nUtJvBm4TpyuEPUNE0mgPZ128iDbV2zhlR4n4/p9DEQ6EIW+Usqdm+PGL3yeC\noQ9fn2EUPqhmhM//ah87Wo1jEUgYvgSpwCv6Mng2Buss35peK/DMq0dnxvvL\nrHCHFM+931ST25AWspl6fdphuM5VdP4ZUmYcO3w96SHlUo7SZ8r+Z9lsmsWm\n6j5T554ufANlUWbeo2EvDKpTvWUL4EEfR0VEjzot6pITGZFrkSiaajNHW8M0\nE17qdVQAYy6xuoTBW3A8LMn/k4bOvCX04vLOU+FnoSuPwHGoM/nkDRP2AoJK\n+N5/xbHfyDOFUXrwHAw823aQg8QsnjmtOtdCsfl9ijOlAzFzXiekNSj2qXeL\ntASLCBtEHwGk1ZgJ985TwJ5wDT3FfHRvQYzVLav9aD5C0SEis4Kcy/ElIrSw\ngguyHAJC4gM0hA2GWm4RJuytX+1dgfnJVlNcNjuL6Qu/W6IzWHFhpYbyssnx\n0UQUwdqeGdFpncY32U5dQv84t4EGOImX+oXX+m3OpFb3txCxzSu/Te7AGR8R\nAniy7+0axhvdNu/oqNoK2QiqemmrZasCXhafMKoTW6nqcY8XYP3Kq4C2+8MC\nO8VZX5k+WjB4ti+rZ6h4FPj3bc3T4JWZwnqo2m8OCIeaX0Yw4L0RsTacLFYl\n/cMO\r\n=HrMm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJp76embGMTnBvbtexDhtw/g6TpyFo+2bhrvuF3FmG0AIhAKt2rU9uuMZQeIuCXteTlkq75p67niuNGgP+cXsHEk+H"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190616203149_1560717332832_0.6957829424712667"},"_hasShrinkwrap":false},"1.5.2-nortissej.definiton.20190616203428":{"name":"@atomist/sdm","version":"1.5.2-nortissej.definiton.20190616203428","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fce0bcc2756c77c2b00d3ecc0282bda3b518324f","_id":"@atomist/sdm@1.5.2-nortissej.definiton.20190616203428","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-tCkEX8T8uIwgfOai06LchYxmXe6Ak3/5c6yyTIJ5MfR8oxz4iY/OkBQ3p6MKY96OtyK7U6wD438xH1eZ432Msw==","shasum":"dd9f8bc7405a16653ad875c27f3e5d274925fc87","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-nortissej.definiton.20190616203428.tgz","fileCount":752,"unpackedSize":3233208,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBqiTCRA9TVsSAnZWagAArKwP+wSD5wQ+Pgf3mHycG9eX\nZKkp6pRe5eIFsHtU4RLjqP0uXJGoUhuAqJwg8+itx+om1XFkeC8HV/Wydj4K\nioheCc2EywspRIgJCaOrA52z73KPH2HIZdJUzB1qbgjhTGhqcOvpMlknLmVc\n2vkEPFvRWl6XtiqW/w0Zvici8hcktMHpDftzBojyYj1tJKnSXGjHutoflz54\ntxINbcDH9UCIR0CFpD+RM2gTyJsYH6aC+zgXjbBOZOzIo5CN+YXOfbscDsOC\nM4eZHV/XuXNVAPBeaYO205yPOMN2tzD0tys/DCeDTX96k7hpNMiKfuTpzcgM\nSfdOysaG+U7V/whI71mr4oBnLy1UhzTC2cTcPMAtmNov8xsomAuFb7bHCZVq\n/C8RVZLd+BZdBL+4cPnBs/TRIZ4+l2ixMP8IGhMLfqaYtYpJkuIqcIjVa0Gh\npRHhKsRAzFELdf/fRD+EWLdocQx4sOt+Tk1/2EliOqHoRZb22dpVl67hQxwq\ne1szo5+BRzNN1w1bCHFTceKriusjOTxOTal+E9weIOy/J8o85IkzibhRzMaf\n5TQ1D5acn4MaLnneOGo95JwUDDYosV7Y3sl1/JqiFLMTI/6gfHPyzBNcRORs\nf4lh7DTdodJJf9zScYAesWRC6obXWkF+m+imH9IbPWVf6vYlwMcmsuONMRq1\nAvwl\r\n=TAsw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHzbo20Ri0xohKBGfCXfxbpN5aFjwjc+p1pFYTIjJNWNAiEAqKt0rjRvI8RDt5oicLjvHVWZ/JPxUCs3BvlZKr54A14="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-nortissej.definiton.20190616203428_1560717458712_0.1231876480641052"},"_hasShrinkwrap":false},"1.5.2-master.20190616203852":{"name":"@atomist/sdm","version":"1.5.2-master.20190616203852","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a489828bd59aeeea7d214c2b06e40b190f1d80a7","_id":"@atomist/sdm@1.5.2-master.20190616203852","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Xv1tkF2r82F2MClmhD3liEKWRdx27EhLG6sBzt5Er76UtS4s888DJtDVCbrumpT8tkQ3BkBsipNFID67sayJvw==","shasum":"00925fb9c900a520297954704f63d131fb1e2aa7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190616203852.tgz","fileCount":752,"unpackedSize":3341913,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdBqmZCRA9TVsSAnZWagAA64QP/jyNA07483BnqsWptINI\nWtkCneBrnWluL9CsyKvjE2VbNBaRRkF5l0WW4dn+cSG6Iu02gwo9pq0Bwnay\nmz8d1+vjUmPu0n8kndbdmefym12L3ItIJS4r/X7MZ5jq0+3my1sx5WkOf6i/\nA2zs25CCNxyXbVUvG9rGZVCoPrGBUag6pwTI/Umj3S6srVLAzHJmOqnGCcbd\nsfPDeDLOe7pRmQ2gsUTjEaCY2ofAndmkb4Y0gxK5vR5DMtnquWm7B05nqeV6\n5Ebz0lwOQsDFVYmfjHgNdKT4+IYwCL9bfeKhdkzQdgCMaohKiyo4Q57Umbw5\nyTIrVm48vZBDLWCZ9bLt5+knORWNHxq3mcJLjhbngsPJgn0qZVi3d0X39h5b\nBIznUdaDcI5aYaN1Ul6zXYe066wvWouiKKLEW9rIHLrutRuknuyBQ+Im1CsL\np9CBlZxXf6KYAyWDdO4yU17Yd0Tqxj4evDmpoM2cWtM1OEEmw/nfpYbJ/vXL\npQztecpx302puQ7iMouhsDzNOaBd0pPv08Pn+En9/FIwEhAvuxhrrVhpi1CS\nQD9AliRP9qykRSDNN13NYBBkmVxDQ8Hj0jc8Uur9kCk7Z9dfQx1u70g/Edul\ndH0e6DnAivDXBikszxGYDunwTpUSX1p6UtDrxy6Z/Gz+MUs+5rRIV9lCBaqv\nVRMK\r\n=aZf8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCanIj14wPzbTdm+L+yFP/tDnPffVQL6ty36Is0JlCG9wIgbP6HNApsc+nSaS+gGC5h2EFdw+mq4Hj+Mmoxpz3mNl0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190616203852_1560717721273_0.9158471057014046"},"_hasShrinkwrap":false},"1.5.2-master.20190621121418":{"name":"@atomist/sdm","version":"1.5.2-master.20190621121418","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b157603f176cd3b58fea3a471554114325ceb2e4","_id":"@atomist/sdm@1.5.2-master.20190621121418","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-j32Vmsu0jFxm76WCAsdWHxkCGzThhpVq6qqqUWS8asgVLAfe7A9ecoft6wkpSpHixagwKSC9eXtwqqd9HNq9zA==","shasum":"627b96b44e2d7c7359a5dfe5ba6444ee1b068f25","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190621121418.tgz","fileCount":752,"unpackedSize":3341882,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdDMq7CRA9TVsSAnZWagAACxgP/AtYyLO9ca3xkB7cJSq6\nR/Jc3YbneCTIzxt7jo+BqhieVufygdWPzZfISjQLcELzFh3OYd5E1ELyo3eL\nBq0uSX5OniRA7gCuIXALQufR+vtoiMJaWVjvjJhdSLPGVqd9nA6nArE062E7\nbNS38dptKOp31fMWbEG+fCjO/5J/x477j74rAlKLU+k30wE2dwrx86keG8RY\nJuOkdZTnZCHSz4FKyW+0TMME2VLKeua0XD3jAgs1q9fkK9GxlizXYqb4IvSI\n6yHmuFAPXnFVInJ303h85vWtO+YQ9p3uqALNq8JuVhy20+J8oFqwl/aFp39u\nTG2TzxJtV+Mx9WU9nvEzl2PXJMcDraZh1zzT8tgfMmRyjLIuYvMNFHiZQyG/\nCV8/WfRnouEYKv+hddGJLJilTT2NXvSLjYQgBmGvZlIZcwQl08SSJDIxyEtF\nIDg3UoPHNx1VUs8zgWuSssplhNjW2z2c48rnqmHIZ8RmEJlMJ/QxwwcC2tJ6\nNHt9nlifMf5LvIGQ4WxR0nh1M1tsIN+unGayThuOKCXwK2YfDkUMYDnuQXAF\ncrKAhqjQ0mFNL4Jxy27Y7RIPoxo/FDB4cWUt8DPwdK4CCPBT6w6ASiXVxUZO\nriXv6DTkHtGkaGMQm9VZq5GfzzoElX7OFE+/tCvYGfvq9W0T0lazraRQL+iH\nw742\r\n=IJa/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH7LWtUoEwQZKjapFPtEsc2+nykdFTvYOHpxybvIMwphAiEAyUf2n6h/fD/e6fMJj26Qjghn8bqlXCSfTRtnKZtfiYI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190621121418_1561119418392_0.8220884934208545"},"_hasShrinkwrap":false},"1.5.2-master.20190621132332":{"name":"@atomist/sdm","version":"1.5.2-master.20190621132332","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"172dbf7643a999de3e02339c159df339d259e269","_id":"@atomist/sdm@1.5.2-master.20190621132332","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-qZCZ8VKPDqboQA6QH7JiyHEEiOvrsvpVgi8ysUSUTRdBiJVK6k+yWlLDLw5eOW+6MGVijKzMfK4eOWvmkYlkmw==","shasum":"d2dd16c93c23e6536697b14855e342539fb36dfb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190621132332.tgz","fileCount":752,"unpackedSize":3341850,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdDNtFCRA9TVsSAnZWagAAM4EP/0JLRS9keaGLP66WyuJt\ndAdoHEUTQEgca9fELVRpgxetYIb2JVgbx+RO4Hm4yUQtoYM2sVEOtErujFEK\nk9DIPLC6sPjv7+Tdm/CF4GDV5jTZcD3Sp3igr18qiJEKQZLLuu9RGw/26q8Z\nWV6hHtUOQZ5bpFfAzOCgsCbrOUOuuyDK+zhc5cDSY/W/dWQNjHBHsMNfnPSz\nkouoSReqj+l/DecXAkF9xpjcDC78ZrQabyEbZLn+0+l1/eOTfSaIW9LDQO/h\nXgBoxFsA8v5zhQw/oTx9/YI4barNWuFsrrlhPxjlV6rnfJZiWkwmv1PAaZjk\nIBgZPQvof0Be4dpLdOwH8SFIraM2l+eZotz+IlHpMre1k5cjHepmylY78N4/\nLNIizHO1XG6/Sv5TbBRffbyTb5+5A3d2hhrQVfAsfUH0ho9Pc7eX+woBlihj\nH8qtwerooAdKPr4SGLM7RC9HW5T+W1JBJwmJxvM9NukPHbXT2ykeWCmMi15R\nxk/X8cnd3yUYLqYy07TpC0N08fmQtco/f2a9+FDdOm/9pwsS9OoaqFSXGz2F\nVKMjHHiONnoaqanKR0ddreqeMKGsoFgh1seBIJYgo8EVSUQORbmQnIro3a1y\n77hNh5OqsNs4cq6aMGPkSeNwhL94gq9G2+aNRCISPGmGKAInAJXzYal7eHqz\nnJs+\r\n=WtBZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCQKh4lh4JYozam+9iWdFOckiF/ncoAiDjeMh5oEE6cUwIgWXBPlqryuJdfS5mB+rSN3B/LvQ345sVmD5/ixjnTOOQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190621132332_1561123653094_0.5089461267038824"},"_hasShrinkwrap":false},"1.5.2-master.20190626145926":{"name":"@atomist/sdm","version":"1.5.2-master.20190626145926","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8c40eefedff9a31adabd574ea16a5bb9315b0eef","_id":"@atomist/sdm@1.5.2-master.20190626145926","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-QyLnIAtolCr0hTuQY/cz0ebKzNkGQZ8XayOTthSaOdSpOqNn4JN8JOR85VkQCxK4U8hJca6YTfNTEIUGMLfN8w==","shasum":"2a0947b38e578123ebae24b533358411e00b94b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190626145926.tgz","fileCount":752,"unpackedSize":3342253,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdE4k9CRA9TVsSAnZWagAAZCIP/jey0JrJ+0jBY/YAZCpi\nt/p9TBY6IauciSNldFggeyeZFAhHHuyh7BCWUwz4NYpH5rPpQfSUfF2xJzre\nqRY1R84Q59/TvgOPIlbptQDhLKnN8BH4uyqFgJc0EiII7QAo4fyxEihbracf\nyEgrsKSrykp5r5wElU9ixzK3iRIJgFe9RuKNotgGP75pwHQKtHs+kOP6UdHv\nEFTkHnvIvcEFsHWFitHpT3Vl9P1QnJyETLjocyw6JbptzPmG/QLThR1AQO5f\nOokAnYECoh4ebRL63g1Bmraa9HUo266Rljeri30PNSRK0W27mCWIlNeqzGL+\nFKR98fmCoaPwb6FshfAX4g8LqwdZFkoJkOCm4LmDYfHhRXQw+rZdHGfhTjR+\nHcxuP5Y6j3Xj235xwUexG7pqhUkcZ+lKn+zQcsPKT/yuEonOq8ArpWyL0Nh9\nZGvVf5Fr6NWiGZJutpm/LvWuB0Y4cRrG5iNzm/d7oPTVdzdtwlK/IWZgZg1c\nLYhqJVx33FzktE4meRCwqZWGleSwiXOWOfiC8MhHPnz8E402a37+DMhaJF75\nUwjW3EuLgmPL6w9tFRvbobjXFjnkEFUU49y/ue+gm/1t72D6Kn32De3lHm6a\nvaCpzHcOfzQSDObLiP+KrGpodXLX71XEWV58HjNhPn4+whIOHX45G+vMpewl\n7tHi\r\n=n490\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCXr6qIs6OCbK7uUnRgF/ejowsyW9/doJHEtbRtkoUcyAIgQg6jHveb+gIo535s15zDoukoM8pMrQTqk9uDJGshyms="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190626145926_1561561404531_0.4817388449140303"},"_hasShrinkwrap":false},"1.5.2-to-job.20190628100243":{"name":"@atomist/sdm","version":"1.5.2-to-job.20190628100243","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f33ff8192af76c2d5b66db2b0f07d4cfae937bd6","_id":"@atomist/sdm@1.5.2-to-job.20190628100243","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-wTHgjWhA7ZA9OCuk3K5HvDuinMMPCSVOUYGRNQ5Ei40uCMOQ09ytktubO0rLYSvX6+CzNRi/wCDGY6Ar8k0l3Q==","shasum":"9115a2411417e7d81eaadb5e87a0c6438471c4b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-to-job.20190628100243.tgz","fileCount":756,"unpackedSize":3312110,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdFeaWCRA9TVsSAnZWagAADvgQAKGBW9Po44kfG63xLC20\n9IIbuyajdTG2MYKZSZVV0scwwvSDuZwQPbPuxEH2ONOydsajWkfUW4J9z9yC\n2bMRcqhsvOcJIynMkyCddPkMxtByBWzqhJKkDZm5Hrq3r2mHTEfaG+o9NGgr\nySGrS9NpieS5xDYPwTDJn8cZ7SDiqn+5RJh5b+qk6kK2U25ZDJuKeVajXcyn\n9v3nGy8vRWs+cLN1GxSK6GCJed80F21V/xxOPsRHpbCfg96F8UkrEorFZ784\nH5mQgeTiInbTpq2+fjMeIQIVvilAHQ4jUwNVidf+uGa/VysWP71lfIcWolOc\ntEnIvqXxW44kvlJJPMWE0RofYH6ZLjjLhwt8LKfHxElUEil9DmzM6+CArK9l\nAubwnkL7dcoJOapDxvoxzGTZh4q7sJk/y3CM/EcF2jhuB3K78OSvT/Khq5s7\nK5FoE1RyhzmEqzm/i5BtkrAijhDhve9n94NTaWBA1y2X6hfZkhEOJTRRRb8I\nJVG0ei+eWbhjGOV/xYf/KkILskxXJTCzCla/Fs67Ac+usuXciNfnrarhWTf/\nhiauQJAE+1F3wUM6hlT4OjqSiqw0OrIfIZnsAGUGp81DafUIH3gWDaq/5OM+\nlz12F4RHZ0GhYVDyg3/Zw3xrIA3Y+d+MWQAP0hUXUFo8dtJhMvogD4YXwJvY\n6+eN\r\n=53Bh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE/FK8wUm67WpFAE31uu6ts8yKKL+74N4UDoj6gCr4huAiAy6kit7gJdgaw3tEzUfCXdkkg4j4mLn25I+zOkkOFGYg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-to-job.20190628100243_1561716373180_0.9834767952913135"},"_hasShrinkwrap":false},"1.5.2-to-job.20190628112616":{"name":"@atomist/sdm","version":"1.5.2-to-job.20190628112616","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ce4113a3a52e2b1b3a3d26153d01fcd639f96f4e","_id":"@atomist/sdm@1.5.2-to-job.20190628112616","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-0d/myRjvTbasjyoJTi/tlchKbntape+a2eJYLG0UrT9L4I/h6njm36acdvIKyL/wBkvtXM2T99QE1259ltg1nw==","shasum":"d6f4dc8a2eaff0ef3321560f8a20e3da66dbda4b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-to-job.20190628112616.tgz","fileCount":756,"unpackedSize":3314584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdFfptCRA9TVsSAnZWagAABaoP/274IRQtj6DvpNO6trJp\nPMRKRqJOQ8zRszExyi60fpiGu2g3FDGypsT1riN0sngMMVXH68nxdSmi5e0Y\n4WyqQ0BoQ9mkpZ3pjEYIx26AICYS//fvW6xmF8nDbA5MHd5u0D7moRPSqxGh\nn3MPUqvSLOAuAs4wJG/spAwvMuBAQwOAcnIjhSzbzJcYSxeOEsgrHFC2fZXq\nvfHXFbMLaPd7VkzioTfI2boKZRRiXHrI3pTrBfxGnBL9wH0gyEHR8tjMJDH5\npkIbd37amvuG221EClhMnXEaN5kjWQlwYNp7OVhfmkH1JnWYoLrlAFVGvthB\n09u/EzTPCvvbh68Kr+nnEken8qLAMpvUO8fzXXpEqVg6ejERF7jmWtGLe0Zs\nsnhuCm0B81XM1yCqs/6di1MtG/Gw8c7TSTh3qq7Z6oL9WfY53S6rAmL7aSqZ\nYWEq3dXZclAcnlY30oWplWCnXv6NKUxxwgJ+HLvvE7sJrG+xDE8OzbFrj0Hq\nN7IfGkCUfgfm3veol29LfHcifmXfu9n7YQUsVULe5c9S5TYgoaoWh2aymUow\ncWL5G83HvaYnPf/kYMgp6XjVDElDw/05vgwBuef4vfuMlvfJ1BEyWDMWpQuo\nrOuDr+k+xbUWEC2Tyo13lGlM0rJZMUAnn/NAlEjeUBt4W3JKQEoBdiZjE+7Y\nlWv3\r\n=tI9T\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCRdi7GWIp9aC5FrwsFoE49y2/JVKCmNk2L46ec4A2CSAIgBl3EBibO5j1CaEG0AzJl3QQPyfRGCpGknpbVyQRjVpY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-to-job.20190628112616_1561721453238_0.3830998175803273"},"_hasShrinkwrap":false},"1.5.2-master.20190702112950":{"name":"@atomist/sdm","version":"1.5.2-master.20190702112950","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8bbb04d0e21c625bdad37c98bb0d2339866817b9","_id":"@atomist/sdm@1.5.2-master.20190702112950","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-t4yUEP7/gzZAml7Yl1u5p7Lk8DDGQHMrNEONNCbf2eaUBZCFVpn6I25czFU9YpSrRBb/sMP8k7umFIxraWFiXw==","shasum":"8451a12f4b7613c1c094e18be2b0b48efe3dc0e9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.5.2-master.20190702112950.tgz","fileCount":756,"unpackedSize":3314687,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdG0DbCRA9TVsSAnZWagAAU9gQAJlCR5c3TFOhXSAHAtpa\nPliO4peXA2h7NH6xBCGkr/ikA4t47kLEskfq/BpmWhp12XAC9XXycDIOZBcJ\n4EPZgC0rw3JqkVdJk9MqGuPiN7+0WoppPg4n/EAXaprx+ZyXGu0O6nA5WI3L\ntSYBnTgSZiesxwOpB83SHIZIYElZuu66OY4Noacu74kn7rkE5bpm8pb5A7Dc\n4xbh8INSgrrGXb61UlBbT83OTnd/24C+N1uORtktJBqV9clmXaPCz+3CeiM4\nqTAT78gg6NYoHzewvq+rkVsD/ZkxhzXJlx3apbXoG484uxbv9v+ieZvhuD0L\n1Hx+ZKlN+JhQ+8HJ7ZE/sKM4I7re4kDWfcXX0XC9Y7RxbF+Lz7ttQ3TuXDHH\nEcXwtjrB0NnK6nR0SPZo5sZVlB6/+MLOfGEEI+PXU2/C+2mdCTiykPCMR6dg\nOl+gaguTX20ITzXEnuCrp2z/zEpv4rwqeuBbsZlySq2AfhfLVKTCpGpyIiLF\nmbfFuIFqLBQ3o6danRpMsOBiFqpapYyylsv4ecbNbr/418cxJW+HoFkEp1bl\n4zbtIFE5h36SXhM1aH4FyH0/FI8M0XM/PZk4cY9YtPUbrjTKtYW36hZyVLtY\nV+QOebOb23+4EMnneQFip7Ij3KC4GH/ck5bSClURLzrj7ifrDewvcWz780er\nZFjv\r\n=aIew\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCTM1Yob/32iJjsWPWy2jxtYYYYclKQBmnBIu5ec/XbkAIhALvHZmPd6s87cxUv8HRJDA9qD/EKJ4lXGrKWjumxR1W7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.5.2-master.20190702112950_1562067162272_0.0796768111326649"},"_hasShrinkwrap":false},"1.6.0-master.20190702113604":{"name":"@atomist/sdm","version":"1.6.0-master.20190702113604","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.5.3","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"^1.5.3","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bab307149eba3c8bf0b912159caf99891d9169c6","_id":"@atomist/sdm@1.6.0-master.20190702113604","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-GkEyYUg5EeEqyKyrFsQVCyRS03W/m2wNSQxGp1N0W1D3J1tN9JQi3QFQB2vk5jrYDvinN3vFmJHJSWiPwY3+Lw==","shasum":"bebde7cdec771fe24c31017a597f0a3fb86c88be","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190702113604.tgz","fileCount":756,"unpackedSize":3314687,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdG0LnCRA9TVsSAnZWagAALY0P/3b5aMMRAPVzfFGnSGwq\np+DVSKPPpAVGA71OA0xH+6vG9aVtNanZkaD0roHHoa3ZGLSVPiAUagcCqNb6\nGG/H3/t61Nkg9p/EKW1XDCVNFR/tuAJ3QiZQAVQzjlycaTA0u3U3d5VJW6U6\nFvqwS+0qmpAMcXeXIH+bculXaE7IA8ydgyUcnSTAcuDQevO+5YHLAWXGoFWZ\noSBUqyFBmgQkV/jJJVIEaaKRPcsquiPvo9a/ybeNcoYsMj8rPBRrqKEXeEBK\nPthrBN9zc4FF6abtyDMJcXe0LlYNlP5+CdyOq+3DKO415mZOqyDtEfrlFczX\nw+3BzAgoBR8KjdV6YMXWxK/OB40D5A9YrXS6QB3y1o9pXwml3MfE9xkc0NcY\nj9vKT6j7ryuLAaB6zLihTtYZJ55f9WQZPYVg1e4ZVmyOLfBHScjRJd+dJfHh\nFMUdwXpU3ixzlCkigSmCUJRWsYERf+8b9KAT5rciugTFBR7lBPfc2zq0ggna\nwL7jXPbBsGfveEc2U5fifm5WRVZyXSWhKTVB8JLeHcHLIR+Dydx0G0KCDQJ0\n9dEGexjomkbVSVHmWVd6y/CLvMtnHe2yUZPfmeha+CCMehlR5APyN1n7MK5c\nrlm7u7H2Cehxv29icCOkEKiPHqXIxje/3zdpb6Ma2wHwJEcSj+yUlwq0riXQ\nZH5+\r\n=QERZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC2BGSEM5VyNk5qzqJqc3z/i5oDAZc8YTJ64TUepsJ4gAIgMFi9E03gUVJVy5+nWf7wpxX521d6tck8/D5E3r5oD3E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190702113604_1562067687126_0.8300230846746206"},"_hasShrinkwrap":false},"1.6.0-master.20190702114952":{"name":"@atomist/sdm","version":"1.6.0-master.20190702114952","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"017d2acbfbabf9d2b6ccd27a16cebea16cb19c3c","_id":"@atomist/sdm@1.6.0-master.20190702114952","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-wN/3sMGFMrzc4sEwd/NB1Fl5ZedxLZucpQMOAy/RrIPrnYwJdU2KNrJNfUHz2TbUJuTKo4OIqDDdOQ7A1bslBQ==","shasum":"d477e180f84fcd52f633f6f960a37bb3228881f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190702114952.tgz","fileCount":756,"unpackedSize":3315088,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdG0W2CRA9TVsSAnZWagAAHJcP/i9s90NWUntUTJhUhB/M\n/qm7dH5f0JX8UkJV0zbXI2jkkc1D4mAnEU4uou+EEOcrjYqKyIzi2RYDe1CK\nC4xQ7JSIMUu7Fe8N7XfcH8tdfZhinbv1CyTXIdaXP/PNDcI34kUp5XmDurfu\nL7fEirQDCio4uLfdiEc0LDKEEUrVSN1aKBQ33XfYFa46gqKbUPzZHGnXkFlQ\nxtpgG/u65+cy6yvDdi/11z6kU7ROOjdJVAyXgt4egtlmFYA+xfwc9h3t3+x7\nzl2L/Tx2dxmCUC/WfeElhUmkxy9YSOHywe8URm3+FfHoPbj6FhnKk1YIpmeW\nTfodKqA0vqZVe/+CqsVLplODFGdmRt7WBi+KSsp0PnRLr55x3t45TZM/MggF\nrbZtGFeQXceY7RtRoTiN/+tQ6v/VA3XUVDQ9bMfZMTgFlf2vlMxHqIOoxlwu\nr+Hbp7VQSHv4Q/Run7kCnc3HZVYFpYYc81fCHqBin8+IcWtdCQRFRiWJRmbp\nSVEMMbcTNBcmrgqeJlIfYT7cF7BgCjKUSag6oC1QTqy0TlX2bO2CMmjZmfq4\nUduMOMSRTnP7ruVj0mkOc3uzloPdye/PKBnARDoh1YiFRmQ0kFVFwFNksrq1\nr5BGP6nLdc1ljB6fldFMkCD5/zj+jT1/O/UkHkfMWIW8R2271AyXSPBbxDB7\nCx+z\r\n=ZcKE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGgIYhGO5nzHXE+46uFjmim7Q68psvTTSpWAT70vWFyrAiBwEzstBlZQWnQA3PevK2CiAxpmn8W6xtgTqMdBNcPD4w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190702114952_1562068406217_0.909352579780301"},"_hasShrinkwrap":false},"1.6.0-logging.20190703173010":{"name":"@atomist/sdm","version":"1.6.0-logging.20190703173010","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cb12a9c818b39f5d9cd0c38b55b6d5170aaad243","_id":"@atomist/sdm@1.6.0-logging.20190703173010","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-TuRkwJUR49oYm8agedh9HSJhf2WeBPYyU2GiE6lJ4mqACTpuN/2tgNuseq11jAXYyvVBEiOjG+GIYWZiz7maoA==","shasum":"83d6bfec654f3ab874c62b739171b6d1ba6107bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-logging.20190703173010.tgz","fileCount":756,"unpackedSize":3315261,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdHObkCRA9TVsSAnZWagAAYBEP/0sM5DVFu+DzRy6VFN4Q\nD1sK/EqXrpRtKsjiW1wAS8K2T1arKRd/WkrABSkC/aWe68hdH+O5LsuW0pmS\n4HdZP3/BAV5Ydmg44G5X5ZKmmu7VybLt63JnBkInUheaH+jTLmFzWEZRxzzs\nf4EcJkLXFUPIVLKqYnhEm91iYi86Cl1ZcXuGwJ/28ysnZRqofczopuug14iN\nfbAKXr+yvRCMyuUuQv2pVfWRPPDVAvzde1Cv39UBkNxgwWGF/oNZV7642Xhs\njhsBL6/yvZg35YXNYCu9chRR2gEHA8H/w+qJjF6nrJ3y7Bh64VR+CyX3jGCo\np+x5UcEIqDQV+4eRnmx2LN+/ZQ2y3WmI6WNFILNidiZ4WzYW6FPKJDnY9wQf\n6f3wKMkAl7R91CwlziNs15L2giDm/7jA3V0LIS1dlfjbvfbemrOhnpMCRwdr\n+xaLJuajJzp2nQXJh5spYCHgmfYDvAQ9vqM7aot3JEuloqwJF/x2cymVl6F8\nF2IWiZv30X/tWzK+utvTgrR3ha/s2PtCY26jhlyflbuGefhnxYmbX9u6ajI9\nj47hxG8x3mZClwlSjU096UsdogP2+REVv+hIIzD6PxvokGlEn6Wcr/hmpbUP\nu9V4dJrbl8PrsUV2QS1SdYa2FAZv+BoshvZjF/QvYF8duIFl2op+wElz59Qm\n0+bB\r\n=9/io\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCEfeXYCHYh0F14dRb2+9K5Tctm3ljjH3AzHwrrCh5nZQIhAItrET/IJsjAUZCLWUwvrFYQwg4X/ehuqa8YWvNNzWSU"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-logging.20190703173010_1562175203647_0.6400228284383886"},"_hasShrinkwrap":false},"1.6.0-logging.20190703181203":{"name":"@atomist/sdm","version":"1.6.0-logging.20190703181203","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"11a73a16928e5c19fa87f2cdc927b2b27c76c979","_id":"@atomist/sdm@1.6.0-logging.20190703181203","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-+RZ9TOA/0zzKmLa3auZ1qtnwb56dlckET7U+wOUB60MDoHdBBbPPdDCfeU1QYdszZpK9XVvJ9RyXywxinx2Ktw==","shasum":"9ffeb37a0917ca2c44f614e6ddd687d91fe5fe06","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-logging.20190703181203.tgz","fileCount":756,"unpackedSize":3315715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdHPFICRA9TVsSAnZWagAAnz4P/R5fp7DrjqLzaDJmsx8k\nDFCfVvMdmuFQr6WZdHbxpQJp6Y0hrzLMAfwwn/1sKAdkenTwfX/sYRb6rxtR\noA8q2XrvfcNDhdVVlkk5VwSn7evzDafXycFKp27z4ebBpBVAY/wcNM3w3DlC\nzvJsZLqEeAp/iyEmAax8hv0xFc3fqfQUtB0oX+q3jclayGF9gjhnrV2P05PX\naDHnGonmgfmd1MMJ0OrejFMoNqZCFduo4u+kuIQiR+QeIF5yTiI5sg7qfaqy\n/zzoP4r+EnSuCNJVvilcN925omPBgr+TJOm7LWVrmoksFiad4aVetol1izRG\nQNZ5g5woChGdN315jdRXLWSd82GjEerVh0VVKr2QJIb4n5mi7H5wDoy6a2kX\nUZIvgeNgvYtL4aoZqIOtv225kTGBiIAWsbwkQ44BaTp9pI35ezYhkubLdaua\nhG2IGkS+SOAjVJPzdy2wCcg74BXSGS77qKR6CXLPunlsJ93ipu9e6dP5U1O8\n0aJZgNReF6Pf1m5I13/Md1xfnfR1s2ZPy5YbeC/hEXMLdjPlwD5l3Nu54OWk\nugUXOHcURkUObCl+1fx8r2LUDz10QfphA9prHn/rFTom4cpGCtcOA1a6JwLX\nd6eLFoAv0afT8smXN8JqgRzLheQZa4qxCJuLe4ekwIYsUUbjaRebmwpYKXXv\noD1m\r\n=4mRp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPJALXaNOE23+7/Uv48zgImORESMwsaYYzW8ZGXbJMAwIgIFunZ0gQP5sOLSLwEnAmBZXnUdBBKYp3ILqsMhVnQ40="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-logging.20190703181203_1562177863432_0.9573629915834854"},"_hasShrinkwrap":false},"1.6.0-master.20190705154040":{"name":"@atomist/sdm","version":"1.6.0-master.20190705154040","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e6d21309a82d0ef5483f5e2af5f0408fc2f0ff25","_id":"@atomist/sdm@1.6.0-master.20190705154040","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-irqUZB9ZOf8yJHs5h6/54YsliMX4h3ZZOUQfHR2miGky6gJ4xfIhxE4u24uTEwiSWC/Y90kl8ei+9t5pebbh/w==","shasum":"89abae9383d09aac4dbfec5e7b4a2fc79f37dc09","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190705154040.tgz","fileCount":756,"unpackedSize":3315454,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH3BkCRA9TVsSAnZWagAAenQP/RR5/zt+S8MY0aY0QhDP\nKciz3zpG0wrbTIge+O5U3mxZ+eEVioDhsa/lvcDRdHs7ln/tHnz7HbadEvxd\n7jkURYiUH7Vfsm6WY/1/UYB5fELp8SznkKq49VZiX5m+Km5R+LaHwUepNo8B\nxe75ZSWmghlIHBsOZtexfbqc1FFH/fF5f8czv01VizFHdhGy8DxwD3/LNwpe\nNBwl4VtRoD0VUlSDnW3kipiY4SQqdXMxjo+EL++ye7e1mBPt6WeyPvXFlbuE\nttQ7RPlrxGtjOapec3F1PIjS/eyHR/poGvmw1GgrPxwM4VG6iYEwTtBv6Tqn\n6xb1NGLEV0SxXxaM+yjZi4oA32e6tbaDF8hDNtS0+qb7vKvVI8HRBur/0WbT\nADz3KUotz8DHitA9kOnJnV7+8lh3pX0inJjTcbsd7DGMSrx63p5F1gDWxYFQ\nBBWkWfzVCf04AP+5sYJVLd/ggh2AWQNF5wwhX93o151tun+KaG6zfDW73+PF\nduePQUKnDyJZLZdqzMPapYkkYYA/2FTSEdwJCmuoC7CdDieOzqdOXSMwu7t7\nw2ZPmqtlogfZanknBVZWF83YlhY00qDUcceKmVL79pFP3mbI7wcrPSRw2br6\nJruD3WGC9j/0ShdXEXqAjq8tkGbJ1lSSbXovxRUHvNc00F+QZYgwM12WevDy\nZMDm\r\n=16il\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB+Zj5Yzga5zv4HWWWK+r5Xi3F5NMdKGKH0FIcYtX38gAiEAp+kybrv/evYeukzii9txrMgtJN99v5pYmG7npAGbJ1c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190705154040_1562341475646_0.7067972731350007"},"_hasShrinkwrap":false},"1.6.0-master.20190705162535":{"name":"@atomist/sdm","version":"1.6.0-master.20190705162535","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7f0fa060c3a3093c07b20f918135da9bd2da67f8","_id":"@atomist/sdm@1.6.0-master.20190705162535","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-4CWeKvkq/VXBfY4WKs4krwweMYRkbEVGKof/Po7Adfg1o4AnVTd7et6SMTuscEciUvFaX3fg9nOvO66Rzcw8iA==","shasum":"47ddd35ca736c2f04465a278a81dbb71c202298a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190705162535.tgz","fileCount":756,"unpackedSize":3315541,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH3rZCRA9TVsSAnZWagAAYo4P/AnTYblqJbkL8pWy6FgR\nT2UvCcZV1URcsV18wUnFTFuLbWjEvIExqUVo9qlplEPLdGjWXZSv3CCDVlaI\nq4AnGfSNCg5ZA4MXb+XFj4QQMLridLfwK289KpUZre8lFJN0Xb6mZhqM4TdR\nTZLB2FT+fjGPI7n1R1F+t8RjRPrvcNyc5RcD3qcirEFSOhxonn5oTjvcLjj2\nwCjIlcfZIMpZ2JP+mTYkm0wRMumYug0CzI71MykWHmoDcV3tpGSmJkP7YIjL\nAEk4lrsexNc3Nv3wxS1MntnyqwJbs46AE+8mIPYs3kBSgrtdsoDnBijDVeXd\nHq1vitwe8/f0k7F9CNLMfjQmHzwcXMkK+xNjHd4/2la9TzG8D5ODuClEHiNo\nZPHKlZdCe2TH07fWF0YBrJrw1bOe/zlx4QI9fCPB5lrOzIDV9uCBTOaV7t5L\nHSIJ99wqaH3Gmq08CPmwxboqY6mziJEjpuV9hbMvg5qfnD0CXBojWFwCORNR\nqsvXc5e6E98kCADPr7RJhr5CWtQuP+ZNnT9BcNjouDTQqkQqWscsC1d7nXhA\nmuxZxHiUKit1p8mbnhoG0rjQJ0KSA1fFjkke/uiwApLIr5huCMTgzTmat1Qe\nHuceOEnwKyYJtaZGiCXe6X3hXkDud2sW7jw6hLZJSEen77MTMvgF9kSRsI/4\nyimH\r\n=ZJpH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCgBWVXakSPV/iAoBv4mFG2P4rYtWsaCn2oYypcF08WNQIhAL1j6Id0W94RvBIOhfDXPKnfTZxUZ2nye1cvBQtkSzFN"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190705162535_1562344152339_0.5090179355825732"},"_hasShrinkwrap":false},"1.6.0-master.20190705164219":{"name":"@atomist/sdm","version":"1.6.0-master.20190705164219","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"05323da748fc29b3e7d295f8804df5d720387b15","_id":"@atomist/sdm@1.6.0-master.20190705164219","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-XFn4IdnGwqKKIkw0GMe3w8lr5jZmTapttfp0S2E8QMnm+9BbqXcmgkl1tzEa9IMM59vOQQ5NRTIppLv4HX9RPQ==","shasum":"8d82f3fb2d8e592e7899c57b86331596d0c6980e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190705164219.tgz","fileCount":756,"unpackedSize":3315541,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdH36eCRA9TVsSAnZWagAAiN4P/2OP1XaH2es0X1IQ1Dzt\ngc9ZyIs5aZVzkYMf+FeTUG9IByOsdx5JIRgEE9Tfm6j+SM8Mq6x7vfiT6B+6\nkElQbce84HprgZz2rWaOgPTuJ4v9V+J3jyk3w0bwMJgEtqWxJcHB81vvnUTV\ngEzGgvjR50BBs3jz38TAH+GizXdik42C7DpW/aTUbIy5vzrUdup1PXylZRe5\ngwxvIx6JpjzQst4keAi1nURtZkqexs9UhPDscqcTVvDj40w/PXC1/IvvtoJa\nkceyLhxupGIVl4Zb6Fe7NTE8lcGEwFt+GfE4yy7g3w6hP4pTZ139kRTCwCEN\nO+DOvTs0pPqyTTHPxVykf1rM7lnM+8uQuXUClvqGtSV8M56w6OrGfElkflmU\ndOJCS9Uw1PLjYhU3P7DUdnWNmNJW8dmYp5QoSweUC5lPznKcikpygWdk00EA\n0g+bISEwmB1CtZskhWd8HdoXCsoGeDK0iFWAP9tswS+53fXvgAt2/UwCirFt\nELhL0XykTbCd7HyNMQeNK7zKntVwz3vb9Q5uPWxZxoHTEUJ11yLNv64y0PAP\nsNpypDoLxLsxrxJunR7SVZbHswSX7vc5QaS0/Iq+a7jdpvrvS8m0aa6eazxz\nOsb4VOK3gP4CrP3zY1EhMTJAayah35uboEH+IlsuBpfAHgscXtLwsNU5J/1D\nF9lE\r\n=0Z8D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDyqDQr6W7aiELIYeCeufv5UUV7EGxWOh+rkJr7lEeJkAiByv+r5TB114b9FFczxJjtuT3Sh86gjOd021xy96FPVYw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190705164219_1562345117230_0.39897791298558083"},"_hasShrinkwrap":false},"1.6.0-logging.20190706134756":{"name":"@atomist/sdm","version":"1.6.0-logging.20190706134756","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/jssha":"^2.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","jssha":"^2.3.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b44ed988ad4d6e78893b2de23a6afd00eae12481","_id":"@atomist/sdm@1.6.0-logging.20190706134756","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-sdJldXOvGevfAIQFjbJNGxElmoAmsQFYkO0wwISiLkNX+slWSe+J5EUQ8upTU98bPVJ/iu3HmPMZwjsJi81xHg==","shasum":"ce0b1ce958bc00d560c81786a656494f9c0f3ac0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-logging.20190706134756.tgz","fileCount":756,"unpackedSize":3315967,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdIKdnCRA9TVsSAnZWagAAg+sP+gK2RZXgkF1z8fI/V2gz\nHTZcYtJNzRctALsu4DDfo+GQDuLKUOr+zMV6EcrIErzIe0Lz5+3SIFEN20pW\nNk62yHwj06TAcPK1SDi+t9im2D3UKfpzLMCmMfFtMnH1YIjZVKGFIPaiaaYr\nezq2pnm4IBNk4d8BcpDP1nTReu+8MN4IvRkx5uZnfiJFkR9HhdhEKErJo7NS\n4Jqjsfdbdwa24te5KXkxJlfuOLhYjm9BiatPWq5U3ROBz0JhJobYcw19gj0A\nq+d2EqIfpy6umd5e8ZKM+5Ia2MdhMaajuQ60BVdK+Vy5F7dmMwHyOGzWeEjc\niHOl5XZ99pMHgjbWLNZWRst+VUxUOCekNulH7Tp9pPcwA/qKiaqEdrVHA5DN\nLHYsybknmgeEebYhxBCj0pawgTEN0kxXeh5j4qaU43UOd+ruZXYjik9fjcLj\n1tiy5DXrgR91g2aF6o38KnA+laM+bLm+NDKpSJEu3YRDuh/JRBcoQcLipBqV\nuCqnBho+IyQPag2cVtK/A9/O1ogan32Q7JbR+e8x7vtQKz77T/r3KXJcUW7C\nXVir+Mtn8KoTC0VTW1p4EJYvODDtJAe7xF6m+pSqsDMZZ98ZigTEnvhURogU\n7xAXKA1OhGUeiQdzzXwkejoa3bYnDqAutKujulhYVoD5RAjVd5lt7ooSJGDD\ncXC9\r\n=vGU+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvQlxkr2Fx+Z4kAOUOQnTA+KceK+hLybW4X8VL2F4pSQIhAMv/b9EVDkQyEnLpl0bw+PE67OI+s68aYVNlLJSPgE/F"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-logging.20190706134756_1562421094561_0.27651775744937246"},"_hasShrinkwrap":false},"1.6.0-master.20190706145121":{"name":"@atomist/sdm","version":"1.6.0-master.20190706145121","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.0","@atomist/slack-messages":">=1.1.0"},"devDependencies":{"@atomist/automation-client":"1.6.0-master.20190702113653","@atomist/slack-messages":"^1.1.0","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.2.0","tslint":"^5.17.0","typedoc":"^0.14.2","typescript":"^3.5.1"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a9b25765a69fd987c4e5745cb63e99496c7f65b2","_id":"@atomist/sdm@1.6.0-master.20190706145121","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-Gn0zPXUC9Dr+C4jNQVwIumIAF59ZlqXYUF+UBiKqDTNJceWx6OXHPdH8S2+mX0+luK0+tNHEVohVOvcj2U5jGA==","shasum":"c7a5acfc169d1f6b7e2e5bd21392d0c5a7759202","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190706145121.tgz","fileCount":756,"unpackedSize":3315415,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdILY0CRA9TVsSAnZWagAAlp4P/3tfduW9LpYDY0zu6yzV\n8j1WVlGnZolzJbzvvnHUyynsSeK03N/sz64K7J2hUzLv3S3OXGjKev44/+pJ\nQw9xJHoTCnGXIgIsWmNOTisSbjUbPBAk7paR5VMfhhSaK9Jwx/d5J8wFTc7T\nXB6aGyGPrbnHpTmfnsGPrCEK00IajTeb1Q3/ZlbnqLcrTtV6naU9QqGjhWpl\n9ouHVKtN0Xx1IwelWpSiOFGZaTZ4unvjJpmtUvM+qcBnP7N78shOxGvVf6Od\npSHAbk7i8rYCHES5r53kSu53xmGlBjqqBB639SfbGfSM4mPssbbEfy8PtJvB\npJwEOVnDR13Y7ewm0m2t27Yh+lr3GSJ9lHN9M+ZeVfxg759+eHlGFgqO1vEd\nCw9dB3Liz5VkIn8PzXppk+geWyeZUbtk9m6oWDaxF1BA82e6eHi04cdnmsxT\nC0tC24ItuC4CvWTzEQdriTwCePpFT2kJQzxudaJlLWXJsmjY/f1IFpEAz8q1\nQn/l/23oHTqvmQCA99S/KKGyoeCXy5B8OJj6kfT9uCy+jmqeLoEuRISSH8dM\nxesfsNs6C867jUStMbzdw/w7eJSopuj9yoLv0uIOJ9ariUCOhO7vblp0YQye\n33jX+reI917wEsCjjTkZlES0ZnjKBVRxHvZnvgV284/pzecJaDdn+9rlZU3O\nwXnh\r\n=rkYP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGJKJFUJ7xiSeuSUTGhmUHW5GRhFRVZ7OIFvNn4I1q/IAiEAqMlf8QzpnTWOJoysf7EFPTvuFdDo9LJnnh0bAi6mJyg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190706145121_1562424884077_0.19464564644303683"},"_hasShrinkwrap":false},"1.6.0-master.20190709001900":{"name":"@atomist/sdm","version":"1.6.0-master.20190709001900","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.1","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.1","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9308690bb05c1965d0a56ba2aa72435dbf570466","_id":"@atomist/sdm@1.6.0-master.20190709001900","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-PbnRky3BtVKxR+tJSn2jnbHEMIgviC2Fgl+BZVkz9ai5G7InpWmuLYuR8PNLkSw/Cd1bvvrihWvG7/xXjtX4Zg==","shasum":"bb4b27f7bbbac74732dc79df675bea90fe6ecee0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0-master.20190709001900.tgz","fileCount":756,"unpackedSize":3316647,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdI96PCRA9TVsSAnZWagAA0VEP/2gz6WGVb2UXO41dSJ4k\nWqDZaAweDoA2LZGabenofpxG2XsMMyeuuQxzS+qHjP9cQDWYTL3YwqRvTUf0\nTJ+FInOasKurYZyiPwnAmxbbZ9ZCbsbG3T2vmtCt91R68VsC+BiOSj75XXkT\n+HWhn0BoxvEI117Bd1b5+1PCeGjRtwINWIyrVzPLBgoDpGfVj0YquXJmssXb\n3eaVvFKb4nD+35e5tYu+R9mJmMT5aTNcXoEm6igTRG+JcFqG6AFqK82lIkJx\nxtC4rOrUT4g6Aqs73GiQNaLw1v/V+kVrjffZ1q2vK7Q9twsZAdbqMXh7OwZ5\nkeRfzj0vW9YQ4Wf+Misc0GKOZcSynoEaxT6TYlTG0uaXm10sbJx53ltj7LM7\nIgAEPdRBfLpPTlfX5vurEwFSwETpUN9Kd6rstIbU08j/N62iTPhdyM/x4YXi\nxoh8UQa6+bwwbpzx7NVw8bzY9EBszHHVKgNPt41+tcWlWPUN+DfT5utDebGd\no7UlseWr0L01F4OZKOCp8xN6sWqL7qKExmx+zCqptdxTCd8Hl1chSNuJ3QRf\nKruDsPPhnlu9BWnYnwBOh4ZZ44wO7Ky+B2d7Qo0zVrblln/CMVxXo8LUFDYp\npsow+APFuhhrGjod7ELu6SCbeCemZmwuXZQwbpdcSaMmrVMkomwIeG8OORNA\n43NA\r\n=d2W/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGCpRb3E8ofnhTWBzTrPkSknNnwCIqTC+nxpZU2HV7veAiAozzrHmiTu7ba2ntZYAANyea+VA+tXIpNRPPK+Sk9whQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0-master.20190709001900_1562631823031_0.34273086782824924"},"_hasShrinkwrap":false},"1.6.0":{"name":"@atomist/sdm","version":"1.6.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.1","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.1","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"0945d5a0165f4c8dabb7479390d97422b6285ed3","_id":"@atomist/sdm@1.6.0","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-EB4D5WAMkx0ZQ3eS+/gpTUrM1O6VdAT+OYxxLKGG6QcxCgu+SVssxVNpy8wzVT6y02DJU/RieH3PKGph/Lv2oA==","shasum":"1e5f3bf302d6fb7a11a099272de0b406b004a8b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.0.tgz","fileCount":756,"unpackedSize":3316625,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdI+toCRA9TVsSAnZWagAAUOwQAJqwBqPzLnV/6YwYR5fm\nhUDLFbWaaXqJ0zl81q5VKQFvlRZ3dxvqDFKxTg6E2L6VzDPmUTAd+MyNdqll\nYTfvQgXOn3FakB8H5yVEFjfH7ekq/jieVi6/8SMPXGL50xpkjB7F0V7Hq7fS\nCTDMTKE3OIBQE7Ca59w1p60M4yRE7nTGeo6Qnm+XNurprzbSAL/FRQsQH5Tg\nstC5QWOqG0FKOMTZOPK9EFMpaLeEF6k+UY7TTb9Is/+hQWrJkKvJ255tNrj4\nQ3fq0iwxmWvaGr3eZPoahetunCsdK4egMUxb/IzAXeO5f5UineOJm5rdGWAi\n9TqS3mRKvbu5VzGyFGcrRaj6OnyIWWm0m+8lkQhu2kB0ms2qolMUx+/jONGy\npSAmBIDHEYeMLYbBLEO7QbnJvB1ln4/J6kGlY7/1yPdI/EJgOUrKs3ygoRQy\nw4WcDRHK4rYFtdbbq+PtBloXM9eg4xcgWuiCgKaoLeLywx80/+HGrcFOQJVo\nyRq0+Z8x6VTV07ohNe1HlbTwLW1Zv4J9uaM64pd5hdJqP+ymU+NG/czQFOGY\n5gNRLvt5Bc5tNapfYXiytO9zmQKN8H0vIXZEG+s5IIIm+IAPqR0kz/y4g0q0\nBq94HGOtKH46ERbZl8tAaMmbLJtb2vo975NTRmhTvAidtH03nNfFn1lEMCeF\n0bAC\r\n=2zO8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDq7yrPBdVhSdGXc5iEb3SQDVQB6RX/I3DC7VWoskECKgIhANqbrd1uAyObOgH60yiDtQjUkROvnJjnvE8IoYcvdzmX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.0_1562635111496_0.1708342261071807"},"_hasShrinkwrap":false},"1.6.1-master.20190709092809":{"name":"@atomist/sdm","version":"1.6.1-master.20190709092809","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.1","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.1","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2cf422f5263b4359bee75530a8c2b0ec7e26112b","_id":"@atomist/sdm@1.6.1-master.20190709092809","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-B4lNjUhzet/j22ub9wAXzVgjKRpiPvRLHgAl/C4LuDBw/Fz3rOKO5Ub7rReNeoL7/pFN8oFn9O9uxnIn66rSFQ==","shasum":"1241f7097b6f75ea78214b7f776d61e5e0ec57c4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.1-master.20190709092809.tgz","fileCount":756,"unpackedSize":3317557,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJF7VCRA9TVsSAnZWagAAnGUP/R2dilv9t0DYIM4qwoF/\nl7LVcRb+NF5cNeNo9vbXSbaQeKU/IE3avyetyWk7keFLC/+4Z17lt6OqQDuj\n4WWsvIGWwP2L8/O36RJ7UiAWXmw1XyUp2QUIkbv57rOCLEcDJu6Wv7wYV3Xv\nJXaD5RFQ2Hu2O+7O/Jybyu3s1segEKOtPZHc1OF2CCz6BE8z3buZ+eSUB0+d\nhRnnx/ELO3EhmcAuQ1KBKBkjw6qG2yk4cmiDBP8nT2omVlxrF+YAfeAujxlI\nJ3T9K92BjQXkt9uzmIrcNJ+BArVkUhofaC+MZK1eEydq77928pX5SXAanVmn\nix2fvYdpjj5bwF06NIop26Jj7SNikEZvmTiOwoL57AOMwyksX72a7IoWzhIr\nbobhNz+nMYVL19TP9ZNwMZgMpISmWIZBjMKE6fMwSzXCnH1ICkCAviX+T2X1\n6VaSVvlUw4Pv7x8+Fql7q5Z3g8MOOqHn/W3cM1rGhxvZ/EH9K5nIdhhOOU6h\nag0wnbWESwK+W94jWJePVPNVq7BouDCw0nh4yn5fPwilV8imaW6Vgztlwu/v\nk6vxoZL2Btuh8N8gGNDxY7upJ2dZvBF3eOI6c0m+tanOKJ9H6hVJrp3tjIJH\nM9FJNW7F93Ysz8ObAN62QGSp5ka2ydjIgr973C8x67SVG5SMdZvpYdbhnYZI\n4Q4a\r\n=PTDo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDsqYuJBHSkNig8L0WjbMHTMGl4MWHxhidIHihVqrpi/wIhANGmb2ar2MFkGhnmRmgG4u/XYaL4EtVsM3omTH6qs7pg"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.1-master.20190709092809_1562664660300_0.2219022730725464"},"_hasShrinkwrap":false},"1.6.1-master.20190709141445":{"name":"@atomist/sdm","version":"1.6.1-master.20190709141445","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.11","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.1","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.1","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3dc43e5b5b6e74f69b28918e6233774f1fac4efd","_id":"@atomist/sdm@1.6.1-master.20190709141445","_nodeVersion":"11.13.0","_npmVersion":"6.7.0","dist":{"integrity":"sha512-RZJu57bA+iOG8eIhEBjIV4Y7uLWQURx+bur+ow8+0q7N6B8eHm/svwozK3Z3mYFbJvtGlCrXugjIBuOJKcCe5Q==","shasum":"2e01ffc8b2c6a3561333d046aecc4f9b55484e69","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.1-master.20190709141445.tgz","fileCount":756,"unpackedSize":3317507,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJKI8CRA9TVsSAnZWagAAyK4P/iAat5ifZbvxkEgtqh/t\nLJT6NS1oUq/8EtVn4bsqlffOp9uRfG5vsWRi/9WlJfgzH9gnd+zMcyNAcRcO\naWD0fAgnbzvOfReNA88AZ0JE7zK/83aPWs2ucxnR+4tCNEIMhFTHM920V9qM\nBT9tQJ8sZHqX93P/wAEJr+KgwDLN9y725W2hUFnnv0AvfEcRSFon9fvxPXep\n7ko2I1SxlHP6yVWRAEPnuwgQIL58qDrNwd3KPbzZNnRKzp3BJRmMP7MtiIx2\nsotyKPO5Ssxt2a7kz4bhnz2SFdko3Z9Bd4lCoahl+bg2J3Iv+sadh5al4H+W\nYSy/ZZCNr9Ebfp2krrBtS1GYXI8zvJrgn3RGpGcx8ZWrHGVjVCjVR0bOtcVp\nk+2wKfEJWfhlPWTOE4hOgWfkZJ9/sdnLaFckJY4w1o425m/jDhHaw5kTcHAl\n97WrfkWziRfnNTOONp+1n8t3kBCR02ginlXHD6aKaNntg75XeGuI1GHd5+/C\nd71Zq2CAby2U+uNmBml+JSdbtEoWCw58B/EuGcTd93zp4gy/1ry30FU3/2m9\n0v/Q4gerKaXxOXRj5wrpjT6+opGHp/IwQGRy4OJiO+GxggTB6zg10ymDc61o\njsRi+RmND09W0nXkd/qSaI5XENs3uB0toBEYIlmeP8hym3RNB8Sp6exun5uA\nanPT\r\n=j8WG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCm5KA8TElXZWKlIaa7HfMWG29bExArwls3jwywRPq1NAIga+CA+hKG9beYgnL785UX3+gJb4C//Fc3udLmmSmHk/s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.1-master.20190709141445_1562681913249_0.7862419242970153"},"_hasShrinkwrap":false},"1.6.1-master.20190711072944":{"name":"@atomist/sdm","version":"1.6.1-master.20190711072944","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7c81fef3cdb440b66d21336770400a9ec72ceb45","_id":"@atomist/sdm@1.6.1-master.20190711072944","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-LAZ0hoNSoaR3/OxWg3+M0SNoeNfzPrB7Lw+Iki/IJOkzWb6huBlVf50ixhlkJhmPQPEz8cvepKMlHucoUKqoQA==","shasum":"b9deb96967fdd683a57449323b52084feaca2f80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.1-master.20190711072944.tgz","fileCount":756,"unpackedSize":3317760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJuaZCRA9TVsSAnZWagAArwkQAIGdnQwH1QekJswPTWQH\nVHGWyEc3OQIBR5fXI91bPJzjcrFRI8lVwfdspEFNaGIsxibdfftEWkC1QMMD\nyfZoQFVtdEQ5kP6PoYj36L3bcIT0Bhq9zaqT8z0ot6QJ9Qq9/U7iAy36qAOg\nSBwobtPljpTWqnS7gghDmBwcd7AS2N5MEhNZJjeLYv2LvlkNXxgb5xgQ71ZH\nVn9rnxS0p7thxsCswDGH69CKQjS/iLNZtJxZdNTbPPVtnMdjBDEQuV2TCfQr\nTWm/pYKEKhjffDA9063OeQh/CAva7NSAAz/EOFmD82AGwBq1khQ0oYpFvIXS\nfX97Z4+BuhQUa8MicIwH9/ODU4+l4AZ0U62Cw+YHDIYB0aIKfgsMT45aH999\nP2FtG7WPZ8F/4EGJG9+IBcYj3nZSvNPx6timMVHEYOWOs9+naX9hJlncBFCx\nSJYULvgCKeZkkkxi0CzG/3bYkVOOa/k70y/5lgEIIWICBHa0qKOpcxKQWFct\nlNzc28VVSHg8Te2NBoxscGoZy9L7g898xnGzxV32kZgXFp1/2YQeGQC4/347\nRiPWk5ivlu2yIw+evYuthDMoUEJOojZtycBTd0hDAr6UFO9B1u4r2Szx6KHZ\n7fjVHrZfDD/oGmJVU4ISxGDAI/78QdPVv67Sd1O9sy6345DG0JxUtGSYKIjb\nzR1R\r\n=rGRd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIELPFnhSd7SU0yWHWJQsObGluVi5TPKspzD7kUVpclugAiEA/gxzymZ6qvfQimPk9bw9ozXCNnS2wMNmdTMlB18iw1k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.1-master.20190711072944_1562830488736_0.4996148344923441"},"_hasShrinkwrap":false},"1.6.1-master.20190711073255":{"name":"@atomist/sdm","version":"1.6.1-master.20190711073255","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7c81fef3cdb440b66d21336770400a9ec72ceb45","_id":"@atomist/sdm@1.6.1-master.20190711073255","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-2mGi5iamVJeL7RYwPrf759PiaWOnuQ9VZEKltSiginj3e84OZQXp3HeNM79dDGU+eI+rpntiKZJG8/o3Oudo/w==","shasum":"8cbdd9a8e459ecd37c01f018a9e9be365c20c272","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.1-master.20190711073255.tgz","fileCount":756,"unpackedSize":3317760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJub/CRA9TVsSAnZWagAAN9gP/iXUUEwowIos21Gv33UA\nRJQhTHSVJz0HcsX4tncsXnUN5LbFjpvdPkAPwgtNqpYC+XgV/fL4L8F3IQ1L\nYwjLRsEaa4iPVBHXR+uQ5eAV72bQN3MGPMCo6ZjG71jwjL2mtjgtfAg/vbrZ\n9aD0hjMrUcj8/vrjO9KoKdeRWA6SFVO2R3mpS4DqlLs+2npSJ78rc/c+vNDB\nDRVS4EPbIZ7Qeb1gPoX8EMBrTiB8NKWGfwKaAiFBCaXB0OLQNRH55RFLSEOF\nhNOVfYNrlR1s8AiQk9DjqDBBkOwARaAQOUc3EmR8lUgx4Ajk7j7Y0dwaXSjh\nOohwao3SQL9gY5HwjwSf+UpiwSbkJ/Xh7JMJUDASm1th+6ku3LQ04GIEhJG7\npj5+gN6p2Za8iZITKa1mVSqAY4pXYuwQpZmjkFau3BckqRNk+FVlcn/IX2DO\n/yoh0HfwYZM+hsoPrBUOYfHkO5yL7nqyzGRn5HRQhzuU2W0b+9ro/9I884P+\npjGriwzE2gt426hSn30LXZl8or0IgdU0F+FmpjBDRC+nmCy89F/a3wHt5G+n\nDdzEYUmjEdnAsf90rWa+2+jkm6f+ZR5j8KBBhGmJySWy8aN1qU8YAFWAIMK0\nBUvIv935LnAFGlxd4zrzkqDbLajquXO7G2HFSkEMAhd6v7CKwIOmFtxwIPdZ\n3bLL\r\n=Seic\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDmNodkhhc3YMpSFKs1MtfFG/m3ab/4U0bsAc3Mw4A9XwIhALBz4a/bBfDIX0VPFfte80QWGcxBRxWwOqwLA/Rx32b7"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.1-master.20190711073255_1562830591146_0.4585058532597366"},"_hasShrinkwrap":false},"1.6.1":{"name":"@atomist/sdm","version":"1.6.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"477a812afff9d9b57b11c4079c22c95d63c45b5f","_id":"@atomist/sdm@1.6.1","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-25uxNtbsT+EQvunGtlY4nZUq6CeDWUEGwfTipJiah6syXe0VQxXdXk7WfuPSNvS6+aAYppX/t95mFRHed1ns6g==","shasum":"e39f51a696e7b39095ddac05bb521e5c44838d0e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.1.tgz","fileCount":756,"unpackedSize":3317738,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdJuhtCRA9TVsSAnZWagAAx6oP/0DqJCB9S4+PwfqlYZAH\nkXIUZv1e9hyV55u210Izgs3gqLd1X1ZiIHj/wSDrAqmseq4CiO6n5X4z3N4z\ncWB9MHuzOt9y3ir/aOlzkiF6ZkJx9XsVppe1V7SPhOBPTmLvbsW2NuZ6/YxD\nusAN7yQ0GCR60VJd+BzxVe3cN6tJWG67FRHfggrWvqcngDvVdlTvxyZeGzFo\n74p7EGUBvvBOJpyhH2KGUJNt+rSQGSqRXZr5tn+KSIDxJUF47qsGNUxP/WSD\n4hkK4DS/z4BEC7nP3+QwKVX0Umjnjr5M8T7XE5cULWC2r71Hd+5J+Rf/KtRL\nPndh/do+E112Wh3iSGQLT6bU3xesguzmN/7me1LvZ2SH+h+JFI0Qj0oEAmf2\nI+oYs1eIKE4KJwiEMmaLSGocmpx6qapR1qkGTgEcb2NCpTQskZavyQbQcAx3\nQb5wWvQJrve8MwcchW6oSkQu9JVrV2bceuBX7DK7ufvJ4EWkr4oHKj47khpT\nB8CZbGgjgeP42pTaJL3JkLqChXfblqfOVS52hcyAUtUeQTxiwefrTEddn43m\nWINKidae1qot1vD+5FsTbIqFnLjULnNHP6Ur9RCC25/M2Pe42JTVPu3WWvFm\nYcKyhfdxKBlaJqxz3f63rIHh6DxboxY1Bhsh54UeUl7lrGDFxvVIvPHmHlAy\nYvDN\r\n=Prl9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfw4qWnvgPXO3Ak7GJIrrqKkhjr9WQIB8ggQWOscPTmwIgQ42/K9L0AKE2QvjG65i1mBXytyQOjfF2LDw1HHxAzQM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.1_1562830956447_0.6677698976154762"},"_hasShrinkwrap":false},"1.6.2-master.20190731211703":{"name":"@atomist/sdm","version":"1.6.2-master.20190731211703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"10d8549b12cf814a6570509f3877f975dd529099","_id":"@atomist/sdm@1.6.2-master.20190731211703","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-48MK9rDRdBhRk35d3yE/ysmoa9FWS9f9hGExnSFet1iuL7gkB3nvsO5jzz85wXq7gqihYNneXshtUIWN2ngIWw==","shasum":"87a36fc8f4a391d0b4316d002ba0bafa1e5778d6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190731211703.tgz","fileCount":756,"unpackedSize":3318348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQgZjCRA9TVsSAnZWagAANWkP/3iOu7Wd+xW3e5KvuvEu\nOxJx6w7SbUWE9jFFyfjnUqlsCiPDSpxexsZQ7QAkcuQNUM3IGWbTunSOpQ8y\nPY1JEUlHrpCrJkNKUiYmkTcs0xzLMTL9HxWfZ9l4XV7UGix1trYpJO09pG1+\n/UnZTbRDNxoxT1/iXMQeiubhEB5BXI+UfWZexu0VEU2dzNWOfVVDrea6dPaL\nVdOwh6mmdAAyvDXI8p/57XulFgTHM1PXHg+b3hczauGhkY3utPZrKK6gVJY1\nQJbCtA1ZuikFWvAEU3xHTvh/PRF8jcjOofqOuc0hS8JjUkN2F+rHnKjSOgEH\nl2woMS65CpfPwviSisDtFSGB473DYVdze/5klo+4hESLO+jTuoCKsvNp8xir\nhUTFaoZGG0OWwDi8pVMb7IIKWDLTLMFn5E0PWRob4FMRM2IMYP+q8/ilfeYM\nxZiNtvQ+TOefmu70aS36uxR/CRbMgd6FpP3v/YOUoHV05tRrHNY4ebRWD0xv\nmXfcLpMDoRaqLp8SC2k88kBk+JKE5Ae+qePZs/bWfPkypWmLFX4tgJsWDPaX\nPN82oUDl7Vc/CicSauGjmbJ6q6jsIsRxtWxtfmWZmpILNtCcKHHDGSfiMfwo\n91stB8pErfXLgTJIrg+a4HNRf1m0/Vh7HcMju3uDLsQatpDcEh4smAWI3isU\n+DWu\r\n=kMUb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC16KIffVzCwG/BGPNmbiSERVKJ5lCAfUCPzXtyyK6hSQIgGaRh020AcKjRjjYHY5+o1oXOlnF+Ug4Xu+D6FRnMuwo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190731211703_1564608098176_0.17020904545074744"},"_hasShrinkwrap":false},"1.6.2-master.20190731211828":{"name":"@atomist/sdm","version":"1.6.2-master.20190731211828","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"10d8549b12cf814a6570509f3877f975dd529099","_id":"@atomist/sdm@1.6.2-master.20190731211828","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-HrwXNandysQe+DFK8AJwTGmKKBUhoknR1c2ASI9ycAWVNXHYB8s5kavIgOP15JwLqBPrWEywJS8nCSNpnM7DWQ==","shasum":"cc673746fff61a0d85f459dad5df31460a151fe7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190731211828.tgz","fileCount":756,"unpackedSize":3318348,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdQgaxCRA9TVsSAnZWagAA578P/RyDpr4jruA+xMP3FJxD\n8gEH8mFYie0SoKTurUeSKPTuep+5rcmUsQu9EJfbj1FM1PRxqDiJ7Hr5O4Vb\nKgKG/jDzaw+7Mn4KcO+exLirG/F8FS7/d5STLI6MZu79o7pQEIWxTQzSIOfZ\nk05CIOU2G8HdXTe3Y7z+Wod9wD9FflSv9tDWavg4PdodH5golhcizM1TEbya\nJy5iq2AzSjKR3L2amBVn7b5wKjAVOXAYZWmXKtGhpFtHG1Za7WtWpjZVN6eX\npfR0P8lyVG8+E78BDM/dxS1Bkb7F2W6O89m5PQIToMH7r3kmygLnI8wWS8xL\nUPCbWPw/I4xE0uCaZ2ZANACK6a7m6z3NDnoowvjXuzQiOH/cq8NPAyTtw5hx\noC8Mve7S83BcabwY57kPJYZ3fci/OfT+kS/hUGNQzs8InuVdkz2v3yEwBhq4\nKmqthjyAPrwiTZXfI1+HhMSP+DZgGFM0zAznO/jB5d184pNRveOg9MjZiKvP\npWsrLvd64cZFs+DCHzh8WZXTf/7hlWmBT54G/V4kqzkj5U4SGiJARXFgny7r\nY9ritYo6U+PhlYXyxf1/0WQqJNlUigpi91uzONTaDTGxIsFt6fI+Y4uu68PF\nZkEPSSvj3ImaXhSuA7YHKyADiUIdxRHTt67mjwrIlKCL67Z+nEuCvRggNi9s\nxdL9\r\n=7JsS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBr3jiAHrATAtnDvRD8xrxrjTjzhxYEYU0xql9RYY4fvAiB+mOm5Wd2eAEHKD2t/nlnipw8afI2WzIVPXednFGbSxw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190731211828_1564608177180_0.5128130559255004"},"_hasShrinkwrap":false},"1.6.2-master.20190803123555":{"name":"@atomist/sdm","version":"1.6.2-master.20190803123555","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"41b14ce180803d053bc50d138dc72f6bda97705a","_id":"@atomist/sdm@1.6.2-master.20190803123555","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-aHO6arxcovkSwuQgvqCMcUNALbd8PgTgZ7nQBHHaQ5QgHoBRnV4pHzILYDgqeWL3qS2gtmgb3dbsc4cfX4iBdQ==","shasum":"6bdcab7736e94fec6af5e46ca19ab83cdb49aef5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190803123555.tgz","fileCount":756,"unpackedSize":3318461,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdRYC4CRA9TVsSAnZWagAAxGsP/iHcmfiKkBeXGJD3UmyV\nyDPXdfYXKZdEhEpaVXiym46WtUYmpj00zyCc+IJUbYGxfHn4lhNxFo3T2YOS\n4Oq6rhAzriV8cGY5pka5UyxKVORldWTydsZiCRbim7+G1NbuRu4KdWGDmqj4\nwOkqpVrZrMGngUMhMDI0BDnbhO9FxN5O7RAQzN6Fh6nYPv5hO/vn6+v6OaVm\nn4n49bYAkRy02O9pqMaziYYY8TgMai5kkaTmKS5XLogml7eA6zmYEXlB5gDb\nxBF+eDdm1RxxAwasNR6Fjr8ObyOt7oIjBzqI0rryTydddT/Qpi98hmbSj2Fy\nm82iM0QHZ9AUzy274z8prOPL0gjo5Fps+5GX9xvnN+oyVLJfzchoXlxX4Ons\nMbkfALwVLMCMgASvxdsNX8mJCO639Dg5s9TS2JqYAjpCJrisgUTSetMvV5nn\ny4rzJPbiElgyEP6diTqAiFiuVTXWPBLe0XkW3PuuGIQ0PV299O9WfTKZovU+\nRcbAfMjlyIFm3Kbcq+XkH2q4Q4G451cYysH3Nlc/02Q6g3xsfvXoV4GCiVHv\npzoMNrikGa58LX2A91EtiaL2nlRcIcpj1OOkAVKIucr5jyj7j6kAlq1qOOG2\nV2VnULpAKa4ZygWVgf3hQpkEe5hOutmjY2JARV4G6qbBhstQ7n4zDV7JVA9U\nE2ie\r\n=yuXx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDLtm3Qb4h50xUpmuTPqZFBbaI7mYneKA8zCzMJY3v4VQIgSIyJuiQFy/dtw9X96TOEUaG28KN61SbPG/gmWHR0u8U="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190803123555_1564836023842_0.6229508037594602"},"_hasShrinkwrap":false},"1.6.2-no-parameters-create-job.20190813203351":{"name":"@atomist/sdm","version":"1.6.2-no-parameters-create-job.20190813203351","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8257aca2ede15c494823c6e87e39fedd07e017ad","_id":"@atomist/sdm@1.6.2-no-parameters-create-job.20190813203351","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-jSUwQLUG3abZnc4bNUbZacwkcSQRu9DZ7x1epo+66llxcNyJMGjCscTbZREffc4G2tiT0yu6b2YqUCD9FORSkA==","shasum":"1d3b93aaaad2a91ec00c1bc0d275ee6b94677316","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-no-parameters-create-job.20190813203351.tgz","fileCount":756,"unpackedSize":3318795,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUyABCRA9TVsSAnZWagAAWfkP/3+1byJ3Lky0zxNFwE+t\nn5nn4yj04EQYIZcZvbiQIeMFzHSDPJngWdKvl7cnz+2h2KgGZW+pC8d8elRU\nv7wF+JTNjtb7dSP42nEcbECYafjGdd8gu/7eG45PF9NpTzXFCQbP+/xz8xZZ\nK9vJLJUUJxMI9q44f5PsFr71UByXMxT6mg+LuhSZbSzoAjWTwOUgZjr0N/tt\ndDDclDqfFHjUrtkUpCbR1wPsNFYJrZOcipGx68xbEKpK5lGeHLScOqVxYz3S\nx8Dp9YGHCOqdf6tuR/Sf4JUEehSD1bePx/Pm1QWTRi8MO73xMtNyVGtpGBxI\n4OtBDMedUyAVWvupbbpk6Dq742QqHKDg7lY0CRWL0yVimWmyZWH53pxFqAPe\n3WOypr8Jy6icpS5GxN6tgmYoB3hnpjZ/zjOmyPFAeEBOVoY1ROqXXFctzVEe\ncbjWPpkVGVk0bCEaxzQpipcQNc9Z9dyVejRpknqufuzzQVLZ4uggSthWVsPM\nJ3vZkuqRqfUxebfBMRiE29OLID/o3qkryc6u+Ay4CPZ1gkbyXwyUeJfmckcV\nBh3vOM813m+HqWWC+JzjO1oobtbdryMGUSwUW5DSX9PXcRI9yUr7ZKGAIx5M\nR3PVfdasY4UOTxYb4fXNLDYYtIYBWkWRG1x17WtAes9HtmoHji8tda9HzcdW\nyxbK\r\n=KuHg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBDtN5SdzOx6fSSGuBj9l0w4MtpsrwO1Jb+ndLcSkJVHAiEAqV7698HOGjVMnqsgfLJ0R5FRRxISzzudUdX9h28kCJU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-no-parameters-create-job.20190813203351_1565728769198_0.250477883800726"},"_hasShrinkwrap":false},"1.6.2-no-parameters-create-job.20190813204003":{"name":"@atomist/sdm","version":"1.6.2-no-parameters-create-job.20190813204003","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"24f910cb9ca05cd4a4f20bbf04cea12d68195a31","_id":"@atomist/sdm@1.6.2-no-parameters-create-job.20190813204003","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-3IwO6WW7aoz81vQQF7WfEOxqw0SYqjGlkkTRea6AcJwmNJ6m20bnnSHHJ1eGGlYCu5eiS2zh5UIe6g5611GYAQ==","shasum":"c932bc8f6c5b70609bbcb61be7063bc8317cfac1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-no-parameters-create-job.20190813204003.tgz","fileCount":756,"unpackedSize":3318715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUyGrCRA9TVsSAnZWagAAXtoP+gOOr6Ur/W+xgbVefkM/\nbnmai6v3TPiB2Yies1N8tlXSno0xcxX+EONqs5rklO7ILRmS68XxWMU/+uKk\n8SHHDTwZuPM1Zsa6ASvhjH/bI2xD5pgV3/g3vDXGpriKu95Fh45R7sG6Pcqi\ny8g2da3MeJPMB8CIWhV0CO9R2syH5ELSfy/jbz31+AVcA8C8jddmFcIwoBL2\nfgdnKB/Uvy0EmgaoQ9SANk1o8VkhmnmX6vVivrKVamKXN9mTrBl/Qg1kuYVz\nAK5u/QZ0jFae9ivfaOrQ9ix+1770tgJA3A6pd+XfXzCtYe59/6kGjDtjek6a\nWrxw2p45nU2pT9hB89kKOw3Lo0Jl+riwWbm7woYdEskBNaGpOkLXaTfbvP+g\nKTxAKxbATXxpfJmU4sDs4ry1oWBWQYlhWMfYjvn3xjJlcArfKPG69b3IENst\nUXHq3VetzCq2EZYCdc34bMnxOBJ7XPOLR2klbNcurCkNNd8QTFHcvvjm3wxo\neZpIaK7EKrOpAooeRfqpQhS6/EyB+yEJwsIhJcpZ4hWd09xqK0OxWabRknPi\n2Dhdc1RJxNoOLEvNJp+Wdtzk1WmmfcQnM5NpOnzABYjq2tQ3tHhCxWKBa/ok\nPLyyu31l48wv+XCV8ugGZkJpMRi1SPhbiAxHxQpl0gITr7rdtgzGyH0p+qvm\nn+Df\r\n=qFYD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfKKb1ZZ7XdNeghemWqIjjrB3lkYOZCMmAtw7JcXP7pwIhAKMGWzCGWWM+ILggRtPS1mqNQn33a7waLKLmyf2MoWYT"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-no-parameters-create-job.20190813204003_1565729194563_0.6364676313620568"},"_hasShrinkwrap":false},"1.6.2-no-parameters-create-job.20190813205053":{"name":"@atomist/sdm","version":"1.6.2-no-parameters-create-job.20190813205053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2fe38306feb63a682e9916cb98b2a5a5b0e90701","_id":"@atomist/sdm@1.6.2-no-parameters-create-job.20190813205053","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-wXOjNPlWl1Ls7dmftsL+zZWJ8kzBf4ekg1ugd/TTnntdOdIoVohE6Fv6iaKF7WH9Qdg+91noq8TJPKY3ahcHSQ==","shasum":"056e0681096fbe43053da10d26abe90abc392544","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-no-parameters-create-job.20190813205053.tgz","fileCount":756,"unpackedSize":3318715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUyPTCRA9TVsSAnZWagAA9MsP/0yQ4JM1/gt3xcwYLZ4w\nAzjuE6JMF0PMvNEXMS8lECrZFJHUHkXjzqA6spnIPvi8GpGm3lbm4znRrFjs\nDmQhL+d+KzkFubWDCAsR82CaEZVL9cF4++lTf2/levr3X5yUxvPKxRhxrig9\nnX0npgie0K5QlyfKmOO3+v+ZryXWhYovaS8hGhIZeQ3YrxJ8mIMNnWZj/cZt\noT4mWFVq7aODvmvNvPwp/HA/O/c9zBKxPofdAqZwRiIqbPA4VrwC+StPNUCa\n/sb0wAuPaMKJqp9RAXI4WVfbsh3GI1ffEUl5195G85bUqDOwIBC53dWIMV+F\nY3OpCqdQv+KCw1Zi+vebL1lblNOR354ONEnnxpbK1gaeIBNEbguYuwHZs18R\n2elGLs1tiIqqRhYq3JohqvvntaAN2wyUqIMT7ieD7s2308CNBaR79mw77yvA\nRYB9PRWq8TTV/brC6vTxC2tnZeUojyVIO2h943uQ2wMAbFBTZq/3uHuXKrZj\nIs97X7b5rEDlKDy0+IqFwQKWWBzjtR5xye/cr2hIkzkpt/7npS8HcjO6503N\n3tp/HNIVnF0R9KWTdxUVcdYwSgKfeUFt3uJ6JHDaECpVkizTWG3E9R+lJrmh\nmrnQNnBnDtBH0eQicTz6JGTI6spjTz6SQI01lo18mjp9e62HkU0WaEidziCI\n64gV\r\n=Oxnd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICkrUNsXVRbaXz9EBsu6kYnfm0/iN6fAoQesTgU2FxAIAiBH1+CPa8hiw3Px+Gal93pC7zEkh+0foC2hd8FSHBGbJA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-no-parameters-create-job.20190813205053_1565729746469_0.09062372762824178"},"_hasShrinkwrap":false},"1.6.2-master.20190813210448":{"name":"@atomist/sdm","version":"1.6.2-master.20190813210448","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4be980693fee5ca2469dc9c96a83fdbd180e9190","_id":"@atomist/sdm@1.6.2-master.20190813210448","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-B7+5I7mm41rwPuiv6UwNy8n/2QPzy6cGQgT0nfyd3KPdIDbu6YGuFcQYkK8tByX+ieoN9b3PhhWg2b6aVEJolw==","shasum":"abdb3cd263e051401ed50e1de6c07b3ffb1b2334","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190813210448.tgz","fileCount":756,"unpackedSize":3318791,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdUycjCRA9TVsSAnZWagAAEJUQAKALMfuoTXmvgQPv9vsH\n0KxLLZ4OyLcKTYXAyboefeagSnmYayevnQA8f1CSQemQ6R8qkHeeWTmYjZ4H\nUswX+QD3XobuxhyG4Y6M4XJv2Dm3LyCi4L2/IKmTFTWodQZcNz8/HuQmR+NO\nXWJG6MNdC2Xf8xUOW9LMpMk3SexG0rDOGBMkDjbvamXKldwzVf7ucqdwX1CZ\nzMK4C/ag5e84fD+wxsiQp0SGLYTUTJ22/B+/DJlxYOm4bI0H1ICQc8LcXHO7\nuVvQZDAXY/eFZUPWVHZc87e9Mha6ujpiY1kLHt+YaLvzetLKZCemxYYJ5Z/G\nxXGdaPM5QfzeTtWCc709Ak/EoTYOWxuVwjtToGVrsN56deGrPHTiyv46g7r3\nF+ENRfN06vwoXJpPHPI22go3Y05fw++9GW+xn6Lm74fE6xujRGOG1bUjB61X\nWhUsDFy6WDjtTv1jivgAV0GfZQ65Gku9+S4cFTfrrckg7GvEXhgtLvInUtMS\nC4bldtCJJgaOmPN1Y6lZvegQmrBuAFsIF2Ic/jBMaRCUkNYVFFQTYd+Gd1e8\nHpjtQWpQRpR7ETWtEzfxNxdA5wY9Ou2yJpx649hL5Kfj3E5elNb6WQuwCGsW\nsAt1Sbda45dasAlSnz/3dTlzoVj8KGTl+UoHIbOPxFY8yy8QZIk0k2V4nH6w\nADLf\r\n=CNTd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCissX/9f2YlSB5wKrM9aVCAnIIdOPcwNU+aUqQBloC4gIhANORiwAdm5xTJ/r2IkcKvi2/Zuz22LtnEdDxN4sZYRC3"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190813210448_1565730594660_0.3239075546263983"},"_hasShrinkwrap":false},"1.6.2-master.20190820164637":{"name":"@atomist/sdm","version":"1.6.2-master.20190820164637","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c5a0da7d1ad3019d41f8fae76fa0f9f23c2cbbba","_id":"@atomist/sdm@1.6.2-master.20190820164637","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-t0poEU16Sp8lNhA64UwTVDt/tYjzsniQXsj3fwtthoKXZO4frIcIJuV5060oHQ+YzJCqHa1kqQDHAPDRC7Nhfg==","shasum":"1b742281e1562ea3bd2aa2158ee748c004567926","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190820164637.tgz","fileCount":756,"unpackedSize":3374260,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXCTaCRA9TVsSAnZWagAALN8QAJbAH8Dr/o2vtBMgXQRc\nzc3yqQJ2tOS7iMmZvSAWw/pAM2yypnA7HUfBzLPRBtkX6TAZN9A+rn7BpQqI\n0RKRdCxLIrdXvyKEjaJV3Qmto27oTuIQd+2zxEHHhGqeQJiRBQlv3u8Uh87n\nVQJrr8F4f/jj3ihS/zcSrhrC7SoBsiPKkPikGsLaEbbiYvVbbZ3LbUQZLH/6\n0O0Ow9j1QiJxonau+RkoRa5RduMRyMosGNl1n4nyqPx7AE3tDWkdLZP54dz9\nhZtIxNYdhH0xicXmVemrmINgIEq1OdXyZvTBYC0kVYpsHHpSJ519VJkOJfY5\nfo+qat3WXDMhdqPTlsR0m+4wXzzpEQXukhVF+63w/0W6MZNzprQg4av2Io3i\n6341ewTPbpaRUQLMt7/4fOIU4kj5Xs/2u1IG/AoiimK5fgCqhTJwi0Ds2Ydd\nLPP/R9GYht2VW+ZccZZv58fXnGr5AG7hp1s+QQHFn7Itz6wRycyQ0sCpkfjp\nXqO+PnqBvVBIs1jV44xE/5st22vzVg87MTImpqbqazBFk9CKkGPupKIuVVwB\nB69HyqkX3hOkrYXwauUnqphgEVNwzIHxA1NHrsW/OJPou15/O5h2twpp7G9L\n5P3pXmEOtv63yKY/HgIoUepOItEgDWUbAQ7s6YOGzuJc8bIo0mNq39vy8wV1\nNpSS\r\n=hL/U\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDLzLpCF4+M/tUYqcIBcqhkZP7WW4+4Izj8cJHB7Eg+sgIgF1X3YLPDZIXDcJ46IY3L49HZ2/42mZ6XzJrsLKz85kg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190820164637_1566319834026_0.5597355608237127"},"_hasShrinkwrap":false},"1.6.2-master.20190821114832":{"name":"@atomist/sdm","version":"1.6.2-master.20190821114832","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cf6b0a6bf8e78748e7252649ecbd52307f2aaa5c","_id":"@atomist/sdm@1.6.2-master.20190821114832","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-Ln6BNLbRQr28mqVxQxcNDZFMbDJ8vLzmVHvq0c8F/cD/yCC1tSVVdV5vjDjHT7TePFxOKrv6Z2Jjes7oyM2ZJw==","shasum":"83899428ce9f369caa155cf23ba1f9dde6edd0e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190821114832.tgz","fileCount":756,"unpackedSize":3374653,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXTDKCRA9TVsSAnZWagAAuZ4P/1UDLAfrj80WI202uk4V\nJKJqnWgOpJQeQAQHaDL6vZ1jTXLuQQ4S+qUpbg95yJb2q1kjU4tVwCQpHxgB\nKjIgI1o09L8CpKx9JCeVK+Xp092zTdYCBiwlWIhIg7lm7eBh6LHWkGZP5Ja+\nd/CpUJx22tJrhMZF8yc7cp8zHPA3AUzXFypRIa33Ipyi9pz2qDx4fOIfAyBA\nUdgupEcAcqW82NUpOxLeRn1zCabre4gAFZr8kfJTUrEAcrorfI2dxZzUUkKK\nJk/tEBLKySsItJl+TowqI+JS3X2hngbemCxtMhNUehmJbeKO3PbR9tCqOtcG\nZcb99UFYqrkstuBZl6pSga546Gdw43gFly+TdPzbeHb7i5T4uGoTITg7v/J/\nCfF8kueciDcEXSnPTev+IGDcozKuVZR7gYEyYRwyIqYhwv5k7eVeS0KCU0Zu\nk/ERW5ez1IR/Kx6bTfC8a0OOiZo4++c5EW0upyfbUP1sgI5doHHNwjtLQEo0\nJ4bbJmN9LLEKFtsYHnW2sZIyheyJ7tDNvM65gio6fI5U9wO6hDEydh9iVW51\nIamIGgUDgSNN/R9b3DFywGWwEvXi58UPr9zfyxT4rrRz5iHPhCmubeXpgPg1\n2h70/WX7Dk4QUFgYij+dq4dVPIKHGckQUMOfQQxvb+rGjWqXCOA7njvlHw06\nmi0H\r\n=6fDC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA3xkusLnhHg5pc8XkEdXVVdqZVaBiXIkBl07HTK/SArAiB37Dn+omBuVDvSNY4M72hzVoZ7eVfFoD8IEEvlltDeUQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190821114832_1566388426085_0.30257264046670973"},"_hasShrinkwrap":false},"1.6.2-master.20190821200131":{"name":"@atomist/sdm","version":"1.6.2-master.20190821200131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cecdcae8fcd063dfb01079eaf7b2de479b4a963f","_id":"@atomist/sdm@1.6.2-master.20190821200131","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-ot89blCekpGiGe8X70/tw8GuM1TQKP8bpZsGQUIi6C0TsfTYo3Dlh3TjiRjmz6S4Py+EWPSGw7LVh1UXrKge+Q==","shasum":"e50467d95eb84419302dc35d45b6062a0256cd76","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190821200131.tgz","fileCount":756,"unpackedSize":3378070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXaQZCRA9TVsSAnZWagAAv5AP/2b0y4Tmv7uXeDa48TBz\nT0BQ5EVTETg6Yhp22Sy/GAxWzUKkKLl8iLmTjU0le9b+q57zCtVYIBn03M+T\npY04ZKPTivH9bEpfej9sm5yhrry9giULRtkOzNRnBN5xw27hw7t+nHg1W+Oz\nlCbfKUV4zgs2JDxfwTXP885v6sr2DXcwreMND3/XK91sI7GWr8HrtUzuzh4D\nQruBhhj2n7eRmcOJomGo3drz0AN7uZu6VbTI17d/zNhYI3oSq8/PduiYRpXP\nFn/fATox0LdmMaL3QVc/Pue6cAydLJRh6q1s88cMIgr4WSrbtINtMSAewpkB\nCrATqgKWe2eMvHGkY0y1/pHxXDkEB0QULipVlOAna94Ng2trWl82Tcw525ud\nU+zg/+ZNqSsK6aY34BfJ9FTu2lNZuqIwj+w5vh5ZWtWFn1yrnceDMq7KjJv6\nJ54uM/N1jpoCReRUCXTKUyvckYEH8TXpJYbC6GObCWSTBSYvMjbHecMtIxX4\novpz8WT+19yb6bc5DpLBjrty0BSgeM5HNopd/ad7oXcP/Nah6Ig7dAiSYnY0\nYMp/0tdI9QAolL2qVAybHxW8bVbCceq8ZHD6BSKl1z9sOyLVPkBfYz6SJk5y\njNzu3PeJhLBdwcRMOMgvHbU8A8uqyUWwK+VdTUQ+yc8TxG4FSpLMp9QqzyFq\n6VBa\r\n=F0PB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHAHLRl7E48IgtKZO7OZkFMPiPSBrfavbNKK+xcLQdSHAiAbUKV4N0QsU/z5V+415wEDnJIAx0oZdTtGGA8uZ06OVA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190821200131_1566417944485_0.48801878353885475"},"_hasShrinkwrap":false},"1.6.2-master.20190821200521":{"name":"@atomist/sdm","version":"1.6.2-master.20190821200521","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cecdcae8fcd063dfb01079eaf7b2de479b4a963f","_id":"@atomist/sdm@1.6.2-master.20190821200521","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-OBT9MYAvrk1XnScmUelxzd6txlFNgZ9DSXpKTZhkxja1DT/J7DDY/3UrqH6uoL/qTUD9LfCzehX6gdlWId6kVw==","shasum":"5d21fe9208830b28a254a634c00e215d2e4d70b5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190821200521.tgz","fileCount":756,"unpackedSize":3378070,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXaUeCRA9TVsSAnZWagAAFqEQAIZCKbyhFG8HTBNMuofY\ncbYEjpLg/kFCtj55eAHArLtD6c6+R3VNxMOl0zUO1g8TOH/2OjoQ6XTPcud2\nw6eZL+IYV1MV3APib+7xFX9U7lBMSyezH06dfRZbQ1JpMrgFAEp/a6QnQETF\nnbDTaHIGNc+hpN44getW7lMX6s3rQ3EofNCKM7RC9unowmaCjjG0YfNDeR7B\nCbKiokym5efQJplpVAu1wFluoIjbJWs0EVIqn5b1g7FA4IDvCHsDzqQh/doD\nav94dn1zshdpBk9TvYLZTuhrrs3YLD7HyDG3ILSeY3TcKlf4S8qXYaeMXFV0\n7+wSPeYgJ2jcOH2FDhBubx3bWeo6QIWBxCVnyfjHWIucbxMIiKua7UPkJhuM\ngU5cZfd/CMM4YEYdvOYja19IuZcdRZ1PutQRHCmNkywGAap0fYNg498fbI9M\n9iLbkEchTU6KnXyId9KricOyb97l6gClUewipZ+r2C5TbbMIA6lH1Z1hkoFi\nVKffRBSiaRJq0o7tMuybaInDD86e4at/2Ce6Ek7xGii/E1jhvZ04yU2Hbv3z\njpGCSQVsrhjRIlLBW1gsY2hrSFVRr6k16K48AxFRnDbiI/9YHt1jvvuk89xY\nfhwXnqurcKGmCRtyBAgeUSBJ/LIyVBndmyKqCN7lEqtP7o5Q74V3DMXTEv69\n8YNa\r\n=o09V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDO3jQ+EQ2s8om261dbMmVcOG/D+519OmfMgwe3AC8wewIgUykSMs/cokv5NqB4oaL/XUqKG4GVoqiFloO5JPh04lU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190821200521_1566418205823_0.3037111516181217"},"_hasShrinkwrap":false},"1.6.2-master.20190821222758":{"name":"@atomist/sdm","version":"1.6.2-master.20190821222758","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8c18e11ddb97385589d5affa91976ecb9a37a842","_id":"@atomist/sdm@1.6.2-master.20190821222758","_nodeVersion":"12.6.0","_npmVersion":"6.9.0","dist":{"integrity":"sha512-EPSDeu0r1Da2M+hM2cVApYDmifbleXkWPLNt2sAnMUuz4/6/Gw8JjWshiXHHnISSjcy285SYukvUakQKirXLpw==","shasum":"e341a676cf2467b1e66290f6676d39c4ce9867c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190821222758.tgz","fileCount":756,"unpackedSize":3378247,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdXcaWCRA9TVsSAnZWagAABFsP/0+YKUuc6Nb6IOY4h597\n3rBKibVuyJLy5eu3ua+eiOMLN8Gm6bCCJrXuBEzjanRgGEsv7jstU45ph4Ro\ngYK8ifOOPTJzlsFSqDWziIZzq5rI9tNPggo+ANLZxhwyu5HfZF00KI/FRgek\niUH+qmRkXWQkuzyeb+OEcnMHscG+UgLlEWaz8nRfA7XnM+X9rVeuEpzglyiY\nOL1ro4jJLWs5pu4VrBzZJBGclbinvduAV9Hp0BgBRxUtIrA/t9adhvVrlY6+\ny9E645hTfCiAlKFa94ojz0TWOwPMcuHDaIU9j8b9leuyjR/0y7LVTyMn5Dx3\n85p89yubCmlhMcP1iMQw7VQ0e5KWEXW5zMEPrcHecaCBH8Rpbzg5HMf6oZ5u\nSNDVuh4oDMw9O/STfgJ1ftl6Ebt6ZFeNOEvGiQF/ltJkYkeE+tWYX0C8EIME\nEkTrCFW3k7AmUTq7Thc8NNnlh7GzylGnznj9KobSkTWRmxx330f3DjRfIWPX\nFJvA4nM8O/HKkcAR4PslANYyIwNywBMPszH3a+ugfAaYTklGq+mBFsUcGpqc\nHzWn5jUuzQy76N46Mls+RMhWaFRvSubjzO5dtZEJMiSuxuXW4xbyqYTJ4khT\nEqt/okpxUk3GizQTreakZMCmNIlgzPNqo/x+X39YnDxys7RucQhNCQ1XXk/n\n6LGx\r\n=GXV5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF8l371OmeyQ0X7UDxsfG5nZ8LXt2R5cS/A11r6R8EUJAiEAgCVppeeEFsT09xVKVuQgbjiKEAV4PE3oGHOmo7XUHBg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190821222758_1566426773747_0.8392656536234646"},"_hasShrinkwrap":false},"1.6.2-master.20190824124708":{"name":"@atomist/sdm","version":"1.6.2-master.20190824124708","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b40835bf6198cbc09d585b711f42653a95b84277","_id":"@atomist/sdm@1.6.2-master.20190824124708","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-xvMOaSIm0AMW4Zo2xokxKbBK43LUTBPRruU3cUlutG9r5X3uzubG/etZJTzbIE/rUAongzYgVdsC8xdmG6Qn0g==","shasum":"3bad0139388cf6575dfe47309a9cc0ae43ab6070","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190824124708.tgz","fileCount":756,"unpackedSize":3378095,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdYTLLCRA9TVsSAnZWagAATgQQAJBixAHGoNO5pGyfoeqD\nAXzk/xVK6uf+u4pB5wEtq+K1v0FUMde6NHSzBUqqIjxtIXIgRyXHlR15owdf\nrb5/pF+OGZU41YZy6jIQCRw82H5aKkAjIIuezWx7ZQkiFPst7HuRAD/3ELP+\nvO/AuSYmPgsW7KwMBc8gl4eLjjiSn94hALvhmO4YXQeb+tmyHY03tUCM8Ule\nnD/Wna8V/WHhNzPbXKtX9sCeaDXEZ+UNMkLApQQMmo1Nev1eKFra3M0WQ0Ds\npqAqWFBp9Ljpyu7th8fhwMQ0zBM43ZPpVVXbMJHDeGW5QXAaE63A1U36Ndxr\npvSvoUGMKsG7P6G2dJhx8A9l1+5l2BZ/0xfEYnJvSQODIXweaogz808sAupx\nZUbgqaCYAss5s9RygqE/RE8zxXmrww27OFhZdMi50b1f1PQBQvO8XIoAU5hF\nXG17Zqlf6g2/bljbTJBulkclIyhmrg9ny8uxsL701E5BJYp7TTSaZ+JzeY1E\nXJ07vkImNpn9cX7xu+ZbDUBj0lkxA6R4HKWjkO/wKCK/O6XiT9h5PdZcVm4b\nnnLztCTAhRBTG2UBGxd6LVkaMRlwmgyP174zxHk33gf23fQRpFEh8eQse7jv\naMxl9Jidn09+yT0seObVjyOvnwGGqv7uEZxlBKRRXQtRIu3nIk+m95MGLZiR\nu2Ki\r\n=a19P\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDaDUI0ECbpGNfF6wCPgkDcCGg6ekn6c+zO+58wXJtHFAiEA6lqwCSJb6vVIFSs5+kMJSmlMM9gUlS9T/xChKq6L7gM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190824124708_1566651082377_0.24326555041675757"},"_hasShrinkwrap":false},"1.6.2-master.20190827165730":{"name":"@atomist/sdm","version":"1.6.2-master.20190827165730","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"35e54a500c66adfb35418b1260c55e079eb13ca7","_id":"@atomist/sdm@1.6.2-master.20190827165730","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-KHWVH4cGW33KaSjnJRcbqTzGODJ1LDoWbdT5QoHRjTYUOZp23d6tESIU5AcqKbogpB8hlGaWuT8T0W1usaiLtQ==","shasum":"2d2397fc0b6dcd35e7743f9c3ce4058f9bbdc2d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190827165730.tgz","fileCount":756,"unpackedSize":3378361,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZWI5CRA9TVsSAnZWagAAZdsQAITzM7akOIVXMiSuwwXX\nPz6GArQxtCujesL+d3vGTWXZwPX18q8Aj/cIhvE+dZjwlcphsBVh06+jGIwx\nGiXYD6dAQpkop7CTTkf9HzJ5bHqThy02aX2TLLqhqJwHwa6mtYrYf7jiNlCC\nuEtLF1V6vOubOyaA0IvFw1XI0DrfLtjIWQvx/0g5VceHT08tv/tZv33AWAdi\nVSi7eU8afeazWwCIghZsrrjO8Z7vZk6il+4ueGExhdIAxgARo+ung35f8oi4\n1M9TyZKpIh5T2u66QxvjWwmCl/gPQN9RLYa0NFwQf0q1b08ufJbmqkfkHxs6\nJBJ6vUutj1hr4O56bHUolUVqn7QH0kpQLijo57ZKsqXq8ovmoZwQUTmCvy0H\nyglNH6XbgUnIlacsVJ82ndkR55wo7Z216Z3exv5h+Ci8Qz7F5FYYNfJt15MI\nQki3885NTFOfHep6kcLJ7ctGnxekPNYT5T2Ke3i3ne67wz4FMoNnzYY35cd9\nNmqR9Bx8i57jwy0ViRV0EwENyTm/wwwx5lBn+IjIZYQQBQ5PqyNlfLzvRGD2\nEUpa46V03X08FPwRMn8DWZ+S91WkvPN8I1ixrKgPqR2BFqqRTnOCYoUTf4fu\niyH4dilEsAsMQQAw1zQnY4wgD4qQIspnxA0YhFyd+tSXl3gB4KIGjOB1LKhQ\nyuWo\r\n=Uyvc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCrLtbAEB0OWZPkRXWOJ0QmPmgS/jRMLj2VFUv+xPnGiAIgR6lEwaNfxd0qBOf7UCSxQgnMlFsx2ZQSGuI5/9uE7II="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190827165730_1566925368374_0.29160225064791745"},"_hasShrinkwrap":false},"1.6.2-master.20190827180600":{"name":"@atomist/sdm","version":"1.6.2-master.20190827180600","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"815359f9ee329325796ed9f8f6894adba0837dda","_id":"@atomist/sdm@1.6.2-master.20190827180600","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-z6IiNKTZh3RTqmwgTRPl7oHtHrYzkgUcFAGGrw0VHDZGInRfXH2e/CTAilLYc41yHoSP6tSXncvhYnS+s9w99A==","shasum":"d79c2134148ab8bc48eae3de54a5cf72da53a01e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190827180600.tgz","fileCount":756,"unpackedSize":3378478,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZXInCRA9TVsSAnZWagAAzmoP/1+gZQUpSqmZltVhmo2E\n2K9sd9BycAEU6NQ6kPr1yNxvovl5qrkg/sQ9AN3XWCLG2MLkTpBDSp+t/igm\nfsaZ0MrBKY/wyZ2bzYed9WMhOHERLQbgsFZYfpxAOniUch1AyLfLqLO9p3v3\nlaUZ34wBtg7OwiL5o2TQmpntlFrX6IdLNB/K190EchHFy/G9CYtN6LMHQ5Rl\nXSFfgnWqj4BJYr7GtRzYOn6mSJH5EGB/PDkSw/qo4t9thsNrtCjenjdMqoer\nesZ7ajt7Xtbla/JPH2LZwZAm741vlCZVyhnGWXw7wgsZr5PfXp9zrANMq8Wz\nN8DzTUUvAkObhbBTyENZkC3D2rQn57AhBTOhsYMqqnaQws+ofwE5+2o2MdIX\nbbgdSI9J66EkcEqLTC6ZPNeKwpfcGFgo6dX9iVMaUPKJgGSFGDmWCi6IY/Be\nCa2o323iQBVmUs9kcuWhTcC4hRhPmXZiaUVEAjkDCb1rkzB3dNf91deDC9GS\nX4aI/2QJMR2ukBoLDvVzU6mABR0tglh7LbSbfZuzBt2eykVduMIfc1HQigY7\njHumahDAv251jtuNknx3dFYHAiyC4iE9yJbEJzrkz9qX/ZKZsZ9+TAwiQZ7w\nVFNzXtZnDsEOrgKZqLr5PoBY6nXZcAvcWQtrjzUe7rp4aod/jx4dgbehtUNA\nG5+l\r\n=/lqc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC2VDxfkP1+cLhyud0FVuL2shwbNVUjC0O+eRokD9suZAiBXO8erXh0J5c63ssbT1nxGes6cK872Lpfq6KExj3yRKQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190827180600_1566929446654_0.2736841142219688"},"_hasShrinkwrap":false},"1.6.2-master.20190827182350":{"name":"@atomist/sdm","version":"1.6.2-master.20190827182350","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9e3e60d8410eaea4c5f315308f5faf9a11878d7c","_id":"@atomist/sdm@1.6.2-master.20190827182350","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-QQiGHXNWvI3aptq7yTUxd3OpW7IT75124WTDLrEQU9cDY3kfPNkaow6YyKHTJcvEO4xaxrSEuUCyeNBxtX2NlQ==","shasum":"ad02865f6ce1ba71a2fc6f7e2bab21a3c423df12","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190827182350.tgz","fileCount":756,"unpackedSize":3378280,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZXY0CRA9TVsSAnZWagAAx1cP/Rb9U95oNCMVw+7h19Wt\nneFlqhYasx6ZkyKkopg++58z4zYY2RaYfnurlR/mRrBNAlD6S8/wcKlfRTIl\n6KZ1bkkGYc+u40XSJ1AkMDCf5J/lC7ZFARIms+jpN1MS3k7k2peKc/LiffJp\njTAppitwGDtMsD1k6bxfGirbCNQK1g3h9B1LAQ1sg4vgDUHsGNet+I9SMeuQ\nQQXjoqlauVa+yE0iifSZzqsibQcuQ2QFvZcLJda9XLjoVMb0u5V8PT9LvRyc\nxLRcZrUuy28CFTJCX07pYm8wjRMV6Mpcq0JtC+3ekg0+m761rmscQcXDLgpZ\nsLC8GFBa6exvir9WpDk1i47j2tiiKvuSwkrB5w3m5OjMFNTTAqAPSjlstEL+\n98Qss1gQuOohipVOwvDJjWaBVg8hdKmXL7OsbVd0j2XJ1Nf0UqQntN8mSdMJ\nuGHRKD8UV/KaCjAWVb/u38uRwd43hy+z9KtJjek3vJH49uAzn+dpzIKCJdwh\nDLmo8h36rdt9V+SpRCo3z27olnacsie4zgYBtlbYTTyDNfZVEH9NC69vunWo\nNdzWuJ7u3bP6YTLEO+P7VL1h6RdyJJRHoyh5audhxpnuf72AtgSiOQlVfGiQ\nmoJuyTAfs2Se10t9I8t8+OirVwY9xQ3wSCeEwdlDAlORAwQDgU7W0ikMvREm\nh7E6\r\n=g1aR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA83CI+jk3y5BGc8W3B597JrYhwXzlwHTrwJAYCQCmi8AiAENXPXQeNjGqOYkGsjx2srLNBBGJ4u9/CYiMz+eSYCAQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190827182350_1566930484073_0.9038993138090885"},"_hasShrinkwrap":false},"1.6.2-master.20190827210741":{"name":"@atomist/sdm","version":"1.6.2-master.20190827210741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"47100083bb5caf9de0ac2468c08a59f34eb10bc1","_id":"@atomist/sdm@1.6.2-master.20190827210741","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-DMgXANl8u+h8ZT5cSadVl4xqN0XrY7Q9x5X5V9bpO/0Wj8W+v2mMA8tN8j1QUU67omCQ+jKMraE20GuZohHOxg==","shasum":"8bb45c3c915af9a49f86152a6ed5c39d43296aec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190827210741.tgz","fileCount":756,"unpackedSize":3380053,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZZyPCRA9TVsSAnZWagAAmv4QAIG3fUl1HZjt9p+IPCKD\nSDxI2WhlGZDNQRd9kbgamVzZkR+pL++6L+cqIMeYxkpO9tH7KwkQAMtkwbiZ\nwFvy7FlvLmioff3lEqngjS1RqDPS1SdA4O5tlM5awq7OZ8djAK1xfDDeAKwz\nItyXBa2LPOr/OU+qpYW8IF9NBNjawFMR5dLTMXy++aKgY/Q7Zf9bOXCDQtd7\nReJZxq/wa/N9aruLbYjFJLB7gaYmHKK3Kagx2wr6QGKLhsFs4ZaUWfUG3BRX\nW4DACNM7L1MZ2kv1Q9khkBpE50f4l9qza7ZENVgeyJrAXF+ZgoA2kC8vzqMl\nRtIJ133JoGvYMQ98VnUBuN4Ob5bjcRct0T4D1tzYHaNSQN28pK9URabsvrVN\nrb9Aqwkm0f9eIf9qrGTh0LUnc9Z0fcgD0KSeNpwHtuq5obccA+58iTGgtwrm\nNW3vcQ/HnLHXBXcU9JJ7SlWDXCGzdZR+q92TcNext2NvzkVaghJPhkcKtxnq\nd5aCSvp9/OSSRcDeaTnOuNGFM0jL2HdE0iqSg3pFcEz5JmUmwHCeKEvrIumM\nqQHFGZvu31LUwsEDqe7IJio6S9R417sc2gOZqWI8Kc9cOOIXPoOfnBESWGyZ\nF49UliiplRVx++uzWHLcBNGbzpwHbif3xQW9AYmoSr2/RAwdGIDBijaTlKdZ\nxOVx\r\n=b5yB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDHPIV9VRQsivDMxMx1fopcunsPILUA2t8doxQdRtzTPwIhANfOqP0e5bi+n75zjvppoXvX7lIoxPxiRP2+NgYpcjJt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190827210741_1566940302789_0.6338177994337126"},"_hasShrinkwrap":false},"1.6.2-master.20190827224115":{"name":"@atomist/sdm","version":"1.6.2-master.20190827224115","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"16a8a2d6405d0d7bc275c7ece037ca63d8675715","_id":"@atomist/sdm@1.6.2-master.20190827224115","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-9NyZ4p0DI0CR0AxSo1o4O/Mw386rym+Kvz5f1u9yCp3a2HPMlF94cC6L/wzVE/jVa7N7IpZXjwNw10Uz97YICA==","shasum":"3d8009668da015e7ccd9cae03c20603caaa772a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190827224115.tgz","fileCount":756,"unpackedSize":3380066,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZbJ+CRA9TVsSAnZWagAA8eUQAIcM2aMA4mY+Jz6Z4oFo\ncS/KdHTyU/74byS19P2nknCObZxQwjOn81BC8C9ZhdZpLc/tJ/XgD5q9H0jh\nkLg3LBNDfg+wdjYIHxM4tAef1t/UJpY4HYfEse7UsEpAjFJHBHT8eJdfdKoB\nYMkfenh63NwIWDyGFfrmj+lGwUcsZTIS8UKqBB3sE9CFg6xFJovYZ2I073c+\n+SgRO74OkShyONYs+bvpR7cDN2CZQEdHN09vaF39o9XMQy5YQUOFqUif1n/Z\n1xLn0yL7olORruUc656g9YiJKcSYNHGIAJ0qgkpS4wq+Tqizr6C0O6PGhZ7I\n1dbXRZreRE2Q055iB/IQ3HoGfupCQJAXQFt0bPIkcwKXngF/uTsv04yfSuBT\nifllM4hU6oo2NOpvuZpY2W+rMA0MFqwpbQFeQ2EMWqxeISHc7STatB6JJgMA\nxHmXVshfKA7mY20GxXKp+9Tpt5Y4hPKwglBhzAhFF8HqWIhL9tKyjRNfDfik\nVygH5RGoLKeKrtpx/2FiPYcAmK231SWdhgfEpO7fCt2YArzonId4PdXPhK/Y\n+6QGleQ4Fuv5DufSchoDfP10lnPPz7lYdPoJWtM+x/3uo4zY1jqUDNDfI5iZ\nUblKMTy/UFhsYkK5ETEv36rGm0iFxanX6e+nTKJi56Ve3GepYO2ZjU1vT6af\ntnDr\r\n=sCFV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCd3AB7NGpJdow0D/PE5ymanhkQi3DHiRnV6IQCx9bacwIgaT1NJY7mxEHdZCEGH503xpnQ+0FILwwRkQA5Jn5oJj0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190827224115_1566945917227_0.4549223847553179"},"_hasShrinkwrap":false},"1.6.2-atomist.typescript-version.master.20190828144728":{"name":"@atomist/sdm","version":"1.6.2-atomist.typescript-version.master.20190828144728","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e74170ecf59a63f31f016138ac3f9ed07fac8a28","_id":"@atomist/sdm@1.6.2-atomist.typescript-version.master.20190828144728","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-LkjCV0VVaDW2VX2IR9AWbW3r2yoY/Ok9vYoGTNy6ULKYmx8IgeUdEBEiIqV0Bg942uWvHdkw3FfAuZc1DRZeWw==","shasum":"bdf8b0567b8c6417d7cf465708a4e6a61f869499","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-atomist.typescript-version.master.20190828144728.tgz","fileCount":756,"unpackedSize":3380103,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZpUvCRA9TVsSAnZWagAAXXMQAKEn7OYDTdvqTHC/vGfd\nvqZHK4774jPwqRhBrAGQT8eoDdffm34/aAUpOFTrGtfYyTECtj+5Bjt+6fgH\nuYVE08rMTV/G9nzq+XYj052S1xNJsdE+g0gfdduvaq48AM5392N+ESM4suHF\n3EAr0174nbTisQCFs382aPLWas8R8Ww2taFCxDYGFdUh3wg/Eiqs88TWskQo\nV/hO9Vlpn8qskli6oxe7bOXtv31JSlP5vPGXYHnUjsasaIjp7D1yeWFol+4+\nlksMYdwKJW+b+QDeJ1Wku8H/O7bBrkUuHlMwq22lS1Y441q8Vy86vhRo3qHd\niMcAWlAY5C2ZshSWF7m7yhvJFPhLarva2DqO+Nleg79cOwYG8JFV9FaBZgjy\nCbRZHiNa1YVkRo57gdGUCFwPEevGAKe9f4y5LImwMbnpj7NY55TG8G//jOF/\nT1H0ylGIRTpm3jlqgbQkPqJXYcZYLZS1Sf9I5M4f+2CJ4b93z1iGAT7vE4rM\nhbHaeJS0QmjOOJkNTbPlk3hGFUpbQTCBRO18A7CHctkQZsyGH+r3D0LgzMlu\ndYmY20NmFherC0Fyz271eOapeKHlvmXDCtLTQwmKuMqzqkxs0RIGx2j9inuE\n2iS79ODD+IYoTxY9wYyE7vf49VkTIi+RlOY4uxy09TQ8pCd79/OokpGRjSor\nC1Fs\r\n=athN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAnwGtMk6PfVmzM7hhePwn5iqKxqFThEa+et0QPoZNXFAiEAjZ1Wwlq/TlM9YpQ4mhoS94mROyCWxieEgD1Z1gnjpYE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-atomist.typescript-version.master.20190828144728_1567003950890_0.025845155183055635"},"_hasShrinkwrap":false},"1.6.2-master.20190828144646":{"name":"@atomist/sdm","version":"1.6.2-master.20190828144646","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"089ffd9f7ef5f1ea6b404e05cb2ced3df7890b75","_id":"@atomist/sdm@1.6.2-master.20190828144646","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-TdvjybOWJKHIX/YYvuEbAjc8X2p4OJ3mYqInJF9b8lcGHGRwLlIY+/4Eg4A2s+zDP0TdvW+mE5wvYqews0bFUA==","shasum":"82ff81a8a18e221042a04a1214853c848a7d7789","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190828144646.tgz","fileCount":756,"unpackedSize":3380076,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZpVPCRA9TVsSAnZWagAAGv8QAJb8jTgKedo1vvFMSL7S\n+kYRIOhzZi4dKJ/HsxJN4jmAhXROYwAb4AFpiErTwxTU7OAbKH7aunTk3sgD\n0AGqI7oyMsRp3X06htcM/++oho9Ls0aD2RiDh9tQfZblUQneZQ11DDEB/K9d\n4Dea3/0QAgDscBKK8P8Qf2xg4bFjLFtIvq/7Ih4FHc2urnqYsHQpiLpp1s76\nFp+EvK/a0HUxkE8xCW3HLFJ7HBgmNh+1KNpv9uFyyLehsK9+24EzSNbTtMUP\ngmUmdhXt1UBJlmaz2FR1gKu6algtLjz3TrXyYBgXJeVmV6vOeSelOtA1Nuyx\nvXqpury9uqDxJ7SGPwFPqiBceWjSpdUfZD/jQcsTz8TZ2uLbW6wP/6MRT+m7\nVw/LH+7dmle7jP4miYkrRGkJlbjPnnQi2WeGvRXAvbGP8wJv5Ahq4wvJ0o1o\n7LFQkquqHN4P8R6kNU5af+2ioEYqnLxyYsN/wPuwRR4jWDQ3Xo3QwpKRl/Jw\nUMbZmKPbqR4WP1dH/rrAX/mbKH91pkVX4ETECEeuS0cGI0rfp6yxVeY2azXm\nnJc2vOltUNP5jKx0IeW5ODdVImID4rufqumhBQ1SVdA5IDtoyqEUWgKcrE+C\nCK9wc1xUR9mbW2yY2Oo1avteNk6sDIdOAxvJkD/9gIqfqe78VXFdVSbQD5iU\nt/jg\r\n=dYNa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFzo1B5Yi1kVxXdR5Dqp7GqaA9p405gBM2UCUP1O2NKrAiBgc0lz2AfazyktZivclBig6auPnatuKmd6PIHvG3kMrw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190828144646_1567003982459_0.8995229443026398"},"_hasShrinkwrap":false},"1.6.2-master.20190828145741":{"name":"@atomist/sdm","version":"1.6.2-master.20190828145741","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9219f80f3e17d15cb8600f96a094a19cd3e7ba80","_id":"@atomist/sdm@1.6.2-master.20190828145741","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-acadYE1d8rxcizHCnZuWN/pJZIBE6z/LQu/ZNNPONJy0zgLRKNQQnjTwAg32dVoSXB7vVM6uUa5wMAtkMmpVJA==","shasum":"2fc02e2994d8ab72a964496f3c90236fafb939cb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190828145741.tgz","fileCount":756,"unpackedSize":3380076,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdZpeTCRA9TVsSAnZWagAASsAP/jvJDtizQFYbasnsOW7t\nPO6bPWNuqqH4BBfowumO+fIUsqIl9oTgGFd+LDxiMlAeSwT4SrnJr7Ag52O/\nVIsSlFt9c9nAkglr5OwpdmWPzKr1+SJGwtc1f+yCM1PaaB5ZmUTO/lPgF5FE\nZkQOdOBKgQ0Tl2MZRUYkhkQ4t3vVbJVTKC1wDDeJQGklod7uCBCCLVi2kuAN\nCx0BnVlhRgHzSbXLvY+NX/WY3KtfTfkkskygX72dSm6mg5V5FTC+y3QoBFOE\nP4nIb9t3gqnWQ8rAAFMTnkswyrmzzhn0DPYCiSCbocZ8J+EXyfb08HgeFSAe\n1fjTa2UnMapuAz9GUJUWPam0UM0mUuUfSY6CQfP9/M1qYJbWchtSxsdJza+7\nGB30UXo4n1XZn9v5l2Gat1d4Oi3gJRhzWXOVVZ3QuUkvf7G0IWysGyecdUli\nmm9Gm3MInI1jg0uzgm404cmJWpdOeKLWuWsY7t5xCThOH8+T0g38zXksierl\nuDUu0F8jcErj+Ow297qH1P7itPOVhg0UJm9z5gbehSLavYcwrkc4rM5P2fQK\nyBhkcajqNNh3KQ9Xng5E+yyWzrfHxKMeRpEx+2ISQEE0EClV2g2xEEflESLe\nSPjoDMOnHLUlI/sTyXx0W453c8FMKnVzj+TnDveLGDjmTmc3XEf3OLCjQZeq\nGKMT\r\n=8Wz4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZ0lTS1JfiwwgtjCm+QJepw6FkGfYZYFdB8MObw/RWigIhALq/Bj9L7NFcQR0EAXAe4C5ToHFZBltU1hsBIDiLsI1/"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190828145741_1567004562929_0.521152203009585"},"_hasShrinkwrap":false},"1.6.2-master.20190831065543":{"name":"@atomist/sdm","version":"1.6.2-master.20190831065543","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"^3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b4a3d36961f5bb76c7c0eb43b2aa1f9a779a6ff6","_id":"@atomist/sdm@1.6.2-master.20190831065543","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-8n2ZfMjDkLkdCTstgliwnUXW86tIZBOV+gkMDlLLm4/HUnU0/p0YFXkZbPmwEJDq53x6GO1YUvn/7pSdpx5LyA==","shasum":"a4511d7f9134c05f6dcccdc04c5e1483cbc64bf0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190831065543.tgz","fileCount":992,"unpackedSize":3603857,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdahsuCRA9TVsSAnZWagAAEH0P/jUFtPQrH5lgbLol+h5q\nCpJu7mVR1DF7lrgkq6oRMHKwxcnKmbRQ6/ALpjy6Lz6s/Hz8Ybq/39fMaoIU\nVTAcfgzxk3CNnuF4bCW9YuQmI44Wr9+UAbY05Ky3HUdA2g10liyTZnDlkTRn\nGpHrY3UnrD3IoIUTDtwHVY2755JGRmk8Uo5OiGQqFeO9Go/aJ0VOPNENwzW/\ngiOPsMrN+LMhVvh7/2EyEWFlH2uEEFzDpMUd36g6+tvEvjWFI+/zT/T+bqXs\n32s5sFgfLAAT5l01qaFQEenO6NTTJBs34fV9oV9hutHIiU42Faf4G78xSCpz\nbViPTfE1MLNIn5BtQMCecboQKskyVNhXDVYLj+1w8JMzMkksDpdHsEfgYFOK\nNSzD1GSD8RS5oDMKYMHNpTyQ4xt9AoF+hSju/AZL7Cz+oCnE2I+f487QJykM\nbifGIMiyULJpRxuHUYxK2TPCP0aoXdOXiDBr7YxPpN8bguu/9f4XZ/Iz77AZ\njDYzdYuqzpjsvmqwk8p3cbkkRn1g6o1bPSbOipkFl59jLWMiZq1wmLg+tYqV\nu1ZppSPmshVHtzPr2/Sb9EmLR5YVx5ZEuSj8j5P+ZPNAJFIkBVqyzdw3WacF\nDDApwP58ep/arLxF6CNnQgG7PQ1kPrLXJSBpScqZnScwEuXcMVM/2gmg3SrE\nRXRT\r\n=Ofij\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD8cz4oPc9v90HD/FFafvGn4LgRz0iuBFeNdLV2H76rnAIhAIAWAmHOsmKNbw1LRYPyoYPfb0jlHoN8L7Oby5f2OCIC"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190831065543_1567234861574_0.5014065303161337"},"_hasShrinkwrap":false},"1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703":{"name":"@atomist/sdm","version":"1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"527b9551f98b082944227bc3244acdddfec30e98","_id":"@atomist/sdm@1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-f7NrgZM8m2msYtRyO/qn4yt4Zzlg0ozx1luNX5ccYjabyMkLr5y3C9E64p+WPJu9xaM6egk+nFPmfoZq9I0oHw==","shasum":"bded346bce207d403557b687c99bed3df5508a40","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703.tgz","fileCount":992,"unpackedSize":3603893,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdahusCRA9TVsSAnZWagAAlVEP/A9OLM2BkNwoFzKSjxP2\nG/LLmovEOoYmzfoPNS7/UyivdU5w5Jd1ZYGYJvUD9WXhN3Wu7aLmV7HML1y0\nn1Iup+qo/ksAYKosHE6EZyZvQRSF0ydsqBYoDFsAn/hfsXPnFkkm4GLdNy0n\n8/kUOvcSl365QT9p4q+vXOPGc3D8nnYkEDq3dAHgnU2Mk+jGIjZtyaY3+ZZB\n5CAq1sgiM9wwC2Qc86DVO2TZyqHvAoR45rWeC+OXLznb1a2YIFSkn7Eb8oMG\n8N8ek0lh2r1oIpKz/9DSijbdDbERNcxv5J+k7Zq7FvaMEEep6fwMDyPjvh3b\nrHea1mIdw7wcrVAytS319hvOZ7C9lYWJAvTJByluzKkFrFXYRkG8yhzJ7Z0s\nfOshZ4CqsgxrkNF7vnGvPCKt9YdWTC1ZR9e0Opiizl1oQOm1N6pDAjNiLM9Z\ndIci0hTZmaPnUm3v4eioXVKYnHtVKtQdCVzg0MfWhH9KpxXp/1THdccXtofB\ndm1rcPs/p/47TBpRNLwMcVo9II9EJoYINrfs7tNo+62jSOz6IsyBJ5f8K0Pd\nvzqu4yRhYqcC7XV7yw3zn2bRkS78WXhfTSqEgwNvOCFeGQMdiQAGi9zLpgZd\nZsSC7Sz+oswOsMt8KfH54pSF07o6aw2ProplVVbcxeAkPZ8FPMd2ONjynm/I\nbrI9\r\n=zFTS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDE7rWc+z6+BYvver3Dtw01qpE+4lrtG0yYmNHGRrIlPAiBMQ4Ax0biPHMAGY21/9I+/r7hF/tTNCRaFwzaMohNPDg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703_1567234987380_0.32102262486870003"},"_hasShrinkwrap":false},"1.6.2-master.20190831070552":{"name":"@atomist/sdm","version":"1.6.2-master.20190831070552","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"db318530ba7bcfc042f85f3d3ce8470d509f1d44","_id":"@atomist/sdm@1.6.2-master.20190831070552","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-r+AAAPiQ2ypF+HwZnh31Y+LAWUliDriK2Q3AXAFlFSvIpPgPNaBnUr6eqIn8mAIOPtaiOOKDusqC1/1rCJtlPQ==","shasum":"ce7f88dbbc063e014157a0b98ae1959d3c070d48","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190831070552.tgz","fileCount":992,"unpackedSize":3603900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdah2LCRA9TVsSAnZWagAAOc0P+wfQE3cWothzl628ix4A\nbwHuN52+k32AqqwEoaUUhfom1CrK4rbaW+xpyJQ9pz3km3z/xDrpsQXLr3dN\ni5bwRyRFq50+GZmM4z2UYxDA7Zj6bfhdCiC3KXmbuLPIvjh94n8mQBxTew8/\nUO51bVzlAdUVP5+WNsb1pbThTtYwLlgO7N5gT4QlrHBkGIFB6hkNAfuTdo0J\nneZ9Iym6jaGH3JSJeD03NEZTRuLg4qPe2scnXFIevWrLnKiNZbnlR9AVxug4\nIPL1f9SwlgyRbeHIrHJM5sMIENE+QEx5FsG/Zqj7yzLZWQNfIurIC4Huu4CU\n6yn/gDVv26UA4/kUWa+ZFQ32lCicTbO9p+l5SqqB2kne/t/0F3xzssG01SLm\npqoZvIZOiOsq4VR/FkTuyorQvutN0BtPfECHbxljNz98yGI+15YkouJfwZ8m\n2m9iYLTpAihFEuCLJ2V/asO35+4UaG2FWti5g8cPlZw0pXP87yRk/trKeoR5\nskZjhzFB5qTpaUdqt/o6m50tEdPH5vSdYQtcpdHcCIl2W067ATFi5q9ESbix\npVD52Wt+NhMl0c8UlYUbv0/wwy/+YtEqBtqwFcMrj+VG2c/vut+XnfeGIebt\nl0h+q9zfjL1yGFk0qSJRGJ2HmUziFxtpxV0GGw1yvjnV98I2uAe71I5yxWro\nJ5WX\r\n=zAVZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID5VouXpo47rtgLX26LBTqTQGT5DpsU/6uhxuIYFo+8jAiBsy6FyVQZDqSQIL1GdE6ZFAGBItRY4mTUBXKUuo3g0AA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190831070552_1567235467252_0.4390080999396624"},"_hasShrinkwrap":false},"1.6.2-master.20190831070813":{"name":"@atomist/sdm","version":"1.6.2-master.20190831070813","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.6.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.1.4","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.17.1","rimraf":"^2.6.3","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.18.0","typedoc":"^0.14.2","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"db318530ba7bcfc042f85f3d3ce8470d509f1d44","_id":"@atomist/sdm@1.6.2-master.20190831070813","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-OwPQvvqXwzEZzctnGv0zm5Sq18LQizJuQRbGdiG0urlum+Rt1LwVrLlu+j3x0+fkp9qPBrXYSFzTVaMBSoSIEw==","shasum":"97d96b5affe1168f2e2014d7e9bc6d4fbec0a7b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190831070813.tgz","fileCount":992,"unpackedSize":3603900,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdah5GCRA9TVsSAnZWagAAI88P/jjlWb3tYD8YFUngsQj7\nffnrQabfeOkJ0kXCNuumJ8qhsFYCXqf2/dCgBfNU/Ml5C1Kp5HSyq23IWWiW\nRxK54hERBwTrTdQJ14DVAbElC6ZNn+gQdYSgvup/ZdmkSdB0uVXUV1GLiytE\n9WfnYe86cJmHhpe+dmRclbPs//GrGftPS8Q6iG9Lv45WYK9lJpPWXCOR0+Fx\nHxFvBAalTMTpxcmhpZe+WsHDykbedwW3a/AQl2JGUIQCNMFMWxWojONoQCa2\npFQZG0KYPdLakgB1Y/ysKkkKddxsSAUQFFpxX89WHNtBQLaKRwbrmC4skbJU\njkIWd2DvAb1PJ5a47hBkZZbnYxFjcUHgQXkM9Q0+DbfFCrMCx/e09f2BAFQl\nPtX2XLMudPA0uiDaT39TfBJiYNx27jy/EOUHfLEUSvkN1bGI6ijk9Zch8eaD\nV3c2KTl7P/W9Kljo93RbV5Ui83A7OkhBvq8YarmU+bISqPYI6jMWLRQ96LA+\nSCNfJpfacG+mKx0G6+cQmt9RhNQXPyWccQ13iaTbZbTKphV6GOsY1aHo5rbK\nfnJHY7Mn7TucoisR5xJ7fOqkE1lkix/cltnE62XaypWlo5CITfgT8ne6fZ6A\nkiNYRirzu8JPGS54vTEIm3LndRg9Ds0/1neJvN+VOFLPXtJt6YlFHRmlYFdS\nJ3x3\r\n=Dlg+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDCH/OZMTvbAvJX60nL53/E1guug80lPM4MrI1OmtbKWAiA9ScLY8w+skBrSusuaVMe0OIZQWIucwRkxKr4jnkJN3g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190831070813_1567235653873_0.5369945117496411"},"_hasShrinkwrap":false},"1.6.2-graphql-code-generator-1.20190902160514":{"name":"@atomist/sdm","version":"1.6.2-graphql-code-generator-1.20190902160514","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-graphql-code-generator-1.20190902141916","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"976ec0553a42db8ba72687289de76a7758f707b2","_id":"@atomist/sdm@1.6.2-graphql-code-generator-1.20190902160514","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-6BZKm5fnfnCPaEvx/LzDimQkCcJCSfHohNvSfLUgUtMQxVVMzYhQwSeK2nSmtrjVGupBc8ZLlxhLSf4A2MXfNw==","shasum":"a0084f7599ed20650e6fce191dfd814b1974522d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-graphql-code-generator-1.20190902160514.tgz","fileCount":992,"unpackedSize":4200751,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbT7eCRA9TVsSAnZWagAAoWkP/ifSYrI/nw3K+PE+0U7L\nadbeLecTGUSw9WXrL9i6fNKDsSzrznQcZ6RZ8tSWSF1fjMqmeG5JCHnraH37\n5tvHPNZVdWz3s+7IKj8Rrkc0jeOCc5ELxv4ZvJLy7+CnRlBVnaccbJgj3Pve\nCHB5v1wULjwDindrv0a3DkG5+iqsWB6hJFl1aySo3MN0Tghy9F/1SMH/Au/0\ntc9N2S2MRC4TltRN+NPDjNgxHqDfTGXnzSNc7RH+4YywVt+Qb52OerVa/9i/\n9iCKw3n48XRyYTfwoziua5ht4qd1COmKEMe6s/r8si+m3Tat3DTIOwssYl6l\nuec1VH5hHG0x/rgCG2QcXQYNzqJIelPGwcOESp7eyhuMrWjriCqgE2eCYqJl\ncB8goQVZvHiIHbSB6dScbKT+DKI4wLDEJCigxYZFObwu/L3dfRMHmeeGYyHB\nK9+we+8HQMn+nXLwlONa9D/6XhtNrMZrLTbd/IXeAXD4Tl95XaW3r8HQP6Fw\nq3KtxH78P17fNo3g7XgT78ARj+Xnce1oh+gmwyev394HCzycEYyZuh9j4CaW\nvGPj6ENNzP5Xk3LOZDaTlm3aWNzNd3ms9nklHRgtWk80jWDyepb2YBZreQ7I\nC1kGP8j0fbuXP+kTr2+dWzSWG7D/Hi7p2EGLPCHpqNyOBxl+xkFG4KKnInGW\ns7z5\r\n=2blD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGNKjMiXLeU/1WzIp0AOlev09aX5Pz6mowhgtuevT8ykAiEA/k9cku2mJLYf9hsaID9w8OeutDsZ7X84DSJ1FRIoWjg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-graphql-code-generator-1.20190902160514_1567440605867_0.9377488895667319"},"_hasShrinkwrap":false},"1.6.2-graphql-code-generator-1.20190903172744":{"name":"@atomist/sdm","version":"1.6.2-graphql-code-generator-1.20190903172744","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-master.20190903132130","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b1258e328a3000285b3a6fbdbb3bcb42aca0b06a","_id":"@atomist/sdm@1.6.2-graphql-code-generator-1.20190903172744","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-1cz37kjcOij+b8Nc9FZwLFsCHrT+qRegLhMqmKZ4yvkhA3801VxqA5ybMfS0k/7flGeutZ3k6QRM1GI9RhUiKQ==","shasum":"99e74d61d2ffb39afd3ce713eb213d465995dfce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-graphql-code-generator-1.20190903172744.tgz","fileCount":992,"unpackedSize":4200748,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbqO2CRA9TVsSAnZWagAAUlIP/0RnXNo0qH2VTQcuWnJx\naNODf6hBZ1e9lJQfxeNi9ARIlbMoG8545ZCfNBKdm1nVHYTgnn+DeuVT2JTk\ng7CDn7OXnfSHvdZ9A7on7e8dVGJFqP5D9+O2uxZyYosWOyaTaGf40DEvopoS\nNmODX/9WWz773GmiaPi1VMWAIHAq2zwk4ld/5tdFj6+vwQmf6lf9b/5XZXTU\nbafJ6sVFth91KR/5krp9T8R1wTFHtjFVDlHafvL62cKJ9drhN4wmc41MoZAs\nzgeNTBFesXaXIzzX4siiXUUWBGrgYmVYG03glIGKMSVCdj7DggLM9y+Y2q3k\nAJESGgLF4joDyf0RA2Qf1wt4BAjjycIGIuHm4vcivy33XO1fO9KRwU+2KF66\nn0pm+8+VkXaovvJXWaw1j54uOEVDdbBESQd55xp2nSItWFhosfl5qt1vhEdz\nYpiT1Wi1qBlc6cR8JwldU2AsxFvvxC4qEhMLXEjxPECjXt8DPPEqS6aPG4M+\na9FkaWf51GOlo6COLwa33SPGPWbJaX00VQPhKMWvrRnPIwXzFEJvTqKOBXLa\nXM1SN1fh6ckcS33RS1hTDXpoZzm2Ojjk6GYudQI8fCeYWN3punwo/4K3lovF\nbbt6X79uFZR63HtnopMCXwPHfjp6Y+zkxtvcr7unRy8nFUZqeb3PPvuPMxKE\n+u8F\r\n=Hwyx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoikb2ZDEaoeWJl8NwTCDizwLBhv7Twc5TVt8K/U0BHgIgGUwJpLmfRepWn9DMXhGDqIh9RwnZ4f3KpU43iIWog2s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-graphql-code-generator-1.20190903172744_1567531957923_0.5622058245996355"},"_hasShrinkwrap":false},"1.6.2-master.20190903184405":{"name":"@atomist/sdm","version":"1.6.2-master.20190903184405","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-master.20190903132130","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4374875181645a475fd552070cf454f0db58d9cb","_id":"@atomist/sdm@1.6.2-master.20190903184405","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-KKoF6UWZZTuLWPK7sgU6IT1gosF5VJn9pQe+rZq7GtV9d58DmlZpQ1yPvMHTUt+29ac++vHVaXm8/seDQuK0JQ==","shasum":"7f1d32cbf63d9d31912ab18f618fa3d1eba6046f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190903184405.tgz","fileCount":992,"unpackedSize":4180966,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbrXeCRA9TVsSAnZWagAAGooP/jJ4+BJA5s9yAt4HlIN5\nKFvrsEsAvryPus7e9HoK7n2GUdAUWtrpaMPUINOsnO0K6uUUcneG7iGyCOKs\nqBrdsjXGnbcFrciUyaHa5H3agiT1HPt/mTjX9tEjDvm5fZKijVhhnDR4w6uO\nr6NZ0iP4w/VhsT8AgJFFEA4IoAKjF3kwJBdONfK0XOFt/TBvMMrrsLcXe4JH\nrUh4Jv/tim+93jvGZsNKGhkiSqRGw2QUPmzXXjJhjx6bEQ043t47Ov3YizIq\nGmtF+B4QVlEaPXXqg7E+PP5pQw+Jk0nS3zVtCMCijqZp/YFDCCUuqGZczKvD\nl45QR16eXYYyB3G+/l45XOIK7J0gX+L+9DnVDnJHr7DEo3EoZ+E+7ij6czEf\nUHbUe/KA8O1CyaJR1cZzSgAJTOh7W8+HE9gvwGLocJLddXMSHLppxqQCzGzg\nBJU+PPa7llQAYuXXwGSa0g37VXXqYs4WabAaTE0rgyYMeyOlDdc9Wfk7BG6Y\npS2OrGA2kKOnvefRdfYSKB8fubbtICXJcwkx7KxqwTWlPwtRXJJOR+OF+8Sa\nZ0ZSvABZdx0fSPAyuxU7d+ivOtZSZ8zg/qdynCTqy5KD3+KjHL6GO4aL9ehf\nvzR+9PHeMn7JNmsZP+m1MKVl0xbCufEYj4t5x3W54Ko8RlqgDMIrLvKe0Ht3\n5Sn3\r\n=v3RD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQClYzpioJPC5FXYhP3mRv/6/VVJUxy2BiSFVjwUrL0urAIgad4/8DVVT0pQIVNYhZdiXUa+YaXysMq+q/kEQNBLA7Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190903184405_1567536605589_0.6085204212017128"},"_hasShrinkwrap":false},"1.6.2-master.20190903184656":{"name":"@atomist/sdm","version":"1.6.2-master.20190903184656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.132","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.14","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-master.20190903132130","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"3.5.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4374875181645a475fd552070cf454f0db58d9cb","_id":"@atomist/sdm@1.6.2-master.20190903184656","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-0tdGSp6Fvft0Iw04MYAfUdu60TwTTsWI9RAjYm4yeGSAjHDxyAwetRseiiHPiJ/1pnEItrDrFWqxWBStAFrIyA==","shasum":"0b10559d8420fd40f732bcbe4f567fa2a3214c07","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190903184656.tgz","fileCount":992,"unpackedSize":4180966,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdbrZVCRA9TVsSAnZWagAA8HwP/jI4cIuB5HzR2aRuABhk\n9btLaMt5jpu2/W3sgp07WvMEAV8B8HvSYJk2nKF2QQ4QjiFxwDk/nR/Kd/Iv\nwhaKAZBqVjwrCgkmCSswKAgzFViNSABgZf7H0+4aISFF/i58qXG3tKRI24/r\nWM0mWPIrZgUZDCOETH+wtjj8BWhi5yZNE1q6n8O77z3nwnCAGjjJCtXheT8T\nETf3Al8D5s+hULos0bc8AtY6Y8noK6H9Yq4GFea7a1kVLcvW/XlgItCKSo0J\nnaxg8JbHRmrhaVL/0QMeQ1JODYHAZuYEZkMraMnuWG2simlMFAUtDrTJQ2IC\nE9yAFGuBHG2+YphA+AXG048DoBYLY/Z+Ad5BSQdvxKyKZr+L+O9qxxLWV+rS\nto5S2nY/nIH0QguiThwKCgtdht107bUytlwIFcK5fAgGbmnF8sppCutCmIDc\ntKQyQTYkZ0dvwj323in+XibeNmbLiGvC52ooHxsgfJXIIxuCJV9qS84JxbBO\nFwCEyBkyBjjUT/8s9tWCrJ+1xr0DmGX9G619n86hKNGQnT5ClTdZ3HlrWHNu\ncsrx919ABv9GJpd1jzCasHtdQbKFjlpn2TgkWYeo2cPE5uvVZtoha7O0zYb+\nQNjKP77HfAgWYg2Foka97sIgZwx5TSJr2//UFVum18PoJEWzUQhUeiJtCoBe\nhoSh\r\n=0qs3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYDiFCiuBfF5w4v4xc8mrRNYdv7CaF5c+qG+t7A2P8WQIgCuoqXdB+k5Ecut6LwGKDcYKTxBvDlvuroruZknp1jnA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190903184656_1567536724463_0.31851372979845616"},"_hasShrinkwrap":false},"1.6.2-master.20190905161334":{"name":"@atomist/sdm","version":"1.6.2-master.20190905161334","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-master.20190905142444","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bf881fdc2fcf7816d68da7ca93c11bed437bba5c","_id":"@atomist/sdm@1.6.2-master.20190905161334","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-35v2cCXly/aBwEVEy5nNU3CG2Rht+6Brzas3nDhftzVnxLFmWeJ1eKj6k7BcUgEitaF7joqwI5wMtSbPBo00PA==","shasum":"1343e9bf30684ba2a6c67f5bfbbd049afbc90ef4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.6.2-master.20190905161334.tgz","fileCount":992,"unpackedSize":4190470,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdcTVWCRA9TVsSAnZWagAAL/QP/39LhQNt8Dv/lJ30dIKG\nYU0wuC6jfx++C9UEE4WgxoIz5k4elpVHMYHA9O/hh7G5KxOURge7dyr+4ipT\nTQyyj5pfol1rDZxN1V7kaeWMnM0K4YPAaJJDVhPhGkQe62fQT4IhwtvAEVKN\nT6hzt1Y4/BB6iuZtUYRAsPKgUDJNGKBwFHs9zxnKqZRU1CMScvU6D9iFnALR\nXRNqL6EkJLqrniBg1sEps4KNFLFtLfTkmTrmejXSr08+JjJqTm0anMuGS3IQ\nMDwzKkdBneqrUI3vKvIDLwt2xZD+eVdtoLrTWNbOIzXzlAJYFL3rhoexaruk\nl/86kr45F63YClD+b4I0Eea+hKg7PxzGbQECXuGaJKP96UBe2igOhZtDF2sj\n9sPLHG1DwvbZiUZIHG+gzRuSCxQCMTiobUIMlC2eiGq7i00+tEIy6wB+GLDQ\nmkFbKx6dDmrc+ovk69o/KI54i/L4MsZqiprnRlOLbBpW9KE4pjBh92ZtCHQW\nljK8oCH7+n9hruu9lTrepyXj+av4X0df7nLVPibkO7tngd3dBqLCEJIq+2ut\ngdCgx8/JWpRO1Ey+05Mpt1KTcVZDxuELPUd/gZpF7qW82mZfkSK2QH3y3AGT\nVQwDmXSEKvNOPVLgKcWCi74RGPg1HImoVYeOqdntTPdjn6MasuGoa03xG96l\nzFU3\r\n=RUhd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDItMKGv8J7oazyPGrpsMBQul5p/M5V4TIwTr8TVeGRMAIgS87kiBxAxXijjvY61FMianOtDC5NNQm+SKaTn/dmxR8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.6.2-master.20190905161334_1567700309637_0.08174786143100587"},"_hasShrinkwrap":false},"1.7.0-master.20190906124024":{"name":"@atomist/sdm","version":"1.7.0-master.20190906124024","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.6.2","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.6.3-master.20190905142444","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a18b05538e9e3839377092c267cbdacd988ffa68","_id":"@atomist/sdm@1.7.0-master.20190906124024","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-KAWEZSt+jmtvpJBZIBU/XbM2LClCIb5Q73RhRDFMwcIWSHGVQy5qqSwLhh56j01W8Rxb5N1MxJiAzwbfxO6DNw==","shasum":"2a7d105dd850bea8b1f9997375cc20f54e59975f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.0-master.20190906124024.tgz","fileCount":992,"unpackedSize":4190470,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdclV8CRA9TVsSAnZWagAA2KsP/1TyNP+j9eIrf0PZoWca\nGxBOhtvJ6mTcwLAWoUl7LoqiN/SL2XnrjbYzq90SCxNezCbea2BetUDmm+hw\n7eKyBvTO80WKHx/dIRkpQnySv9x6+wJTybdCYkmiL8YnoKPAjbZJGvuk3zyL\nYJWyjLmKwU+MWk41/dZzdgP7DfMCqsEoZbFsd1EomEFfSvUM+hy1TLsuaISc\nXkAvDq1UuIMnSxqCOLVyydTGVljRE0fP8F9uu99nyxlDJmFoLyWTyF2g8xsF\nYcg8eAhUAH6q/tgJuDBquEJFQbZB+PqNGlTidGwmvnHXGaK6AGNs1PwDB2DH\nGir6v0qu0DpBKo5TYG23a3gVknEe5mn9hI2CA74SPrEzyVdIrAUo3yvRb/Hl\nz2xVpHg3nkOWSzswN9l5TiXOmZwgpFq9ciwzZgV9y0vsRnWJgzbP5NMZm24R\nEYQU1GIOx2kWBPGYKvxJo86lWqlg2F6BvVoQPzKJgOZ5wObwXQ2y4rQVqSp8\nnuCNHBR1owVOchb2LSIeyZBLzjpyrRmtaRyqKuyteeeWfBQghqAHJjvB/nYH\n8nXyBjND+ei6Fdk6/aJoDNGttusa3owtMWJJWNjjQiWF5ipb3hqQ8vPMCzyY\ni+gLqxh0UkhvZ/4M2lqYK7TcdqVVJZT/LszScUB39/Lkgb7zpcubFtx2OhBy\nENxj\r\n=kh+I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCAwSavLIm9MTaG4bR1DyB7CtINW/Fc1cMNmoXmahPZJQIhAJc0QyysVjdYOWNEQXJ3MZqW5KahWb2E0SC49w7S2PQe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.0-master.20190906124024_1567774075860_0.529959984990175"},"_hasShrinkwrap":false},"1.7.0-master.20190906125625":{"name":"@atomist/sdm","version":"1.7.0-master.20190906125625","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.7.0-master.20190906123829","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3bd50091d0f22d2a54824f3e635439a701ee550f","_id":"@atomist/sdm@1.7.0-master.20190906125625","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-DPTHDcetCpyHzyYXraYMYus0cZ6Ne7j7frDQ58XR9nYE52PpXBfiuDDvz7xtB9XyVvY69nAml155RKp+z6qmiw==","shasum":"a75002bbac51de2b839628ae530448bb1a23453d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.0-master.20190906125625.tgz","fileCount":992,"unpackedSize":4190469,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdclhcCRA9TVsSAnZWagAAteEP+we6zjpAj7hT12MDD9UF\ntsLjSaR7ggAdte5U68H/PQHaNfVYktSsyGoqkUG4CsQzp5hJfkVp0naSHq00\nOZdYRocZCbQCgc5Wd5SoKlDDFBvgwhwuScA9svphI4Ua58OpW2zm62YEUtg/\n0A94PaNZI2LeTihoXS55zbnrFtL/bq4KvCBH/YFL4+I8uoXg387BtAyQ1pix\nCMAIIW037iwwAkysHYd4QsxtvvwZ8V7ERugT2nyMxQ3HQ6sfsS0k+vKXOERk\nUIcZ4eJVdO3lweunnwI8quMrmuU/0wWwR05/p9BRB07G1xTdrGko67dXobdG\ntXj+VC4JewnkcrtfEYGKbvC7aQLea34r0JciJ23LYo5wvPpjfC+XnjNutdaI\nz6Nh6/KDkkVavGK5zfX6asrvFd+lUZy+eC+EqwuArgYDzhuLGpcCl4joV2xc\nG5RlJXNmTyKzTiZ967wLHtnJ/TvtRl2cwkWUNJRuQ+/hSB5SKQ+NpsG3g3TC\nvp0n/x8QppLnb+vrq9mo01pXIPicg0OzlXPwuUDXw/RjfV4fe0mgVB4gAs+Z\nU6N0UfCtlinkfLNch96R37F4pr7ZRqGGMiCOK2eq7i0t3ImKUzaPygef/p6i\nC1+gV/6uqgNZrpLV4Qxqte+ZhYN3j69hMcydQ1sXDeksFrcxvD+WcsDTCWWj\nbMkm\r\n=Uizm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDS24PkaC0hqwn0dYgCUhcSYy4/87VyLBVjzGVGzZTuGgIhAL+sjhD5he5x8BdDRPSvlZBb+gXNZek1TKTWYToJrU14"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.0-master.20190906125625_1567774812038_0.02284306867204533"},"_hasShrinkwrap":false},"1.7.0-master.20190909163442":{"name":"@atomist/sdm","version":"1.7.0-master.20190909163442","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d02353589113aefd4f0a9a5e18ad4f3435b506ea","_id":"@atomist/sdm@1.7.0-master.20190909163442","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-JVwHX06NFrXnkb1JjKF7CgPcK2EWZirDlAUBWNrOlrvTgevkpGFZlRAw9/UXeSLL66HlohuZrDW7AEowUDCGyw==","shasum":"4591d0afd0f89b3cef4d32f4d8f367c9ccb38a88","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.0-master.20190909163442.tgz","fileCount":992,"unpackedSize":4190791,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJddoBbCRA9TVsSAnZWagAAR7wP/0FeNL5VS7bF/JqUHyBN\n7pXv8zNmsSY5OSOv3OJxu8b9nJqpjyKDPHUz40sVDu9UVITSyIwOr4kLGHrh\nm2y67UTzkyqn/R2nj6DJv3IQ0NSsvZVZc8+ak+VzySHZMvkvLnKJlqiM1uj6\n7EiUynBCnhG5sGN2pbOIj6CSMcScCgDJtzBiaCGTOeW+WDUR4nmM1l0m3vZ5\nOeJ7j6OXgcUvWVvRGGWai84IYCLzZt9GbQpwGGKlvTW8rtOiQmKqy1+3UKcf\n2TX77uz1JpDZnj/1hkEy1kR3puygYGRNa0Yrk3NiIXZosj9zwb/ka10nca6n\nhCh+CNcltAHUststsdZrMYEsPAgY6G1QcOLmU3YaNt488qcCKl3oMM5Sf2dl\nsXshhTEZAluDy3AcJ9KTLnKisjzb1udTizpReZY2nU+1SGypHQD6wIU8coTT\n1efNF3lrBGRoMNKLkCHHhfFZd1dYrdeO65MbIb4yw5aMmtUeYaaBX98xI3d6\ns8rTSMMb+7/jtei9V7clb93kkkVx9R7Yi6D8LcjAomwV6MvZ408VZT9NmTB+\nfHQh9kQ9xcauaYeyCiM35PLnblDvVjHOu54OwgiWdHTnyKE+emwmRONP8tNv\n3KNX4LNbd4OZdFSTLhwJYDoRdMosYrBgolvQvRTirhJ53I/lkwpchiB1yXV0\nMqf/\r\n=j8wq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDCITMX0U6++XmQrlPg70CBIhKZrkrb2MlxtKrJaNOWoAIhAKWS7cQhDpzSR/FIBxe7nl97MbpLkks+6Drw5T+AThVd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.0-master.20190909163442_1568047194184_0.22382134047213165"},"_hasShrinkwrap":false},"1.7.0":{"name":"@atomist/sdm","version":"1.7.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"gitHead":"db67b41a51fe6dfd0f20f0ce57b1486541aa0ee6","_id":"@atomist/sdm@1.7.0","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-TDZw8Sbkj0mjxWhBX8sntW/Foh0eb+wPwUTToLKdchfQjCdnLaCbQGM2nRhBEOWJcPDktnGvPkHS6Cz41G7Omg==","shasum":"6ecba65b50ca1c635353480286b6e77532f587ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.0.tgz","fileCount":992,"unpackedSize":4190769,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJddpNnCRA9TVsSAnZWagAABGoP/1GDTYgyytvbxn8jEmkt\nQdd+KzYURKz0nYGCGdw+XDkECWADaEHc3mky7QG/FqIfTUFZbsSB50LUx4Zj\nR+z+E5fKKtwZOuddGlcrB8MFGpGTS0K9Xlaig32txNDKn7E180Bi7nFSft8U\nxHltrBEwDwOYs4nmnwIJXYsMd6kxgHPuyJkKkb/IvYYyJmLhVrWSjRO36Dn7\n1XmKM3Eg1M6JOGPGNvoMXBef8yIfkfhEGjDmwlL25UabJVrRLxro9xiIP35H\nf7NO45NFGpxHSdbYrlvPA1brly4DV2KqWZR+LdFgGu5nLwpbP0Z8P3bHsgCi\nsygpO26CRBMEc5Iq/pfecTv1a8LzFOPFbsgOSnHiiBgFgwP6ZEQ0R+uANKe/\nQYnpTZNSSi9e57KFUlGiZhsdTfBA8SK85N+MEU30sHLGp0axDHAGbMG07wE1\nhaZfdZUJvuf9XsfF5oQJC8a63Qy5Ncx02EIQkJFrxEJbB+ZGKnCV3G+TTvy4\n84KiynauXKx5mpPL9D9B3bgjZ6nHwiyA3Fmqhp68xSr8IigNATrLtmEnvn5B\n1lXxT6n7gnoR0A+gHz+O26VO8CLom3RkrkIG/DV/nWuYRNuk4wvTY8mOoIeM\ngBEKUUeR71keyDO6LW33yxBCODDTQxdm6uH5HF0wN05NIL1WJc94BO3a5imd\n5Hib\r\n=j/GY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDd1CwzaLKQONHUxePd1EZtb+jwgL+hvJpyhZpmwzcrRwIgPUJLO32stVv2Rfz6UCfNM0Im3fmefYpPBJPezHcEECM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.0_1568052070583_0.31470678290526544"},"_hasShrinkwrap":false},"1.7.1-autofix-log.20190912175507":{"name":"@atomist/sdm","version":"1.7.1-autofix-log.20190912175507","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cfa31948764ef762c5cceacbd5e8f4abed11e269","_id":"@atomist/sdm@1.7.1-autofix-log.20190912175507","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tT+SbJuhEPxJrykJcroLI4t6ji6/Et+gqa97Ufkefy71vyGIY5twUjTzVb1leemMiU5UTGVl0WoaMIO1iRt4LA==","shasum":"fe37bf8fa1bc070b7c2c78c9b780fe91addc56af","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-autofix-log.20190912175507.tgz","fileCount":992,"unpackedSize":4189071,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdeofsCRA9TVsSAnZWagAAabYQAIhQ3h5AW+DXni6NmSNZ\nAdkeRENSdpb3909zzVE/H2eUfMpwNBxACZHXo2yKAmac9gLN9jtdyv2pQMBa\npOb6oBsIl5tS9/7A8SyAdRFzof7hWZT/S8CRwfMThKibEE9It5oac4MGzmTv\nKVHyOBxGUYe9vkehKBXF2M5sUio8nHZqWtitySDPsGcyvxK0p2jmffUhdd73\nkfhQPCnli5W/BsOnVEgy9ZlKlKAI3tVx2DZPddNyVc7iSj2cQs5cwAho5TN9\nC6C9f7RVyh7X8LYfyOobun7Ws3lmre/r+I3M/563s/0C+kM+05l6RKa0SPMi\nByX9ni0bBojZ0Wu2E85DlmcDSHoBTJ7GhIf/JchTXP4/7nkWfs8/ye6c0u6l\n4hFisdzpQPQst/biO+lnW5O467oRG8JzdJsfRYj10L5uXdHmYRNe2x8HVOkf\nRFXCOcJzFKksZiek21C7Bt0fPS3q1q3NIh/RX7BLkrA/xWjwYJGYViNsKxkJ\nyW97KflVcjiLOyKFPzkq/w+R4+nW3pI8fy96rSiGOcjBmdtaXBdZMa7xtdPH\nMs2muIWdiwLzbg8aSEaVbORW5Lg/gCzFswHsIa2ViYUeSejygtYcfOJB8zPK\naZxobbt4k6RB2/PQ0pKnqfNXRv7uEYeuGnQY9TCvCHa2WBuNXPq40meQJ7rz\nD9xZ\r\n=PlDe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHT5qjpns2VMNwMSa5tGV1pBQrqT34VhiIpmyOQJN0iWAiEA9gZT2GytIhz2zze2zJ2W/CAZU2YmgS16AvrgncEen+g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-autofix-log.20190912175507_1568311274972_0.672220319284554"},"_hasShrinkwrap":false},"1.7.1-autofix-log.20190912180333":{"name":"@atomist/sdm","version":"1.7.1-autofix-log.20190912180333","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f5d0ee0be817ca8806942fd21156b5a78c174bd","_id":"@atomist/sdm@1.7.1-autofix-log.20190912180333","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-jMjvchfAOcJECXTo35hOZir8N+JjN3N/wCyVd94te+Ydqxr2ye2tdFrAuDQ7HTqNMtbfB1qBNtDMezBWbJkwxw==","shasum":"4f8bfc3827f494f2a019b2dc50c45be1923c9ae5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-autofix-log.20190912180333.tgz","fileCount":992,"unpackedSize":4189071,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdeoobCRA9TVsSAnZWagAAfngQAIHx+m3jph3kMLn1QS0l\nEL2/1sEDDVKmBj3dem9GvJEXdiDVwAWE0fd6v02my9zkY5LDnVYFOzNQ5RzF\nd7CmP9qk9318A1kHPb71+6HOPCCHGoSbC7zt/m7iBXM9/q9BByEOHkvqy0Fk\njZJQEyjrShYwMmU+aonouDSCqR8VKRiZaNKNNytk5aIHDgDXe+X6m2fQvxSB\nbXwr4KZ2SYBIQpyT8CEjjmlVG2GNftkZUzx7PKhtpt0M908fUG9Y3RsJVP8X\nOmLxVlPxfUbl/elPAfGk37iGbGJNuLXFCPGFdpSCQBSA3icVuVbz/OSygreT\nxFyNQkQznP5MB+tbI7y+CSxJo3ZA4QzyjG9Zbs1IkXq+V2igJtarcKPzQKY5\ngk/8oprHym1rQz9uZFEJNhEX+a/OV0SO3ZxB22NWDy7RKn3pwvYUxtMxJXX1\nC9j8D8PMbvrBgJkn+YmCq1aZ89cVPaJ4wB/URR1C78VKvAGqO214YPTRBsMn\nlkeJYdPv5eUEroaKTk4phXZZCDdMJEowG0RnISwXg9+W05v95OpO3FJycEk0\nPVjjOutggyQ9cJ3iEhkfICRtXzRzRIeVtv8YDoHZm+GkB2vFTxiNYfyTnnYY\nXXxKHnX0oPfKjV7Ytt5zw+otSs5BRxWR6aWodSWkFp2eW1lD0aDRwefTvw5m\ndbhS\r\n=4zOT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBw1n5itIznWvdmUmxqE90h1bKiFJTG4TG5SEiqt7TTZAiEAr8cPVMGuOfSa5s3ojN9zvHmS4S7hjwbYxLLlcjT4Wr0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-autofix-log.20190912180333_1568311834560_0.04970858340642326"},"_hasShrinkwrap":false},"1.7.1-master.20190912221843":{"name":"@atomist/sdm","version":"1.7.1-master.20190912221843","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b1999e85b1de9fb6670f63d1f7849c2e04e64d08","_id":"@atomist/sdm@1.7.1-master.20190912221843","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-NDuBRabvnsAXBo4VwOasGUhibwlfu5865TNdUhALr66muIf8Bw/daPNsJH2hzxRvql+tkVfrwAD/Z658wGyN8w==","shasum":"57e323e6d01ea88d78759672bd87f874f0477b02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190912221843.tgz","fileCount":992,"unpackedSize":4189174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdesWmCRA9TVsSAnZWagAA/lkP/10SZq58Rlv3z5rLlCz0\naf/NP0y4fq4+/W158r12spV+aiXK04JORz9vTFFc8hcS5FgiM+PJ4Q4h8N3j\nN6AuA834F6t9IqXSa+acKNNeE7QH9A+M9CdH0Whsjao7NytysftXFpOtnuK/\nPI7NfQiUsJv0+rBTtoCDXFUNZ4mVpL++s0mQWeH/LCnkJqZaQdUlJOIypkbe\n0TDk2L3TYz6mj4lShzgn527NHNXG0fG2lnZeWblMVOKf+dfpS63OhuDfOp3+\nw7MVCCkbkjF75SOYdtmcheisRgLaU5F1fdN0amICPdqBwcmjKsg1aN3lvaxB\nyPEMSN4oGrP8SjmWiQJ7fBFRnN1RY/bs5LPTLrSo64z2U2O9mE35wFdwGszE\nV8eIkb7zYiCnTeOy1pX8lLqqLKHLFvaNDAqOx1vHgjNWwYNpg4FcZvqwV0KS\nMhbTtZE0STSl49hjOgP1g6te1R1JE5QBtRByVPDm3WjoUVc5jWP3vIaXxXpB\nsMMTGjkmnUkYgflgiiyRXXYOjKokhwMQdUdWXDjLpcFfXVS5rnoZPwNLgRpO\nTJWpBro6FuhVs3cBcC7U+SGLiAolb0rkrf2FkapqcgPkL6Oaf7XIcTqfQUrP\njS61OUg0TvViPN1/sc3XDclUjtyTQwD86RTdiUcwandblWeGrQk6kUIfzoVC\nrviz\r\n=UA2r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBahX7Y7I+FkrrScuGwhqLcAlX/uhwPdt5Ell5QegopKAiEAwrYlW3gwteIAlvJHaSy4+ydEy3pC6JMzkopYJdE90PI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190912221843_1568327077274_0.28278740333352026"},"_hasShrinkwrap":false},"1.7.1-less-verbose.20190915011836":{"name":"@atomist/sdm","version":"1.7.1-less-verbose.20190915011836","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b083564ab07fa884327e275a1f3e204f02330e73","_id":"@atomist/sdm@1.7.1-less-verbose.20190915011836","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tjhEdo7VGoyU0o/TwBaHpnR82ME3PBMnOLxEDEzq0f6fwNyBWej6I4W3s6VkzRDo0TnpXK6yMctr8xfCbw2F9w==","shasum":"a6b2f86dd502e498eebb09fc4c459f4674aa1536","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-less-verbose.20190915011836.tgz","fileCount":992,"unpackedSize":4191296,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdfZJvCRA9TVsSAnZWagAALFAP/1QZxWGQpIjz/JOAyjE3\nyPlGCO7KbKOpd42LMZxTuOywD59KhBP1vKyt+XsSJNKvMMXPSjUY4yYY1PNY\nQXnwgZN+bD3M53DdOCJA+Kzi18jnS/7EoqvzBa0RWstb3AGL9EUjL4dQBrMY\no46ELe+I2QlCX/6uiFTg79BuVZR9mTXyM/lKUwTcfpI69KO2Dc5dKsrRt2EN\nUDsWfe2TQ8bRB0UPHipOVzZUr3ivesIraAUSv5HkHZm+W+b935Vpc1XY3y0O\nWeIV9gtkpX7mlF3MJ0RcfZIC0klSp592Qqu4B9ETnv3ub92aOr5iMolwv4PX\nmN8zcxD+ffevNjyMM/HjIfVQlmM9+WiLhDvjEr5z1ZUGiGrB8HY7TpstK1aL\nXPYLYlpJcd0OvqhBazirmbF0emuqzH7BTxP7aKen9BaQlSX2k1kHp/35K6xt\nFnd7GPbxmpM9wXj5O4+HuV+cm3j69Uky4dLB3+SamnLVmUO3dOXDlAt3wPng\n8Ub3QsJrZOcv3+hBfI8zNAk5T4toewF9T1aodp5VJi7bco0W2P64nzj2iM3V\nuigQU5BBLRqYHZ+FTgCkyyFNrPIrXupvd36j1vruwWSGIOUZlh7Til1jTAZ7\na77qbeuhu09QSsSftvNN3fmAz5AdAssDYWT8EtIFByLBH/x7vTVIJ08kXsou\n2Eh5\r\n=Z7zf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEB+XSYIulc47C8qsyggqJzzhVVv+6UBfrU3ZMYxptwOAiAj6P2j6lJ/pqd7JM3nWd5G4qQ8Ymc6/P8G7CFz/rMTGA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-less-verbose.20190915011836_1568510574608_0.8257977841405126"},"_hasShrinkwrap":false},"1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245":{"name":"@atomist/sdm","version":"1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ee4e2b3519ef19808f8b861de1b95084fde01abf","_id":"@atomist/sdm@1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-4PJJ/ft4Q3bB3EcJ3CZ3f5FYIPoy2RvN5xBz03rGez5FymuEgjwHEMwCexA1tgUPiMCgnotv1oAFA6Hl/KwDrQ==","shasum":"5eea0e0add9809567061fab253d78309b7c6bd02","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245.tgz","fileCount":992,"unpackedSize":4189211,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdfqy6CRA9TVsSAnZWagAAcVwP/0WVOuxI2WAiilZDgxg6\nldzki8TAnQVPtZP/lJiouP2GlXw0W57223buwYRAZjUvvUHZd99jq7foE3VS\nXOHQraxN8RO8dd/8WXYVolI1CT32pG+9WhaT17lb6pgJ2ValaFGCTYm/wsA3\nfYdmw/krdUvHFSZEPoWTcMwEkguQD7mzpWTEo1q0stiNoh8OvnR0hWrvZiWS\nK9lAK6sICa/a8bThop1M4zgkDoRcMpg+0tAvlBgDMmcAy4FKdsaFdUe0g+fS\nNzT3b7w+lvo3ClkIKJER6YD92XCiNDbdUTw+z1LalQST0XCGtkupEg6WIFLB\nhvP+85Z3Qg5NZ8fAkFfPTBgtuqF4UulDlQ5GEdHiY9sJUm+FYyLeVrgdm04w\nV1FFqQvI6mGlIXl6OmKobvQGUmXERxblVlIXaaGinVdUDKbUow2WiBoadjHH\nIZjl6ZLRX9mXC0+IrBzgOP6+EXMindG2whm/d38ZvEj3ml8VTRggcHu1rRnn\n83KwzF1anD5INxYRmN5lOfweyuqm9e3G0Sd5ej0RiV3qSYh9jf3sI+ZUITPu\nEOAxe2QTIHXNdlmIageF1fsDT14uvTE7tvBHSNcJexxDLi1QkTCN1v5vwLCc\nA4g0r302hOP/jdWuOejDA/S/Qi4fe89DH3LmNfkjp5Nocv3VOIfcL9X1wM6D\ncPUO\r\n=2/uu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDj4NF5QgSHPwDTaGQ5l5Ew8FWXqWlFKVY2so1nNvgmBAiEA+PrLwaApaqiu56KwJpj0UcaZthd0GTxP8X9OB2bTDlo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245_1568582842073_0.6021617294489203"},"_hasShrinkwrap":false},"1.7.1-master.20190915225602":{"name":"@atomist/sdm","version":"1.7.1-master.20190915225602","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1c4b2c6a2e74c2a0707bb66847192b58259ec1e6","_id":"@atomist/sdm@1.7.1-master.20190915225602","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-biYlV2CJciw/z4gj54RtGrejheQrg/+l4cOr5+horrx8HQn3Egrov1HnnBth7seP4g/kTGkr5Ju4enP09wcpUQ==","shasum":"e4f4eab96a769d9cb6284011fbface26ac42223c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190915225602.tgz","fileCount":992,"unpackedSize":4189174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdfsKTCRA9TVsSAnZWagAA4SQP/0pKTblElbRd1TkcJt6J\nD8XgCCfDv88ASpU0zWOs2UcfrWben6/CeAfOX8aiy2FXQq2wf1H+NzCLp481\nfNuG9gdMI4PqFBCEE0HtyfKLCSKFpvnJ4zAiHQu6GkW4jZOwkyrett7fgxEQ\nK89Vrq5KRneE7nvTkFHpLJMqEM4XT4MVTzeGl7sHwb1WtAazsgGlSRxYcwk2\nqL4qvVNAPObJCW5mh+iOE0OH870pI2MyIZjUSqutXAugU+ZcEgAMBW0rU0Zo\n+9W/t1Bif7M1YhrOYcqEMvAUka6pGt3WQHHX3dw0zAoTdJnKzGuR896qDvAI\n6LVKmPhUb6YmhU+kUPJp/ap1GOdrxJckfs+s8a0dbaK25PBzANloQ95eXLTe\n0QJ7UX8D3qOCNpBrCnyNDviYFdypuZJy9QRFOfs4n6qAMDUT3Kdd+inQrS9S\nGXMmr0TlFGUbVnQIIMh76VXDJD4r2E+8qmjSOdwU4/UQ8bd5vKHHMzfxoP/S\nRUPRwU1VxP5x5uDgJt8zmHmH6hVUcVZYoQMSXxj/ZGmcQqbQ7T5FPF4bsm80\n9U85Ieg75Se05D2SqsTaFLTWzh1uKWXr0jONFkBfE/xZMVMDeWHws9hMGy6c\nuAUWHLFnnUcOgEDvRqXW7ra/Ha4rX8iuT1wTDwbXcI68K2JxZYYSkgQFgvON\nEZiH\r\n=dT84\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFUocEo/KxvK2q+tZahmb3ACHPBZLnksbtCrhgGhvhPeAiAqeDeVCF9ABZ6fGIzXENe68JqdxdYy5dh7kLyUVypiwg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190915225602_1568588434295_0.4716256020122329"},"_hasShrinkwrap":false},"1.7.1-master.20190920014925":{"name":"@atomist/sdm","version":"1.7.1-master.20190920014925","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd42c8d7f75100b5fc7caed15960aa229dd02cf7","_id":"@atomist/sdm@1.7.1-master.20190920014925","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-A+rF5IRWfmJzm3UDs7LvazuNz4R8CHj+LoBtLQrxxlwtA3akv0NuH/oca6Eb1gQ/e9Pdq6QwSQnSszjqgabkGw==","shasum":"0ff0db7b5edce8d33b3d42bfe21cfa3602fc356a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190920014925.tgz","fileCount":1230,"unpackedSize":4866965,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdhDFACRA9TVsSAnZWagAAgWYP/2eGk1RsALIGqqpeqzuA\nVyVahU/5XFB6AnBU3J5b8yQbync7YSKTW6waEhENt2ipkxeY/586cy0EO0LA\n2IeTzoX18nlFk2etUoJ1GppP5rVTEyalh83FAhGgLOV2F0pR8dU/zaiRiyRu\nzY5J5DU1syBb1iZze01nGsVY6fwZzzyF49BXg8WrSKiw5syivtRk3fNn0Tzg\nE42TURh0Ifz62+vrH6xVJSayUsQh7wDFSrG9ycRM3CFDOBfbGhDB/2p9DMjt\ndkZIHmaCPhYVWBQbkd3joNN+x78CJZFwDhjqEOgKRFQWHbbiaLAAiyjyYWT6\nSqq5/qubJT2/9k91gZgrG72n5vjxcrmTGy8379Ps9o2TRPAS3vLU/OByZOm5\nlh/GiKPto6U0pvWZHAodzzlPy/THT24L1d/aZRaH2v2RquVw9aS/mIIXabdn\n2HVaxcUOLmoT/oiXac7ZrF/IFUjwCZp8CRYVR2qwatYAVKnNDHfqhQiI+dpL\nlqchqRb+W278JdzLtXWxVqTrx29T3zoVKLejZR/C52tDN/8owzP9C2hnYyNH\nPx3JZiTpvbKkHv+V3nEXi2VQb4ASPm2cDc0y6q7rA5V4xco1ZOrhAtKkzbGL\n3oijPqyivgptu8lanG5rJ8pUBvJdXBZy1OoXK3vyCLOJCYUcPqVrCidL7ZIy\nXLnl\r\n=ujUN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGLGvZH7EDZnjcExPA8TQl5fH4d3OvxtI1s2AAf1HI1UAiBxIub7a7Ked1ZzQtYke954wMY7Z/mFBPsWc5WV+mHc/g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190920014925_1568944448114_0.7723575206912972"},"_hasShrinkwrap":false},"1.7.1-less-verbose.20190922200723":{"name":"@atomist/sdm","version":"1.7.1-less-verbose.20190922200723","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"18be78f08334cbdfa8fa613351a8679ee4a4bef0","_id":"@atomist/sdm@1.7.1-less-verbose.20190922200723","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-lfdtS/Kzvxl2I8jLPxI9hJJ6h+6pEE7SXntgjkp9NFCG/kxu1lSgIxXZvZQ0x/chyo/BIFXWAHHrFy83YGjXRw==","shasum":"4ffbc16ec4c5346d6eb82e555c9cdf5d13f583ca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-less-verbose.20190922200723.tgz","fileCount":992,"unpackedSize":4207530,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdh9UoCRA9TVsSAnZWagAAKfsP/2phmB0/hOyQ0NzNmRee\nFE6W+Q/wci+o6oLfW70W94fHHfCe0eCvChLbE3yRp1GJYpHheiisgZOqRIFo\nxaGh7NrLM6OfUujeNfW3un3kS+rWJAS8qd2ZRZX45A5l1/dL3bbFr2CjHEE8\nG+8BuwjWnbHUx0td9QaTdd9TecOZp9qQ/6my+xbaF83oVM/Uu1myKwAF4F/v\nsjPnHHrW2nenu96f0hwyZ/bDQ7ZorzAmzk1ZX5OBM1qPrrPXx8VeJN9RC2sq\n8XWIUss9X9DFwnJW6vV90GhyF5ixPqxe4pEqYNOSINVIqZShS0hVlEBLaPcv\nMrYWPRCsIVmX0HIThx12AItFOjsIB7bCZbBDpSQw+GyrSXUxNwXbm1gHnLz+\nOUkHHIf4Ea3Dr+RTcUli9P6JqAKSoBCr5CUREjtRDlMyaRYQx91u6QdOhruB\nYtSTmUWKpPcC6/66lxVjsS+B7Uzz4/Ciuhnz/RzEfCLy6TbSqGj2tQdgdBfg\nPLUMHLod/1GEDNytKBJz7mUDzc/OK6oPJTH6VNbd8W/ytOYofI5CwNMp4ECu\nOWkmWCIVxpBA8YJMmh28EiiZaCh+k1D/0z07va3PIiBw8NPZ+nNdo01n20cG\n5lQsLp0YYw5kmt1eTa3gHDpaGRWryLCyuORISAXyr30yo0CfzAtze509JdOj\nE5TF\r\n=NCUJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDaM6jEL9oQ72m2M14omEbbKKt2U8sA3/6AljUeAeHsCAiEA6jsCKOT4bjWdlzQ9s5Mm6wNFu3lqZqAw3KJI/+siHTw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-less-verbose.20190922200723_1569183015430_0.2009536292989964"},"_hasShrinkwrap":false},"1.7.1-less-verbose.20190922214812":{"name":"@atomist/sdm","version":"1.7.1-less-verbose.20190922214812","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e27525c59d4e40782077b1522d6df805dc7045ab","_id":"@atomist/sdm@1.7.1-less-verbose.20190922214812","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-1WY+Uo+ZC1vKvDiF3KOLFeT2gb2sCPqPxw5FcLxPze0MwNlUG/jSB/sQKhUtAC8hXfnS3j4MPfyr+EMf2RZ5sA==","shasum":"f0fc8b0ba04159ef6be3b0d43e0d71eebd24766e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-less-verbose.20190922214812.tgz","fileCount":992,"unpackedSize":4207457,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdh+zKCRA9TVsSAnZWagAAMJkP/2SbmvRQYMDbsxn6XiW/\nGELIPh5c0KKEskF8/l+n9kpfyRDvh2JxReu89UR9rHnREfLgSXB9molExglM\nDclFyjPRHY3UFBqpsjOmDPuR46xVMf79seLCkfxNpfB3v1/xhiFU1db6ixYU\n2xzj0FXHsCTRFL2XxPzZBn7PpmzxoPQlFc22lOvH1hXicYoj9oyIIzLE3Xi4\ne5VomSAt2vUocxsiIPNHIVi0kIu54pFsQJKbuoV3D5b6ExJEguKMjaVKnyp+\n4MO9cQwxFnObPOQjyfJ2SdomAay8wqcLAV6VkUSoaJ9P4C3ca1OSx97NI9f1\n0cJhA2Ymq2kYkXtDijgHIFQyCb1jATAyorZYW6ZRgyTGtiG0x45XLwikmj2E\nPRaT1mangb6qmPoISebIJreF/Ptkaq0JNkBu+0L1zDfr/O69PL3/snUKWVf+\n++8ZMLsQxO3gKdO9FjdQt8rETni8kWNz6XttjZ08EOaXH8OTwZGsuRLyhDnd\nS+0a87wQfXTEzru6HKWAKeCauhZ/H/8z5jvp//yRj96h2nRLPYgxyZPfaH9V\nItNfbZ8avjamv5L+PbcoMfcmJ3aMwFCuLE2PAiOSJNG2fo8CQMq/UknT34JD\niVSOxvt4V4NqFt2RYX8jhY+32/Zt5CoUcyLvk+1eWzauLfkzfc4LvXatVVM4\nMmjV\r\n=6agW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBJmEt/0OSzv8Nx88wKSWIpV6un0zprEnrrLFiuZVmkRAiANiOk89FKSqgwIpN6LJMLCLACDTsshtZYDjj+VCXVrpQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-less-verbose.20190922214812_1569189065651_0.21600474524800295"},"_hasShrinkwrap":false},"1.7.1-less-verbose.20190922220434":{"name":"@atomist/sdm","version":"1.7.1-less-verbose.20190922220434","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9aedc6b35955d3066c955fcc4084ca25ae458f80","_id":"@atomist/sdm@1.7.1-less-verbose.20190922220434","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-5H8Du/gN43Xb0Mx2csPuBGL1OvunpbIVyOh2X146K2QUkeRdCyVvgVFSSvQQ0AfYOqVrMQy2WvbTdEFjS9Hfow==","shasum":"cd9a22c199251dc8383377ac5b5dce155ee92770","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-less-verbose.20190922220434.tgz","fileCount":992,"unpackedSize":4207479,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdh/CXCRA9TVsSAnZWagAAz2QP/imScAJ+BTESlYSQ76os\nbBKBXG24ILf3DYjhlLea88uS+Ifaeo9g+YTgPAKYZFzEn+ZDeumrlVKlnM+Z\nsp3pDnYPsPNHqFPKlJTx3Bt9AU4GJj3I2Uq7nOcGXs4foOCRNWP+sWeItvDl\nofpnjss6533gI1bOL13HnfriTCHC7mG8qQfvnZKnX6ovku8I1rYE0y5GIeRy\nau+B7TT1L0elgKUVLyZWsYc6ba3Us2U5vJ/zTS5kr2yB3mgcO2K2Mtt4zakD\n16P4qrJitlKvGT0Qel0x+mrLkMohkUmN/SlBmNVlHFywybDSCn7lp70aMobN\nqnBG4U1yy0WkD/C7Sg3eJLf9BbxakO7eote6n70ZM95rNpz9bS16mg36Q32y\nyRlBND7qCOPYHOZCJlzMt5J1C99/WZ6fAouDK0WxMTc/9sGqXan03VrlcDB0\no0SBEQBd0trHHVpYriHpMaObdk2KiERRmjPN0xTzieojD56c+T7cemeOSswv\n3/RWsUC5lSpQje4t+PvRjaI2qDuMbuGIMR3yOx2TxQN6q3bVZxlAD4Zoa4Wb\nStiQHC14UzMj/rm2GLFHqdwtoouT+JvmglYWtv8TooPaqaYbF4r0ZIJbtk4B\n/8pE/1aN0SaQ7QiY7S2kQ7vDyJy5Jhqpz+l032Z4M8+JK4kgTifJvsPcTYih\ncRis\r\n=HH/b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDOXhHUVLQJK4qXjXgqyU/k+lZ/x6/S0pi7A66iPCyMVAiAbFYJ5pRHNdahr9UHxAmUAEa5rjdXDNclMq7FKTv3XNw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-less-verbose.20190922220434_1569190038427_0.035925389415573994"},"_hasShrinkwrap":false},"1.7.1-less-verbose.20190922223307":{"name":"@atomist/sdm","version":"1.7.1-less-verbose.20190922223307","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e30cbe6d8db53c3ccdafe02a677f7d49a8a9503a","_id":"@atomist/sdm@1.7.1-less-verbose.20190922223307","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-PfuomqkVpHnKI34YRm/QIjAxZLUCnUWR5VLWda6o6k0Q88DHu8JNNI0L0OHr22TXy9vQAvRFt/aXqKFHY3sgEA==","shasum":"127a3f2a432545c3274f3fbfe01ff2a411e84d99","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-less-verbose.20190922223307.tgz","fileCount":1227,"unpackedSize":5082123,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdh/d7CRA9TVsSAnZWagAAa78QAJ9kSihx8Genwc9lIfut\nYnQ8+D7jFKgHthpBEann/WouC5MGUStVcVNdJj9pIQi1W8GLoQ41s0LKRdnl\nGqGPvjLsdsUqOe1gD8Dj0OdGeDQAxvoY10kUWZXEeQPPAFuEIZIf5lGl3pZ7\n59F12+v37t3WNWI7KiNTpArNk5iNeDogCVc0RgZEX+SuphyUgrnD3ivQcf3W\nDd+4k1gwXtZ+ctnIcZJ5w+b2mo84UjdGJQ+F61Ga2oprTJFAXeReQaC8GB0z\nuhXyi+MiPvwgdQfVCEGCe6ebeHO1SMLXKGYEyiBO8umraL/RIy7ING44kw2y\nDV4Tjw8nAviSrWTt4RGgGXbbefFXsyfxWVWX/iG5w9vE0p9/X2EuKVR3QWbU\nh7Mi8zj8lBmU4GZboxP//DP5Ipm6DYdCFiuwXuIzIX3o+cjEye/YLpA9cWQv\nTLl02S2MuFlnNY01L6lj7SLkXNd4hk5+QlK98LhTDrtFH/809YudvZEZwJH8\n1zJ8o7bsKPsoG9NhR/gsbsWdaT9LrIhppWzamH5Fs+4swH2Cmf7xahZbw48t\nIaXNj7eyFsq3X+Rnocz+PFiAzgM1thMYtCzCyFSItsH3Yo1SYU317noPJzOe\nkZtqC06xogl7OtMsK3SG79uL6xR4IlxY2WoQI6XOHXfaHg3pJxWpCA3XCHsq\nRVao\r\n=V0JL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGSILJIbuQV3dvWyxi/Cl43XS4Ul5Zs1kPKRcK0MWuPhAiEAqJik08DJF/HcT1tn7uahxMlmxAZf7yNLs6lwLGgU2Tg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-less-verbose.20190922223307_1569191802264_0.2612098862285146"},"_hasShrinkwrap":false},"1.7.1-master.20190923001857":{"name":"@atomist/sdm","version":"1.7.1-master.20190923001857","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f827a502bba23bf6a02a69dc6020027115dbdca","_id":"@atomist/sdm@1.7.1-master.20190923001857","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Xs+sREsVfjsGNYXMdVxSyNwxqMK1q2B7Tpvi+WWaRn42CWmt8eXeauUSnxcMQ0NeZrqTQyLX0ONXjIqbCSFtDQ==","shasum":"758fdc84cb1aa5688dfc998680d950da9dcacd70","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190923001857.tgz","fileCount":1227,"unpackedSize":5082212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiBBRCRA9TVsSAnZWagAArRUP/0D8wz/40zcVcKHsHGSu\nXJSpb0yZhO63S0ss1nHxdiKJ6KrVmpOHqRq7Ya0+n2MLip9/1yW1FuxmDgGl\nZC9gS4fln1QcCyN6DYGoduNNmZUfOfrNBfORWkrE4lNKx0HYXvoVh62R9ymW\nOfqJuhqSeKTIPgBFRHlVmp5Nl40mkHkk/AUFDQ7ZurxGSaJ2sShHQAgZlFdn\ngednMCTBYrUVr89YsKulVgBNviWS2PLi/RIDw/D1Rj6eeQEvLD+AhnEbVv5n\nEnjrR3lyppPMqRtGRJQVOPsLklq5zFlnjdGGWussO5F8Jkn36WDC/j6V87i5\npeISimR/VEIqir4aKvR5ItuKw+ncrxkHb619Qg211i6/s2g4C/tntuRz86nO\nzlqFIJCIkQaBc6g07upvw7jORkc1boPJPCx+NQw2HDRHTt1Alj9dAYeWaM4k\nCCVHLBmYnfOwEK4X9ilnj9zIvQjuVMTxWsX0gTOA4JiQalQDAZeAm8miBqK4\nLGIuO9sve75SZD30LeHuO8lnx9dy3K7LhE0sq/tvhr6/ZI2Sff+U5837C56I\ni1xWvE0atrgmVFm1yfyuFk9ijvqk62JVAnde4cw6NKCc0JEZ6WSCyLQklyUe\nN+EofX2/3fAs9q7UCSopecNNuLA6tpc5GeNE9MU3S3SKJxtuM+xjsdwfYJjf\nlFjv\r\n=aEJP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC66tcesaA8nGQpiQ0vqhI0AqD2flx3tMmiPm2ZbFsusgIgEtuRlmmSQYXP9aR3YPReLKG7WHzCTirPbk9wJtr3Bsw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190923001857_1569198160660_0.45538723436875195"},"_hasShrinkwrap":false},"1.7.1-master.20190923002339":{"name":"@atomist/sdm","version":"1.7.1-master.20190923002339","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5f827a502bba23bf6a02a69dc6020027115dbdca","_id":"@atomist/sdm@1.7.1-master.20190923002339","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-BhuDF8T7+qJoUujrnJsvzIVnUt3FseYwnRvNmtnLBcJ6l4OLJlx/zwp6XSQr8LSU4gPpUf1FFD7UmV7KnmuqFA==","shasum":"4afca34068f616551eb2f6d8fd130b12d6e3316c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190923002339.tgz","fileCount":1227,"unpackedSize":5082212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiBEvCRA9TVsSAnZWagAACrwP/iuhC7eelCPzlloBRA27\nT+VLzR22g/4xbBgiUq05mZSgUorylwym7rdBHRE6jYr9juYtVQTchT/UaRO8\noYOaADmAJfflEPJz4o8nDb9gRTZr0V9fstIJZXAewrNJFT1pwm6nXrC+QJYY\nVfI2IVANoUa/jgJR3lQBxqfSDfSS0Ucy7ExmM2W75ero+6sWf1bY4jCVQQYW\nLIFLa7M6mGI1L+OB8OB47iRRQB0lk4gD4nR8Ju2kYq5GTYsyJTBup+/QQniJ\nNlF3Tgwn/sGTUx/uetAEHpz/GzQZHNbxOlEtXTQTa+3cMwZx/UMluixImCEG\nNFAKTZL56iS6fM3bNyrwjRbx2KC8M6gG5AKcX5vCvNCocxWxB6TmEOhKLrht\nw4ID6qJpnY1mdrGE3K9g0pAs56Man8iGWs/cdYHlD4eEMmOHvzh0koARneyL\n3PHdhznF1sALRWMO8C9KUWoqIvzYwgmEfUwfy+t4y0d9aEuytJFD7oTRnxHw\nUr86gUGtLyeOVfHXuUBRqcR9TA7FLpPObcEc4f8KWkScZPbXcy/2KOgZlAH8\n19CrfysyGLQqN3gNgHAn7PulUqasIQUnbpmo6T+1/oCgAUowl1+CrnnCnwj/\nnFwuz1EeuJWnLvN9xkh8kWMTqN71z82D4WNM1r7V0Aiocg3DtGfnI4FpFGna\n3Q9I\r\n=Mjrp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCN1GsNFcrKKEt2iltuQ6CUT0YacVD9rVYK8Vm2rTx28wIhAJ7+S769ZOyKyBCZ0xz2bjqXPQL3Vga/mHSVXeN5DJ50"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190923002339_1569198382264_0.9294586209720392"},"_hasShrinkwrap":false},"1.7.1-master.20190923161521":{"name":"@atomist/sdm","version":"1.7.1-master.20190923161521","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4486cfec01e804de88458d1de687c0d06433d480","_id":"@atomist/sdm@1.7.1-master.20190923161521","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/18Yx1fm18bv9MqHb4VMl5NbXNyb5y/Wv94W0v250dn5rGO7SNkmZvopCOI6v26zQ5ii/qNK5l9Tn1fdBurrqg==","shasum":"27409757d1d91fd65d85db1b7543fc95df9c4b75","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.7.1-master.20190923161521.tgz","fileCount":1227,"unpackedSize":5081176,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiPBMCRA9TVsSAnZWagAAxPQQAJ1dcoUqtLLmDzxdOa8m\nuERUeHD1rj6w3yvrXni4q7vbQMK3U3baoTpaBLIVzNVKer3dGN8jYbE8AIAF\n6kZwASO9+cmdamhoxI/dUfZackTz4NpmWEIZZX/64tpkX2K3sbC9hQOdLt2s\nz/1QP30uXEGzbI3+PzOEr0kNkKNohNtONxEhfUqxVQOGDf7U3U2lif1XTUA0\nLE0QxOp/bifPfvFC++H52ay+rxnl/BFWKoJlp6IFSsaM66uHeKCx6AgJPb7Y\nq2EhHlbvsiAilpM6zFz0P+2+q2HuICzI0WXs1R6LcOJd7VaNkp/urT0XgTN2\ncOtv5GfsIuM3xdKgUZLSflB0YTspbMNfjrz27hWjKvaPaqYvXBWzAl47UCfM\nW708hTqllR6ZojSdqrNEYXCVHu9vyaMS+wqm3M/E9zKsPDJ406rDJlC0JYoD\nvo5R4C5iunTkuEVhew2wly612u/IjiA7zOW/JVIB0cqv1Mk/5lJLOlX8gKjv\ngUuenTMwGAnv8U+Egj7UK5P3K5eNt8eDqU6SESun5nJe0p92zru9dYKsCAxx\naznoAx6ScGJThmfUHa1KxfyfNUAbZY1Fj/124V0DgOhmpgYSkPdyur7bUnYl\nnIkDBDLx83TrixbRNAjIHEz4t59mnwHW2+yQqok94tOIqsyUoKFbpzirWYTZ\n00m2\r\n=OQlh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfq55LogRYb8Do+Av9+apJd5FR5hfM5n+6Jv3R0ZRVAQIgNQpjqD9bx43mVMfJeZoig+Io/QHj5SA79O08r5feuuo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.7.1-master.20190923161521_1569255499469_0.036585898351763735"},"_hasShrinkwrap":false},"1.8.0-master.20190924042736":{"name":"@atomist/sdm","version":"1.8.0-master.20190924042736","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.138","@types/minimatch":"^3.0.3","@types/node":"^12.0.2","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e203aaea43ca1cd16b935c3da2faf441265ef988","_id":"@atomist/sdm@1.8.0-master.20190924042736","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/ltDK65Qwltfe/zgAYqJ1F3C4hCjIqOwXNGjUHBl3u8czUAqovTIHslxUH1rtgH3zMhliIjgxJNJmvUJGOR+2A==","shasum":"712675c3be50d1da1841e9cf036c0bdce4acf7ca","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20190924042736.tgz","fileCount":1227,"unpackedSize":5081176,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdiZv/CRA9TVsSAnZWagAArgsP/31mI5o7t4l7Qf2IYF5d\ndLCobqe3zvLZJKi6hvut1cXixqfTw889Rx3kakRJdXnEHGe9ovA3wStl6RHU\nCxkSC7O0UNZzWgK7SVMennfJJ4PF//YweVBzJ5gza1CwZgQdhwa28itb3HB5\nYQTpkxTt6bF4SZO91Zc1PTPDnAPKT6Iv6bs/0n7rj0Fx9ZszS6tQt2hYD8A0\n+ZzTkDwmEmS86DaDyKQ6ucgjACQEUPpXOAphac+arhBSemWis8wvzXLBri2H\nARdQ7PKDCoUWU1yDMi5qjxIBVxY73QMGy8qztmwuH+DkdzWMYunZaSKdvmAB\nih1WdoWozz7oeHO2xx0xjBTLRcplFLNicEjXx9Zli88X8dCxEDogpLBp+gjC\nvUxoFk4F6R4iQGFkAZ90W4LNLxTKZ7VfoTo3+tX+vutBIiGBq1jSY3PWZ9fU\naUybgr/yT/iDjJwSnkOjCnZ+nGPrabIr2Tm2Vdz0eJ0PPRbB9wf5iL2IjjpQ\n2GcUDpB7S3L/q0rGzoRyccXQRmlwufP2RXmy+Y7CLoGwCx8o0j80Lyk++t8l\nJsaK1EVaDr0bEAs8U0+8R7rE4pF6sHxN8d8PYdul9DHwhgiVB8/gK2OvDWLA\nRKTsf/Z2BG5AcY8/Lnf9eGeE5wNwo2dtVODSvK+BgnNX+32cn+B/IqGfY8qB\nUtar\r\n=1mfQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHr85v3M8JtNazMge2nWf6t/VxouSxhK1z+T4Jk4az6LAiAr/wL92y23fmJ9x+QBDr5IiJl1GWxCZcriJHK4X/4L5A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20190924042736_1569299454835_0.94545173427802"},"_hasShrinkwrap":false},"1.8.0-master.20190926164141":{"name":"@atomist/sdm","version":"1.8.0-master.20190926164141","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"143fe69c4728503e7dd055b766cb0ebd78f34140","_id":"@atomist/sdm@1.8.0-master.20190926164141","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-uuGL2Ws+Z35k5QX8su2QNRIIDDKrO5tmwZpMb/D7lM3QeEBQbnOnd2dTzcYkgrjZrEOrKFh6up/inKhTbpGIGw==","shasum":"ea462d486591efc3713d696d3b4926d69a459683","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20190926164141.tgz","fileCount":1227,"unpackedSize":5081176,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdjOsTCRA9TVsSAnZWagAA+xYP/1LNMYKv1B85XfZA1fJg\nYHXyfoWtbXrYNv8wXhKpQgAZrDt11V8y6gPowq/OLY2caaPLhhwFBU40FRo7\nZDARQEgfRtwMAeK/7JRc/tG/rgo+/W1w8tqgiVBHRgtjWctihH3MviVxiGPy\naX19gKLmgZKiKl/bpY6NpcLUO9ppfymEdqSUYOa3+ceC45ZVc+6skXExVMjJ\nDeJjgJHkcqpzRSUiuGhmG2/Eg48HMA+bbE9ZDKUbdZC+DeAX38u29SFsG8MR\n70X9Oh1e+/UC6lqjO2Bivq29wzGI/MQ5VZkf7cXwqCDHESKSczxYlNqqT/xq\na6VGnJZuCT6DNuz9USb/zuMDXpBG4mFpBemE3VSUNHfGeqQvAs86ViBUyT6o\nvD8w/McdKfP8osjbUll1+psko4fWDjRJnsoscKqE1qpKihvXGN3eUkmWgtHs\n4K8ROIIz5XqoHSpT4vhAFZk1LDJbyZ823GtyZGL1I0sXPJ++DRDDcJSKAOXl\n+tA2JeWSNJumUQIRRR4QohrMqEU5hO1qZviCdczYApd6TkZy4DnyAQaHSW/h\n5wElUNgc6SxeZ7l18v8KnuYCQqJDXFJOQv8jPaCdn5ZqFxHrUYDf8U9MjWmb\n6FuxWf15XcM4YKrPwdUGLD5o/08FXf5XB/+PROv7wPz7Drj6xKHrjzCjNa6s\nthpj\r\n=sDMf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICyRnnNNMm7V9LgTghxDzLs+miXtPezk8cLYcvVXLZ1WAiEAppMiAup82y3QeqYAIxI8OvcU0XhStHS2TJdDY8GZK0M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20190926164141_1569516305859_0.4570216251630619"},"_hasShrinkwrap":false},"1.8.0-master.20191007123745":{"name":"@atomist/sdm","version":"1.8.0-master.20191007123745","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"04575571f0a9542e719d561466cf8df74c8fb2f3","_id":"@atomist/sdm@1.8.0-master.20191007123745","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/oMjDeRPSF0nCTlygyGcMH4qATDFLx9hGHIoddjWeik28BXwJBE6atsCMPuvMWgyiMUnSlFNClzP6vLgREXYrQ==","shasum":"58948b02596ffedddcd2b404630740fbbbcc5d03","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191007123745.tgz","fileCount":1227,"unpackedSize":5084241,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdmzJaCRA9TVsSAnZWagAA2dsP/2Fsj6pMn2QStvBF0isc\nwZxJSYy7Vqavrk+V0CLvqiWCXkRumGD+6uZ5uR0wBNu50/tQ8V4MVg/zF72s\nJQdpmtJkKgYQ38/ITYg5p8UF4O3JQk6BwGemZtaaLewCAh+oCi4R5jJHmljr\nuRuGyPgFX8Ow2V9kXZLXcM6II+9cVPkVBf2hOcfabVvX4a7SoyVLiauP+Wem\nreazeNYmKfqZWd+DDDyNHV2jKy4Ryz4ceuL1FHjLmgFCNohXi+1dwRsarOF2\nU6qBuB36EDr4Sc4QoK0DVa6/TTLFfBgbP6oEpGWP0kbpnaG98or6pWCcgVtE\n3o1MmmttSEd79B9ro0YyVPZtJFnqZUDKVTU7TUuQqLOwBpHMLVGZqGrwrJkT\n7gHg2wozR2bHzlwhJnbPq+eGKFhVhrwpTHMxBYW6D5vqiAAvfTKDaCii8Mrt\nDhwhHd0ZDlaKQTbagxsuOOVWC1ssXgjrpV5bF8TT56T//zM0o4ChB9EZIJfT\ngsUzX0L6WZg05ZGlt86fvMG5q5rAtvQVYOX+1Z0dZzho6HmvwX88A5247biT\niWeboQHJySJcvupDUfppHGRjwatupV8eI7RNnQSGwdznmNQTV3y3BxUN6QLR\nnpntk/2RZLGJc3fTZh0cxny0cc1BhTJj/5Hp6i43de6oxDI1dI7p4I6Qe9qF\nNupp\r\n=ed+m\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICQVG7nOBdJbAmlctI3msUOvqz7aVgMR+eHh1RyVuIIBAiBf04McFhx+bDJxzUjqK5h7zO+kkFvlF7dwbglCwhFjJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191007123745_1570452057661_0.3586079352755849"},"_hasShrinkwrap":false},"1.8.0-master.20191007123910":{"name":"@atomist/sdm","version":"1.8.0-master.20191007123910","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"04575571f0a9542e719d561466cf8df74c8fb2f3","_id":"@atomist/sdm@1.8.0-master.20191007123910","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-JI5JUgoD1JMLdh6HRFZKG//hLBbTiTiA93cdGejEJ62ZJW0V1dBrorO+ln/eiPDx96ETbGBGqNiBPVVwbH5LzQ==","shasum":"b7c908fd77e6e084a086713625e4e63e36dab85e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191007123910.tgz","fileCount":1227,"unpackedSize":5084241,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdmzKSCRA9TVsSAnZWagAA9a4P/jViE3rwoq4PakG9Wuc1\nYYemx9xMbt/cfJKj8Zd/4/lkEuH1h24LJEdeI1gt8bWYiyI4bjlG1z5zHXI8\neVbokTk61Q6O6HlK5Pp7aZJ1NleiimcmlHa9N2yocx4BtUa7wIH72NZ8NTr9\nOAZjCXPRGimSC1emTGWpinoE1wREs7kNySSy70IVw5WB/nskZZ5s35xMovD4\nF2WXUo6uZ+1uTiIiqUGeSiQL8dYGJPO5vRzx/NzkkH+/1C5hu93/tHMfjP52\neq3IU3Q0rc0qPJCGvQz8sWX6XEr0Kfcmbl+eKkRsYel+y9043dbtkfFPo+CM\nmmVaEcVdS9tYOnavY7XeYG/TTJDRZl4MHT+A/etaQ00YStv3h0vy7DHsHTd8\niEgsF/Ryc05lZ/aD151dAXMD5XJXKeJLUHSU0qKsn9Uf1fKUgPPuUCdLkHfl\n3DhHgjcULqi6Cr4fOgVodAGSYY1Fmwf1+hAfbgJcR8FIyLuTBzCdyDJH78d1\n58DXJbfm6j++w0+57gFwFBK4I1MJb3eKszbisT9A5qJBa1jKNd6k9GZJETHL\nROyxCekcSEFOd0K2sljzqhd23PG11NDbFoyntWYxwaln2BcvR3mcDn6ZkH5g\nkLZt3f2Ou6e7TvfSlf7Uh2fK9eEcWpiB62aZ+DQFMA4aBBtyO6Fts7A9TPm/\nbPaH\r\n=hJY1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICo91zpv4xZVgSVfeYX0dOn5mKtJ7cHfeANgJQkol08xAiEAoFCAJS1BHt7IV5O5GaanDnRH5ROEeH8niWpOPSIUnfM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191007123910_1570452113531_0.35345005965939835"},"_hasShrinkwrap":false},"1.8.0-globby.20191008212622":{"name":"@atomist/sdm","version":"1.8.0-globby.20191008212622","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-globby.20191008204458","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1710627bffd3732888b63a465001d9ecefa00f52","_id":"@atomist/sdm@1.8.0-globby.20191008212622","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-WzralBc/scBsQlgEK9pCigmrTQnruJbmuEmJlkvm4r8gWJ+k97XlSTHZGV0DP1zvdsDCyUlWykkkWcpgrobxXA==","shasum":"6d57467ed59211be8633aef395be490aaa1ce4ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-globby.20191008212622.tgz","fileCount":1227,"unpackedSize":5088704,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnP+QCRA9TVsSAnZWagAAh+cP/1GHHDH2M6685HBUoi4R\nkwEWBfdaQfpwc4v/138EerckxiKEcdKp1lHjlkdrf2GwxBSJj9QP8KOwLfoN\n/hpJbPY6B13KjgqJT4j6pNyOSAb244/CQS7fw+qw4pUGiX5k7zhpmVLlQ5F1\nBTH+Lj6hFubX6UqBfKwWhreGJgP1m9hTeCRqua6+qTZ5y8Cs8fIzd20t3R0M\nRICdzZ3moNN28rcOhrChtOrlsu1ucFfcppwDvLStGqIKoR85ixGqOgDMKZEM\nnQ6rUUgeHFmQqZ3b0JKQTrHSHVlwK4Y/xHEdj5NdvL2N0xM1KsH5K/IQ5CQC\nGOQenjgnKqYJS1pYpThmcvH/ZEVmXYg4Hmfi1qhCsOHccf8VbB0tSc/Iyj7O\n/p+7fABRgKlg6fgg8i378zZGnXyZK++XI5rqGhvVKRTjEl8Iv7sv+bM65Dha\nbp3xi9IRD5ITVq+KhIkdFLaB814nyuBOTb/+O/wiXhz9fteuQ5eqhsRIim5F\nxEfrg4xxKIsnwBU6K72QfkeVVxeG31n2dU3KC6eaxvTSCTGt8zhW30LQ4rV7\nNU1VZ8gWV+/3DzA7Qbecp6mLUtY3Jt6S9xlZo8arAIHp0Yf37d8ko8v4Wnq/\nS+Ie0SmDDYXiCEo+UROht6h1CioKFoZxHpas3Z17/tV+Ecq8kZ/anrq1dinW\n0tje\r\n=xXgS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBbj1deolco92tHlK9CUMVMO5m94As330ajcyQMlVdITAiBlql772ZiWhiqmEyorRISi+lLoIWVfJN50DN1z78eILg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-globby.20191008212622_1570570127446_0.5234330593813439"},"_hasShrinkwrap":false},"1.8.0-master.20191009103804":{"name":"@atomist/sdm","version":"1.8.0-master.20191009103804","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"67c469fd09c05bffa69030a3c1db4ebb0f413898","_id":"@atomist/sdm@1.8.0-master.20191009103804","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-aR7tbq5bS9wNiJs9n9cxMTP81gF0OvOdeoo/QI741V1h8mcp/Wjn2Xp0jMrMaePHE730/hNNAzSRwiiSRCSgcQ==","shasum":"9fb8019ff799af5d03285c1290c84b01fd5b3a96","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191009103804.tgz","fileCount":1227,"unpackedSize":5085461,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdnbk2CRA9TVsSAnZWagAABpwQAI3TERc8f2F1vxiCnVbk\n00L57xLazkRgBwSEfHSqTChzPscqlytdXdNASHjpmI7SfKTdDnrwc6nR9S7B\ntfGtegKminbwhzPBBLYsBb2C+xAxMiKTdGAAx+GWQM1JjeZbPZtQXDtZPnHn\nPzuRo++m5Qr23HElRoFq0Di4I75Ps1V6DlnydVmbaS25XwGeNMeSBZBLiOl5\nTYccU+8EYr7W/9gQJLmOTmieJMiFOl/tPggrc+PSvXZ2yz+QyKqWpV5QsIDg\nOI8Dl4QGO1slwtvdzBwGidJa/ZTuEn2YNCo5cF0nBq/SRuIYHtDgBl1WCyWf\nqFpoMoBVogDKDsqZDaiXc9tQ5QN5D+ScD879A3XJk80OETJ6gHvhCLn80QY2\nLIuU9xb2OqLQjK0BsgeTQri60bCAOBLp/sxEaf1hFPTeub+ARNpx1RywqX9X\nKyX/+pNzB+JxpT0QnQTzD1qieRXxmsiIr2zo4JvbwELLzpCFiG9L7jCwfBDz\neI7hAeSonDuA9EgwSCiSAL47rNL8D2EUi77dWyD6WSYNBrPEtpiPfk53fqku\nHTMsjbEBrv02KnIAYtRmphfUjnuI4aa/G/dKVfTr+w5LgDmM+g/2/TfLzHJ4\nXIElWuZixh0eLyec9quEc+g3JBI+6Zf4ME0LWP6bklDK1JQszxyVMl+hmANc\nR6e8\r\n=/hqr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG483MuRxgowjS5lKFC4NEfV53wX2YF1kfZI/J6m7cK8AiEAosksgTrdBxv5Pegfrj+fvcLlE5d68OMmmVngKH2uG3M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191009103804_1570617653400_0.8305707474436685"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191011070241":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191011070241","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191009184515","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"78f0db81581412326d33eb92e105028ca97c57b9","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191011070241","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-CtzyRE8khJ/82P57JOAKhYzWpzrhhUo9FmG+vqZxWF5dD3Qlb1PkQwUVruvmYJBYeQ6NmSDaich0Pd7GPuBZsA==","shasum":"246b23c69f3a859ab7365b433ee10fa9dd1436ae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191011070241.tgz","fileCount":1227,"unpackedSize":5505069,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoCmqCRA9TVsSAnZWagAA2mQP/2Xkek7oeQZ6lJ714kTF\nOIggeYGCu69LCm8nrPxV8cZhPNkAI4k9eFjy4GUto+Nx06kHmCP045LWyesf\nNW2AfbIPoJH1qWEeVVoScFtrH61KDy0+2KBiEFI79tNeAB9I8cH657VnKD8G\nOGR9vhUrc88k8tPxM+5WrbLCi8TBWTnxCWXEMQBe5B6dTFWJ+S5bv7kETAHA\nxgRcY12/9/DssC7uee0plKhTXZCwyo7LWwGVdtwcIuOrA9tSy/56uxRe5g2P\n4yIoBG9gVShdxd/+VePBgu9p/+X6YU3BkwpVIrjE0YBeY9r4GquVKO1tTeRm\n4lGWXLGeGinypo4uEc6FCPMKHpREDJTPEf7FBGv1enzjnYOoaEG8P9uCuzb4\nnS8YHq25x6hkS1JOeCpp6srExwOEZhzL7X20hTXv/CyZGloErXfJ0UuN951s\nXZd8eAjiG4OKCjwaJRtrPg+z4utzwQR9AziXyafKXOuE3IUP6kZoIueb3G77\n8O3s6qhpFaGyztY+kSH2TWnzLLZEOJB4Ztlmq35Bhbp7BuxJx0Gbq/DwlD7Q\nyWSz3dJBFGyCZ6uZ1IL3L/pB+F+RVU4d5WPrJSogZdo0Ort14kAYlSIQJczT\nNVqeE4VfkGBmYsM7Q1leSdwREmfgiJij+gM1ZCYjjgpm8zK09oBWfWNfRmAk\nbnW5\r\n=sNQz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICyJidnmAR/uWE3rFiA7AfRRjvWr78TR5KLt1MjTSBhEAiEA2iiVaZDGK6dF9d4R/x/besdtYPYah2fOC5D6N6qMzd0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191011070241_1570777513582_0.9438447105930217"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191011075330":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191011075330","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191009184515","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"77c961f70ac965e7b84b7fa922cef98952ffe960","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191011075330","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/SqkQ9SirEjUScIGEl6KN5PPQJHmmVsmD25OnKaTo/eM3a4eF8aZLTCkSolTZVW4VLesHTdjQoiCiSshZbFJuw==","shasum":"95eab5d6252dec2d0dc7069db57cc6084d3fb308","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191011075330.tgz","fileCount":1227,"unpackedSize":5505073,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoEKPCRA9TVsSAnZWagAAuGQP/jAeb84Y+BaXiD0xXHBX\n5kM9nKRg3e5FRbIhFgWfG5RM+3t3nkEJgoPhqemfkq6/wezHCAIhWx6/GNiU\nP2nhZQQIHtk9WdTu29Jp1W+TwFnzAqnhNnYrYwIzSOZdBX0ktrmkSNw+0Ia/\nojq890r2gp9ltbia37aa3CXiVBZWsVOl5AtCMkqQmXFgI/tpwdrb+4AkXajz\nsFdjixGOO1+f5he2exYCkWRowvg9ZswgFgjbT+eQ5phnAdPZmqHTu6HxEIeX\nGol8C4tkTVDvhDtG1bytq2sZANh0W1+8dVPNxxMBHv8Vyi/XE/06T7Bz4u4r\nthiG9mVDpMd1wFZO90+BnPaA2Pm5lHDjXDM+R/8rILdVdGndc49nmJStnlmk\n2gw2ukaUpyt6yHRw+GwT5kVttNu5HSDNNOP8O6H1pTcN2M4TkoXaYZniy06O\nm8D1C35lyVz/HSalm6IdZhNttPhGC+/GaHbJX1byBdCf/u3xGi5kWtuqGroP\na5x1CgY1GfDc2PgEjA6emt/MSQYKZCZ68HWNwSMVgqO95BcRHdRcjMYjuRRm\nTZlPODlOmwqnF0abDwlYX1NVPbLRwCn3J1/P8CsZR6Yg6slIVCh7cQyHyveQ\niLKYWWeKQZ2VQzyfS7Vtg/CdtEqabj/vfegQp3469cd4X7GKWG5S3ZEDfIbx\nEk2I\r\n=at46\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBV4JAi9i+ig41lTVCxG1Rwy17HCL2hjEU+Cdz06zhoDAiBzAGCjXlWG8Kt2EC0cRnisc8q+HOylGBxCPB+3ErieVA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191011075330_1570783886247_0.03764878450054554"},"_hasShrinkwrap":false},"1.8.0-master.20191011091430":{"name":"@atomist/sdm","version":"1.8.0-master.20191011091430","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-globby.20191008204458","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bca390eb4c74da46b808fd44f3be40d54e7d89a2","_id":"@atomist/sdm@1.8.0-master.20191011091430","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-nvDmgTo3UjlVmYUy6mdEuSbNL3Bgu8csOf3wSW+7nJzsGHPSKTKtfcWW4zY+Y8YU12oo6PjRk/ytnGd+nWFdBg==","shasum":"6ca1bae3815a94567237a0760fed90911af3c96c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191011091430.tgz","fileCount":287,"unpackedSize":3026547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoEiDCRA9TVsSAnZWagAAEAMP/Aywij19/8yNYbtXkNvc\nQLVLHEYiojUrIqE4TJb8QyEv9tpvwQJnMzePAFWOe/AiyrBpA4UPB6TF1JpH\n0PR2S7p/QhuiHsOhGsCf9AOVefhAtpT/fYKhYVVAICkzPZ4OTPLVPuV/itq8\nm4gDKlX83fvd6cdqxgEWGiwgHS68QHuYT0TCE9Ni9a4t+yhvf/srHVrAZHII\nfWpEOoXJ1ujwPrU1kFhL7OXu3cZUPBatHdmr/9g7vUcPJx48hPeqxY+RKxWy\nsLXp/EPfuAH00xNEYzQqUEjQEldGJo3OJZAAEEREdKKFoEIZhGt6oJPZvRE4\n7ONeYKyGWOOAoPZTeKtIX9Rn/AFzj2RWKZlNTq3o/QWsiY2isgQQ+JgjuAs4\nR8FmzoGW97jAep9+vdaaORj1WIkgZywgvpyLKQcqNoSPwPdchTXUu+4OiPJb\nYlVOE3OVTzlH7Who/QlY1PEufWeiws7xh1vXI0RybCUQHDn7ytUzBj9PJ6WP\npYFy6Pv6NQnuaT1VBNcK2lkgbbQ0oMKxEz5LFWH+TP0tLnUc0E/rcZfNyHQv\nJW4dHaeE9GoaK2lk8j4f6n/RT0OQv9DXk3h1B94jmpOFYqlvjcjhJnyhddcL\nde3p5dWyhjAhiB1wdBIU3XSlYYgVXALYKraxhYvsD0xj2nua5hWD563+P8ou\nKmMx\r\n=Il0Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFmf4FAB62Fq6k+oLIstPhyzoz2ukejWGp5QiNyjrwJUAiEAyvKuJts7HMycg34ropUAMmMnwSu62Gvdd2PJNKPQqYk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191011091430_1570785411090_0.6104536581501459"},"_hasShrinkwrap":false},"1.8.0-master.20191011091613":{"name":"@atomist/sdm","version":"1.8.0-master.20191011091613","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-globby.20191008204458","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bca390eb4c74da46b808fd44f3be40d54e7d89a2","_id":"@atomist/sdm@1.8.0-master.20191011091613","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-eiEfZX0hojMIGDCa2qPX58bMe1W5HpCdBwbafNPql9mP3Qg6E9EtIB09JATdJqHm2KSjEVSfDPkKRXidKryl1g==","shasum":"3aaac59ad7051754ccfe1c13c837c463d099074b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191011091613.tgz","fileCount":287,"unpackedSize":3026547,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoEjmCRA9TVsSAnZWagAAZb0P/iYBr/6gDXzih6Eimhwd\n90fqIN0JOejwGu3vYJ3yQ1heHM3B25zcrX7+HQ4l02S5lIAJ1tFsDVU/3WSp\nIkjtVv1WiWuOzTDEa4rdmAEbjUkfdMSHsLUh2iyX2mbzhA0bXAtIeAy+YIGs\nw7SLpDOv+FXEbzNBQU9gmsu3uvBTaAU6FenaRlcbwm5Uzmz3TR6bMNas/jE1\nBPEysqHtj2CqAS94qMMam5ilehVkRkKwKn3KOyKm+WGb3OqQep9bhlFGXTnp\ncQhZwjFMCU+R3xgXEC+Y74tDwdYk9HPnGdDMHwsaHpOnnvaeWXWzJNL66u7Y\nmzrhJxU40wZW5UkOZhBtFERBYzgDIPCKn4hmvIqTRnTT/BY5YEhEJorFh4cs\n3v5INMvWBe+OKJoWHuASZP47ZBZttlQ5ZdhY3+4NZvR38eYaPIRo/0f34zxB\n5qR4ENV/d//qOoPEGqVWvtdl8yDXuB9g3Pl5nD/NPx7dzVKpsT2BBY+vPb5l\nGfbOT1IJICvE7vyAi3OprwQapZv9a1c630nV4ckur1LDeRNAhDAvRYCnvD2s\n6Og4HgWsxPBr4Cve4fYPVRN7M8rtMDhofTGQB/sWUItJqA6q9T86INOCyaKP\njoPMDTvWC38S503pxT961ekJ7gcqUrV3PI5PYRUKWgpudxnTUWAKZycXtAWB\nAae+\r\n=5Vhd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjfm+1nxDHcUsRlnHI4EVsSdWwrKn/nIDqZRLHvYlqtAiEApgCinjrBsrcVk04IYoGW+IwtTLZtKm+cKUmmboFafWw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191011091613_1570785510129_0.5382640020988494"},"_hasShrinkwrap":false},"1.8.0-master.20191011095543":{"name":"@atomist/sdm","version":"1.8.0-master.20191011095543","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"02ceec9af1f9dafa362208cd36e990d960cad726","_id":"@atomist/sdm@1.8.0-master.20191011095543","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Qlu5yTOx4/XQMbCW7+kQppbmNG+NcN2hUQ/RlPvyz8P0YUAmKRU0cYECOWC3xM6JutVUB0HypLOGLwTSG7ta3Q==","shasum":"65f5b08f9cb177afd31d7220d197934fa383e81c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191011095543.tgz","fileCount":1227,"unpackedSize":5093757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoFIwCRA9TVsSAnZWagAATK0P/jb4e7ECnHZ08FFt44QQ\n/gJxhRMnta1x+q0ASdKrFQ+obOWDGPWIwap6CB1vY9wjL0Ywr95clh7PvM/g\nCLyoF8R3bdsb570Q/VINbx80HuqKPJJKXPYLIrawObh1BEJUUfnpPAvylm6o\n5ERj9/MI8ptTgH/ediCaWBCE2PGo152vW9pBvLYy31YLNug1MWqPSXVdAi9G\nZtPWTCXoHs8qp3uK8/RX4x9zV0Gg9BfYplQJ5Uo100SB9HhO+IQpOM52I+kw\nU5OVzR/1UkVEAHAc99z3U6ZxgSybfrDFPeWEhUB/WMJd6GT5Ke0sc119f9xM\nBR2v8D3/J2JAVBZ3GDZS95CQUbGMvi1ikTitgAXFu1Xe7EtlnUnIanlFgRCQ\nyHv6UFG4vmN1tvxsznskIdJ4QZixVpwfSTivUA4yFf4eaw6pifqOC7dryi9a\nAwHqDkt128hEXHBuSFf/z3+gIA0+zOUzvJccRmFd7sTVrMt31lh4oq31KOba\nkCsmKq9BqPG+Bu4D894DW6aBIhGGUoktkigDMexJpNWqx4FnpAFEDlknuomA\njTs9tINI3cC5GRwQivw4bWRSQ6lN5OwpqF90wGzvQH4gkAm66JeDBIIxCGgy\nceoc9zt9JPoKDhhAKJYCeSNehX3f7Dug2D+vfCNKPOtXJ9CEYyeu3Znd9Hji\naRIl\r\n=lvBu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChge1GjyjH3/5nni44oaWBZDE5db05oVCGzK28IfhCjAIgac16iI8jiNUJaPAZs/6Oe+u7rGQgx05EwoENJ6fe3XA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191011095543_1570787887739_0.8799975104852442"},"_hasShrinkwrap":false},"1.8.0-master.20191011095607":{"name":"@atomist/sdm","version":"1.8.0-master.20191011095607","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"02ceec9af1f9dafa362208cd36e990d960cad726","_id":"@atomist/sdm@1.8.0-master.20191011095607","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-i1jDsjXbPPOuKnj0oBBiYiRqP0qvrwIvHyIs2EBAzWIvCgj+FFo1p7yNS8Zu82iE9j2cORV01a69TcNdp9SkIw==","shasum":"162c7fc1af38fae59c342b7722209d28eb520646","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191011095607.tgz","fileCount":1227,"unpackedSize":5093757,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoFJeCRA9TVsSAnZWagAAiCMP/RmzuASYLGECwRgtrTlY\nTMDaWKvQmh/mwTteOMndVJ1MgQ8qLOG6pF0Rd/m3GADIImqm0b7wqUx+u5eW\n/gEY/WSKF+DT7Ururlnacrc2D5vrxKhgTKMgPJUDe+aH9vUe8z8WZjB5qa9p\nUXkvEnqsrxWeLqSN0yyIBuQinMGDzV9/MbJDyskhIYvXjE21X4VhTdwPrWKO\nIyvkMzenOqsS+cRcSqt252WT9zsX3gOGAFBtEYDFmaBFzmaCtt2HGInZScWa\nUuMTNIB5B6EoegX8G3Z8++c9F4K9UXo3bennl1a196gvaeCx/wWM/8r0dN6J\nlC23v6casjgLo142d4bgbFWgvxkpj68tH0pHOROw39G3p3gKh1AeK5jda6qm\nT1YEaAMrDufykcu4a7Q5cJZcdYbprzTnTjppHJdl7urekPVduDDgUiGySdYB\nq7jvtiHwrZJity98614pAzlid9ao23/09WdhenYU9O2MkWjH+9Q6VJv9Mmea\nVYiOzkFFk4yp3EYU0auIYi9/+inBn8HOSMBoX47mfKJrKO58n5JDTPF1ux/y\nqLjhXmYkr9ed142oDORNJ08B+nbZDa9jNSVTtzPHtwq5U3MF7yArCwn5buGP\nqbu7Z6NzlJR/McHG6mitcL7kgiV4sNE9zulUBTRqZebd8r5+a/huwinrFUzr\nFBEW\r\n=rU7Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCvjyrkpu/W3Lai72Pli129B1DqZtnNUNdu/+lMNVQWGgIhAJiRxJhAuEf7EA0Wskavgr3eermNqoGzZtj7Nb1ebiGW"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191011095607_1570787933844_0.5040975596108295"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191011125733":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191011125733","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"602412e4b562fa97375c6a6415eba468e7b255f7","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191011125733","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-hJ9h/2EA4eEPKQo/BXCRKKBNeCchpBK1pfW14chEbO4u3ty1Dcgx62CO1ZI9zhmI0XDyPcP1FWYcWiFXp4qrOA==","shasum":"5389bd17ca48fd07d93b084e6c87c7af75b6e7ac","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191011125733.tgz","fileCount":1227,"unpackedSize":5511779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoHzcCRA9TVsSAnZWagAAriQP/3TCOhmc62M1t8Qs5eON\nwjjbgPyxi4FJkBruNB+Szvif3GzC0fsfOgpiNyQOv45Utt5FbTsB3Ttc8YcI\nojjM13uFrKtYvTzFKctMtSOXf94kjAQB18ozGWG854f39aaGkhbYZSuY2x1J\nyV2deNBOhcroCzgg/i87IXGp2zRuqO+rNlTMyIA5P5zjr+ENPPpFl7eev7Vj\nzPvTDOgcKCyKyFT5aM5maAJ17DfsbeGBw2y0dEgdzAjnggWm20nII16pV6gj\nxQiHepM4dM1E/6b8vobEATstsKTHY/uNihtY3J5ov/b4RJ4rOx7buke3Ncnq\ngvPwmdsq8icia1fboRdrGHue/syNfZlSJ3fBg7oYqOuOK/ONWJ67tCjyalN/\nWRCj4KIniTY0lu7hf0g5OZhZZoLhpkLcboGltZP3MrZ68QPa6sZtTaFK7eN5\nHy05wX0edZ6qhhh12WUK4CXTPrMssRAUvictdKfYPnTzq9BA/bs/ssz32kOm\nHnMjv+/1FgexQpd6PJUdWf+Ang1VlN/Gl/3A/TCKuLrIHYtsQjQX1OJdt722\n15Sj8GXPIZmRudcVfTqykpzdEV59qYqbISPN4cKOOy0rg0HcHHqSgXQgALZm\nqr0vCcWcXk4FuqT192R44Gw0wumu3hblC52x7fZQqvojrN6MJtMFXiG80Uyn\nXIp/\r\n=gK+e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID/U+5XwSm4/Nv289Ntxh7RuvKgaTTpyHUltiL+t732eAiBWwSb35pim77LO7WBDarFZDsp/fERbq3EuzlPFITAxfQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191011125733_1570798812022_0.8969359112527862"},"_hasShrinkwrap":false},"1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407":{"name":"@atomist/sdm","version":"1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6ca221c66cf8269a06cccc0aa34167b950b93f60","_id":"@atomist/sdm@1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-O1EV8jnlu07y/dZcUxW9skmtvXSPJWM16OqvpLEI2UaqxA54HNllNY5IxyOlJLMvrW0gotn8Oz5Glm9RJOI9UQ==","shasum":"7449d0da97b1465735b41a0bc5a3b0fcdf1636f7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407.tgz","fileCount":1227,"unpackedSize":5093813,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoH5eCRA9TVsSAnZWagAAT3cP/1+EDJiaK3GlousYr6SA\nABmNo+9kuozVKYmAExoO7hKIJmnQ/G8l/eS3CX96FmAQn8eKxN7p47NtL49E\nTJylM3wrwmKjwABtLN+KzuaymtHgBp5j8KI4K8cIvrlHbC/uM/QJu9fvMkKi\nn1wfKaaycrfp6el2oKGyBPt7yqc132a+3cDqm8GrT/rsYJrAUoB6vO18X5Vp\nsBtX75gp22A+/ksg8RZQnVMXkZ4neSyHKt2apaz0tSB+DfhC8WJb947dcbUO\n6xe1KszIi7kK0AaGc0MY+55VjaA1/OSCOwCfNZLk/+YWWM1PSG4E/JxajPiN\nuTvQ2sJTZzHo1hfLapPBm9rvL/DM4N0owuIK04ysxDcAfP/HJF6s4EHB3BFK\n+Qiw5a5G6grUkIFyTRgLxMLI6eWLnLkUFxmu6Tzt+E6FS6GywJYCK97+i/8U\nmuwYS9HG+O9xNepfsudtb1iInPDjZ5CMWVQVNDVJcnJbw5dE1oIW++gEyKuM\niitfyAaRFBJXOMeYt2eTJPsniJfDeDSK1PW60R9p50K2xs4dfsYyS48urUXC\n02Fef9e6kzF76jq/KjHtI5YWu1k18yprIyPRCnPVJ/CJNQcvYQDxSajWNqnN\n2bBRJwAnogoIQxtgJJktGsAOlMTr9f3mwsHlZ4JYKH6+95eKI4pPKUDAA+bI\nz0XZ\r\n=DEcR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHivCWqfSWSrxs6G+krmLzo7frhVerDRpgK8fWp0JKx5AiBC4cIEVpfG878S/Bm/wVzKmAoTKQncQRyQqIqoLaGxzQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407_1570799197953_0.4877149828854612"},"_hasShrinkwrap":false},"1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502":{"name":"@atomist/sdm","version":"1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6ca221c66cf8269a06cccc0aa34167b950b93f60","_id":"@atomist/sdm@1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-OK9B4xFjfMRnyExtF7fpOBoNoRkBeAU4rG9vczrVUwkqii98fsB3L686WHrt40D8FMVrw4unTOoWsWkZowEevg==","shasum":"4444d9a90c76d20f725dafa3f7f73e320c877e6a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502.tgz","fileCount":1227,"unpackedSize":5093813,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoH6PCRA9TVsSAnZWagAA2zcP/2/s6+WC61fMOTI084pF\ntk+yIZS2AjWMXgWJbRMu91U6zXuRVNXFoSyK9ovVLJQoQ6Qi2Hw9pKPsZ3h/\nJXLKQe6yOtUQBWqnvK4QjMQrblBfN5ErNJw1frXTLpb/NdkrFX/C3blL9LHi\n1DnaQDLcTJPz0ybAjQlp7wKxoUe236t8LwBn1HzX197MRCnE8WY7CrDYpMT/\nkm9uCfJmMUlOa6A0a5pMZL94aq02euLtZI0zRXL3ezoPci9CMtzhfFFbH6g1\nR9sXavORoF6dT10pAWvnq1iJzzSnkBGzZAp7aw7t0IjlcCQDLNaBkcjPxf5t\n/pMfjoWrm70U3ru3E4SdCCnZczpAdXvDCxR0NA+ED3+jzSpZ+6pcVk5xLVCb\n0pa4z4W/QzEWyzHyNN3+FiNZ+mRxnOdD+O9eZYbxJf6axShh/zeWFlf3tpzs\n/p6W9VfJotq0bqc4twkG5l0Q756qQpRzXBxmJ3QEeh83pBSuCmi1npAQZCwM\nW3HiKQCq7Qk3xvgT0mI4sj2hSVjpDOwt4JU9gS7MVEqolA+GZmKWmOp4p5Pn\nSfMtartCEd28tlbhVeDZ4v6UDQQdXX+B/yGwe5+Box8AMqW1iSgryZjrb1ip\nqwugWPJs/qIy43xT8Dy43KfUH0r7bWBFDGIm/2QjJx+xyXGo/EyQF/moTgkP\nsphk\r\n=jHzx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBan3PoEnX8J+KhF2mdVjHNj8vJYAM58MnX1FnVe23p3AiB/cCcrMiYdZlkyK6NE8zfM4HkGOdzwOZqKNxSw/kUofw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502_1570799246463_0.3435245309276045"},"_hasShrinkwrap":false},"1.8.0-master.20191011130951":{"name":"@atomist/sdm","version":"1.8.0-master.20191011130951","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"07634bac7db414a9a1855bec66c4b5783d662322","_id":"@atomist/sdm@1.8.0-master.20191011130951","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-GVSqHtFLfrdyAHbNDI/uhrc0HXjs5/TsbGwEy5mfwQP/3T5zv0nvKpMr9aFfD4YVAoEg8dDmn/LeB+CJWojLrA==","shasum":"064c827e3803f093e465db5f6923575fd6afb9a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191011130951.tgz","fileCount":1227,"unpackedSize":5093778,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoH+sCRA9TVsSAnZWagAAXB8P+wfTMEoxTSnD9VDJLYEA\nljhFBCVeHffdfRJaVRjvDTWm0fqtxxkpRzLXi43ddSWbTyH0qpkWN/ya8qhE\ngzIp2xaym59X5nNQH7vnyj6Zzeu1SRh2zJJw+0tIrRF5IMmp6Pwu3TrtbB8I\ntvIBoO2SAs3AOXUSRXcR7/qmn86wcdhQlp/0ZX2IsAQmLYAnpT1Lzrc2jWEL\nRnyIwUu7wHW2WeuDeSXS1pWNdXCuy+mdiUTHphN/L0qRVeg/JWALyzi0z6i/\n0bmAVcoJVAVLFN01oJq1SZjW71/HAUwytr7tD7MLY28PZH8rstZuYbBZ+8tk\nEGwZmnhCfbJUiP4fGu8VIlAD3Q5omTc9o2Yu6mRWK86bQUi0ys0G7jAzDDw8\nfWcCGOSQS96IKqobqS4nLFub0wAP+xIvQMLTUAekQ4zYMN78UcT4nsdJ7JI4\n4pkFBrdzMwjFAemTsQzCUAdiSo2ZMiin32W8v3MNWW4RNV0KrgX5OEBB5Q8y\nvYtSbE7kf8z4j5alzwqkQX4tonih5mJRHErrE9ER35PtVvkkDNdbbEqi7rrv\nre0M1m5Hl0ZL+UfAFf4rFvbZVxWMq3w/gQ3LO1TYpDSVzsQ7sffzV1fCe/+g\nu4FvyJ0Yzcmdk9x12AjLzdMWXcL0cPxpuIT64DQbMD+z4cRvDvd4ABSMxR6p\n3a+A\r\n=G0pj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEpmBM18XoL7euYD+XQXI8YuzVvij/DS81hhXCobopHrAiAvHBTtxXi2hWBpaO7zeA39vwCbA3hZ7x3uFwXWJGEOnA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191011130951_1570799531257_0.9099230270309246"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191011160139":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191011160139","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f38fcdb3467cdefd3a92b5ddac66e304b7a8bc76","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191011160139","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-kf95eu4cPHQFJHEvrD7orMY8JhF50Uvek818668Biy8buDpPq76keMMqkDuo2gm/3TCDQOQDf2jzp2s5Cb3ulw==","shasum":"91a7c97cf0d6c34947479ce981af15c515234103","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191011160139.tgz","fileCount":1227,"unpackedSize":5511649,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoKgKCRA9TVsSAnZWagAAmVgP/A27bA4qqkZ0XTlFALhI\nVqylAZG4f3jTLdRxVXHIeDlKrstxRVUU2YWz2dcSJESguGxsgnrKJR3Prkhu\nRjCKMAyV5X3vyl02ViRIlV/oZUggMmiQ9Ngstye2r3GDH9EEcdqXStuGVZVQ\nYR4DbL+fHkUF2j/g1GpOjBKjzNO6OZrWwDm0RM3MgN0m4UZH+49qI3A4paEb\nl08fi9B8ZScNlGUqORoqw/drOAGaPfuFG/1hoVo1xGe9PgxI7UK7ySNXHHLG\nO6NwreFmjl5h5fb/Lstk5p9N9iHxVMqooeno4GcxJEW1/94YTH97rPPWexTO\nYVZFDCI7jnvocv68vPKizbMBmLlZfvrypu+nGQuF8iCCiZav1KKmERM9LTx5\n2s0Pr3oaDBBIhhRBtv/C40WrYf+UhILwHp+e7JzNcblmzrCLowbMXkQ4Zzdb\nr72AXzNlGuun5jF4/LPCrfwtQBrSZ/T8yar8ZnO/0vWk1BiznWdvMWZplkEA\nSlFyaJS1fzIJPcOhEhZg8jAUmNdVm/9Al8GjWZy3J/xU/4ugft5nxoWCdOsZ\n+ZMoA8cIsn/IkNJSeM9F7D2XNuLD3xRK8sh0uOGSfqO6QMbpSaIov5lS4FeK\n05sB3g4VyIKHSIhMIwmYeK3+On6va+UzmJnY8UYU/UXWKAzSl3hl7+Edm1PE\nVaqE\r\n=n9Kb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDINR/oyG51fCP/3f5WcuirpgzKrxyqPKWx0dXrK3wSwwIgXz+WEPPr4THV6O+rje4qgSRdVkgO4xy7vauN95wpHt0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191011160139_1570809865169_0.7183032720900215"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191011185910":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191011185910","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ddffcfe16ba0ca5a2d8a4aebcfcfed000221c235","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191011185910","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-YBnLMbDBv8t7j4UfuggRUbd37HLuFMBPJi9zDKP5L8mBw/bafSfiNbGPx5E2lhF4n099yeN5q56WK98Ije8ieQ==","shasum":"0d82f489c899e79fcf5a5d496641f0cec043742f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191011185910.tgz","fileCount":1227,"unpackedSize":5511660,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoNIOCRA9TVsSAnZWagAATVkP/jeOQ1JG0b6XtZ6FjaBk\nCFSySYUT3jjcZZd56sJnAsgsP4kokNcpIKc3WKg/BDScTx3eLAf9+j4RbPHX\nQibe1gx7PNc2gaMp+IyLrKVQHe2aq5bxjN2/gRh9DZTuOt4hudPJXL6PmnvP\npmKkhE3CRrpeg61pGjhuZ9c5fcBm6H2L9/CYqjcHuy2wxZ5WGccZNtAeS9xs\njQWgUaMqIfPhdkkZ+jSTEhahdLUavZ2sPhVWxk/fDFxg4WimFb+BslpeTNva\nG34vDrIcwekMTLmKrYOwS9Kzc1zzzemMKHhldsQ1wRitZGuC240RsLqt8lrN\nBy/cSYRtIjsR/GrRXA1DB1JqN0Dp7HEQ9bnjqje9Q9hFW1qajLYt7ZtJfFhM\nVadE85g8A0bQJepnvz4fZ2X/lrZOu+1Gkw8UKozNKrXifI+7P9AQ6/FDxxqE\nR0pLmdu0NOtbUMYCGL7biiifx8GmF4RnD9mRQiT/R+e7xpLfOia3uAoFw8ds\nrDKAbcY0w3o8OjlRw0hX9UjF6GCBnO2RCoOEpYJRnQgtVLt6rkSrH6Ob5/ba\nd5ZXvzRjczEMbh0N5ihQQlLdL4+czqBSSKZie+Rl+JGzSgGA5XAXsjkoSVpE\nWacng2FmXAmQMtJzq2t69phFrKrMPkED0CLic+//2GH78TZSi3DQoMUYwHDW\nb5sm\r\n=MIra\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDARfE0NrsJCnnAgcwNGQpDu8ptPFDc/P7gcrlaXEXYKQIgKYVMMaUKESbpJsxPP3Ax3C4czGYIVAxEZNKTFd8Js6M="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191011185910_1570820621172_0.50778846894729"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191012135711":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191012135711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7a97c16217101ffe38080c587be0c9d79de81be1","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191012135711","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-NhFR0uNsYcoAfIT1S1MVoM0Gz5oomh7EsQP62+lNx/Vd/zZNyJmaBDbXn4Zl88qU3aQFOJedgG0rSie+GLWUHw==","shasum":"41a214c98950d415711c64678c7405feec3a7f2d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191012135711.tgz","fileCount":1227,"unpackedSize":5511922,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdodxICRA9TVsSAnZWagAA67YP/2UL8iGx36wvYYJV+Kqq\noiLGhERo5J19wmG+2rBG+3PLy9F6zSRnakPsAOwPJsScME87AtvCiJ82FQG6\nNvE1ETfd/STfaWns+jLXzu/gecpjAhhCI7Wjtu1ZZG76GfsWh+e8Xj4MXUb+\njrQUJIqRsTRhvsHQ8b1RtQjUOxlejiful8nq+iiTsOKyJGyNpurzbgRVORxJ\niIOL+0t6I3xuR0BE9iXGKB3IMxjfQ4AQr80PxI2DcTnaINhXO57+dWuFW9Si\nxmMScu7L5+w7DHqS90LOOiFV2piGJS/qQDVO8p1YeJP/l51TDVAwzljP5jF7\nyuyLtwtgfR1BEpkJsNi9c9hQa+anXcA4qLXpZkOAp4s/dot/uknVV6NfDHvV\nY0GY0jRdkkzOZoeroxwQhitpf+blZDDG+N5bQDGKkRN1kidqc9XunxUTiHV+\npruFY75BkPwP0UKlD2lvKzYXVYE7+vqM5ngh5B6YiUJMPRgj0o5SV2sCgo71\nnUxbXg2WHSpiQguUb0YTPqcBiBPQaDE/6VT7olc644NXFYd3M7BPBt18zZN3\n/agcBtQHN6y7p1VY4r5qR7RS3LHDs7fRXC2Wz8V64puk4twErf66lv9+w1bp\nRCIzpwomXl1CUON1sinMgkEcaH5oJWnRN+IbihRJ7FYHGpKGFrip5jbnWnIc\nq1Gt\r\n=81Fp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGgLtB5poH3ndl5NCYxitZS0vdnUfJ7HqV9TgU18/VhPAiEAkfG8NUNRuoSZGwPiMBCW/7NDIBZj8O6IPcbz45sbbd0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191012135711_1570888775740_0.7052391503774718"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191012160056":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191012160056","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d0f832fa4c70b2c77636a0a6d28cdac27cdf0772","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191012160056","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-iYGbW/uFgShkphQ4BTbFDdrXX3LSjSZR69Winu75u6xtg5HsLLaPlQESXN466yOrQ3bgThSIUhhVDk/d1RZ5EQ==","shasum":"0d07d4fefa0077632b17086ece5beddd4ec3c739","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191012160056.tgz","fileCount":1227,"unpackedSize":5511993,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoflLCRA9TVsSAnZWagAAbO8P/3zjmRKUFDy3h7RNgjE5\ndyFyQjEoB3+8vZE2LX23d3U14PFsc3Dk1K5Tj8i5riAncNRwb2qUBWtFQ7n7\nor1qC6VTKaovOYszt+pHuZkIjSkIoX9P/C2m6dZD64hQmpUu8H5SLgpU862y\nXF61b/VTFR4sRNDX8xQA5PPSNkEyyR9V3N3cJ9wlWPVkesHGJi/GT+/HAs8X\niyUuJg0n11Q+7DOXGRLKRlY22/vLSfvZ0lOViOhl1L2whhpd4mxtBgH/WtKb\nqyGA82X6DG/LM0hodF6xC/MSKeEChiUIDyOkTjNF71mNEyTq4fioRbtddHhD\ne9PR6cnCw8rEHTmFy+Ek63y7iFzldH/IqwwbwfKsDvUbw33bb+cAcOXeed5k\nkfnFdwiQH8NLM4fMVX/5YsvfRcuaLIlxBTqi+vaCddprijF4BtG5U5A5YkQU\n2UyzYRXCnCpRXBhkzCyUgwCyuUzUQRLwxt38BcC3FDNMxzJMFoz2wPp+MRID\ntJG5yrDlATh9j7jwibMM7HQooh/JH0NMWo1pHbmXGP8BiGIStzkuwMXtvwHp\nutx+9DTxfMsHZUZh12lhcMVcBTMqS6rJvtPUiBxwDQ5R3lYx5g2o8X2nV0iN\ndFTyCZTo1iy9Xn3XCafxZNbsB3x5YadduGvn2SII9VscfIWnzajoB4IO7VnB\nUlFM\r\n=KLOc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDrQLeqq+OP6WZCrgCTanizTQNjkquvZ73iT6gHDtpXWQIhALbPekcMN4TKQjysVd7REZrFuDVQuia4VSDQK6WDAST4"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191012160056_1570896203152_0.1596745124801664"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191012171147":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191012171147","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3dfe32c970126769ec2ab99d678cf5bfd32a28dd","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191012171147","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-UQS0MUqmaSJYu2qjhc8kmZ1TlUTi9+BqsBDAKt1rFn7uVCPvcZWsYTdEOfTeDdGvQ5N9P/XVZS6tJUHr8Cny8g==","shasum":"0fb3662277f08c41addd8d4c82e3b387c104700c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191012171147.tgz","fileCount":1227,"unpackedSize":5512004,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdognjCRA9TVsSAnZWagAA5RcP/RZHSplFNsdBnUvIwzA+\nASfbtMJvRS81Ewi3KOcj6FQo/ePLhfCTYdPd0ppfTwSP6Euh+zZ8o/1AarV5\nm/P18nL4vtO+vUSEUj4YT+xzIuf+sBHq9PIkDe0t/Nka+RM4j3LMJjN3myTH\nhr35fiKy8dPFvUWHxVIlFf2Eb1aGKe1t72r9679Ac00MKxK9JOYMdYvjjF1Z\n2cm/HA9E6do0f8uI+MCMipQduqg9YpE4RksXvdZldOp8+mit78qqXywNvP3l\nErha6wGJp6+2znKdJ/4zHH3znRQYZpzfCjJ5vNtvvk9XTIn/WN9PrE6aC2Sa\nwVwyxvuRGBCr67BZ3ecsC+53HjU9dsljhhgHYvvWzFNkdomkPuTizjfiiU7v\nPEoiHr+n5p3VBvmFGbJ3yJd6WQW7bdyxZklrvVoeD1IM52jlGfYFkyPK140m\naZu8tSlvEbK+cfKQDvDDLjIvUriLJs7OfnB7FL7U215M33ocZOl+tcxniS9C\nr05dhgmS3ROTw0X8hqzHljvNgpgLLcqnwVH04OV91Dtosq8pqI2JFYforxV1\n6Fzum5cI00ECRExJ8KYmI/ddWpDQwrFySABnr5/PF5o9lk2xAgeb2VxurnNs\n1nshoFgZMUEeHt0DhMqgodLkhWTVQNgi6VG4u4nRh8Uv5a1WOOwgYx13bjCb\nVqO3\r\n=NH6M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGnQLwnwTd9V9L9dDk7Ij9etnIADGCfwbUjjfmhovvymAiAFPWDAKKqR80prn0yKqx3Lym0onH0Hem1eOWW5FLH75A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191012171147_1570900450603_0.26591990803555876"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191012191805":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191012191805","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"63f269d26b15d776e2ab9daa5003f84cec695fac","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191012191805","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-L3fNHPnSvESozEfeKVh6255KUlKTytmuc+XZrhzAlDL/UmjE49+AWs3dsvZo7Y2AUQqCxXb9RhdI+ukLiaomrw==","shasum":"fb593de5106a9a5518e08652d4a5e341a73ed430","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191012191805.tgz","fileCount":1227,"unpackedSize":5512967,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoieACRA9TVsSAnZWagAAcicP/0SlKXd0pxuPVQAqIwp+\nTgnlDrbbCpKVR+a58VQwojX2Oz6R3JvFCRfcEQtFhWDjuq3DiAyofnYFGn3X\n5bZVnAv7fImXAVYIEKp8qP7xRhTeYOnbg9iIpYCO1ooaOVCPDvbH3fFDAkci\n0xPG0JiAB8ukFfYBdo9/Q+U82OIHq3qR6cC6PXCOOYRKDvRH6JyPWsYVe/fr\nGmeMpFVcK8TaiVik6EZynfxRIcELTtEO/b4WHI+WM4GXHXkxCWCu3GcGTrGR\nORdosRZKb7aBG8divpDtVuMtk8TMDksgjitdz721MSd3B3pjkET/SVEWVf30\nna95aYTI19KAuxfpTregAuHOk9bRQkWrnF24cqept/Bz6k2xfO3s7HJkus9V\nyRFMiMqkGIZoA35ydG1rQvORg52m/rbvSAl0EhsO7idUcNOsMgZsdJrEOnVl\n/30NjPnxDDYYC8+pUERjtH26EMV4GbfiKiEhCzpAh1kxKnzxQ5nygP0yoxgB\nklQZd2S60eMs3nkxDvtpVtFJcsgonMWUrjD0XRkgWU0pABCYsToUu4v3valf\n7Nw//4Y+/URlHctgRZIm10+mVfMl4lTyKjOjaEuBojfVF03e2OP9hN63f3MQ\n6HhMTxViLxG12vx4ljj7IND4cwgOxA3pwRmMDlSH73Q8nA3aY/7akuVa51aq\n0xE/\r\n=dZIM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBL5jCBB1eQ/jztsUgCbWP8QmtHQr2NYV2UGxUcmm+EAIhAJ7bsv/siymKaVkkp+eaj0m0YLWEi37mNG/EYZDt/b70"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191012191805_1570908031808_0.0010954253322776886"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191012194127":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191012194127","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6ab2f24e5fd09b2df3a942bfbc4575b3e0b4dfeb","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191012194127","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-dOdZ2DSSocWDPwhdPWQabn+JN7skNJEdrr1kgTQFpBpsSfESI2tyU82qVKCHHW4ZNw/+zLyzzIQhJZzZzGGO9A==","shasum":"75d4340db6f64ff6aea1565d86801b11febe0f96","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191012194127.tgz","fileCount":1227,"unpackedSize":5513077,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoiz5CRA9TVsSAnZWagAA13AP/3LOtv+OvhhvXzTHdrwt\nDFUVeOe+0ASw9qbMh/oq5AGu7CdLf6id1odUaK7IS8mxei0sCcJ+MR1bfywV\naJURm/4BAPeFccfKkTLeU1RDcvJ/iomf5gK5TTH4vwpfR8wF7iMVZrp+9t8a\n79eK4Xfes1U4C5Q57QhaBmJYFBqJq9mgdYIpI+p6Tv5ij19wUdyNLoXvhAgA\nfUQer8JFRMC+ftjSMGyjZ52MBl15JtfWdjGi4sxdV/UdxwUYPQhg8cvDWoPD\nn4AFPUEFT4/NdUSNecP63i+nJpouBjo/8itX3Rusg8u+ZOKCCApIsCgO9iBT\n88lOqmt5Q20jSJ1P/RHjjw+aaO7tAOv1A6gp1YJJvEUw3sb6zqTbCZ+ZQbl9\nKQCXmOQk31pi6SOJR8Y8ujICXG9bLnypD9qIVk47pIV/0g+P508an0z1DQxt\nH2UjrAZY+gCLz2AaG5Kw8JZwXYGVFnjAroqu8LQ1BnmNcZrtPie3NAE43C7Y\nh1cjIFa8yKxvryoFb7dxRG7tHbV250/thf4evyfjqgNIybg1QW0kZWiUdbhN\n7QiWqme65UHoJugBHAxZz/jS6jkPj9JSWc5npRLGVcr8DTh/+E62Mjc6CjgC\nkyWLQjdLd3pqrZNsyRwFWJ0LHCIWZulfXuvbqAoJ4XqdKMNWfj9cH/OBHl+G\nRQYu\r\n=Yk1O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHCQ5FUA0GrKi55PGGvSggWscSxNKuFEvYK5NzWc7dHmAiEA/tH+GTSVP5F5LiMXc/8bXqQqlklFHcLQyl1gYpFoJmQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191012194127_1570909433092_0.5164328538501697"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191013133647":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191013133647","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ae8aa63023174cf01465d382b67a08cd003e9973","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191013133647","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-36mGdOEJDfzVWG5UTDbsSpnU+WQhj5ytBF4LjiHhM3MWSAGhP0pAIuYykG8NIaBMVYc8gUH9Y15I/gQY0X9wGw==","shasum":"78c538a6cdd912296b29c479e4be6cae590d3be2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191013133647.tgz","fileCount":1227,"unpackedSize":5516008,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdoyj7CRA9TVsSAnZWagAAQacP/1wlAH0YFmZHvSWjnWY/\nakhaICElX3BzFqTuN1+vi4k788TrFiel5K0KEUtO00lfuvrSUwpc8Oy0Gc9K\nbB2Zl0vyUOaPhjEn7iCfzNwP+zagxxidteh8M2LOTjiAzwgvUW0a/c4XQK9v\njnOijU7RlmKvJN9A8LgERICZf3DSeRYMtW6p/tqv2dzoiPzc9tkoSQpPQnEU\nf0bjkek3+uvUHrSleSdYUzQ6WzH+vQ0e5X+sAK21qjWaIFOVZ5PZH0yELvUQ\n8OEn5O0R7eYjXKm6d7vDx62AwdDLllidwKEGNOzzBn+KfvI3wnXDeBH25WqF\n9ptO7nyF/rIu2fzqLnOPITUJUlOB9piqhqOo6GfTtMBhlipkGJcYbvA5NO8g\niL6QFQEgsDciN+bQHkMDwvRhU+orWGcYR+DR6h1py9198rYvoon6ugwfc5zg\nJU/5J/UTGiM4S77WeNQRycGrlgF8dLnyn6cJqwkBciifjSFkErzK4S8m0CqQ\naDQsbAj7ZIUqxUoBcCV6/4yh3b+Fsl9bB+OAnX4vgbKXPWK9bsmi30RqtshW\njyHEP0zneuM64pW0/CFLNU2K96ohFwPkXagEkyA/KMj9EBzusi27NC73Akd5\ny6ms6883wJyMyujhgS6NETwY6+p5Q9Nrh3HZMUOyU23ky6BFHqAPmFx8mlxq\nq9t+\r\n=pYHH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEL/Rl3TJd6paPxMh5bYuUi6Q3MG9q3qXoM4JeEqnUpdAiEAq/88Cx/0Y0F/gmMgZO3h+cOBCKStlYNxev9B02BGKsE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191013133647_1570973946741_0.8963966931578535"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191016170727":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191016170727","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"40a008d6208dcfdb5afebe00a2ebe69c7fbdeb39","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191016170727","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-0VKjk9CyCbThSjgx755cspscVGwWqU90pN9dJZ3/qcXqyq1O0rNSahThFKSwwIzHbRkMw7AXMKIjVYMHfs+euA==","shasum":"ace9a940efb246a8c366414b3608945f0bc99593","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191016170727.tgz","fileCount":1227,"unpackedSize":5516002,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdp07uCRA9TVsSAnZWagAAnC0QAJOdpzkhTdWY2C4VyXQa\n+KwLZzTobPCfnQQmi6zk+01x8fQeUmgS6A6SAJEYOGSc6qdc97DboG6Y5FWa\n7ucymjgaUG99Pno/plG2ZmuflujQ4EIRDPO/0PqVbZesYRVpEt8FbPqWWxsP\nK58ligalNWKBXAQApMbMzhuW2hDnAIroHHFyFGLW6LKFpP2nOTqbyOaDg9eR\niWcZ1GVROkoMHnbvRa0sb0hytrljQvaZhkleXjq2hCESCbixCpWFTC3otdGO\nlcXBtzCMKHq60Jc75uQgXRS+n64rC7MFPlroIEefhVJAXm0rdapCe27xp68z\ntmcVsrMbwkFHQAYgnwoBH2bwcdFcq1Ar2NXCA76lo28KCbXWTZHnD3kj5Pwh\nq4XqmxOzs+wSc/5FVc8/IBmqsl1iLy+VQN6OMis+/yPHXtFOiTpFI2/uzO29\nFPTaRMO2N++lyhgmNSY+QW6lxv6sd/pIo90pvG+22pdBu+u66iwUUmt053dI\npXPu2bDV88Dp9AV10laXBlH5y69xRPZpbwN+SLs29seS7j2Zz6FyLAfs9SUK\nKrIIC1TnDmOnmBdsFcsyRZGRtI/imvgjSGTgRdfFMXO7bBMvRfTzKjsZsfcH\nNAiwcKHUhrm6yWUZlz30TlhzFtFER/NLUfP8+Loi9YQAjy7PEVZMyUKaSz0D\nnaSc\r\n=hDBS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFxJoOOrtP24iiqOOy8j2CDPDsQC1br5zduPeHo3eelHAiAB1CQesj8xdcRaxgK12g/Z3Smh2vOEwk9uWN7Y1sfCuw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191016170727_1571245805387_0.5846229410990764"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191016171656":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191016171656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b3d2bbe7436e120d4ab0d9de697ec760415c13bc","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191016171656","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Zpso+JblqpLI/7GPqfkLRuzZqRrfZJ1Pt0u2QMwF3IPqVhAS4O8D2/PlD2OEJUT3PrQ5IvlhX0jjlwbzQwjiNQ==","shasum":"cc0c3e5dff5bc5e64c7ce20241acb4e679086d52","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191016171656.tgz","fileCount":1227,"unpackedSize":5515998,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdp1EYCRA9TVsSAnZWagAAIhkQAI3Z21ObZncnjSrsT1YJ\nejdQkQQl/l3uNi7v/oeFzazByj6rDhb5TOnIwQLDd5PlfpeykTiC+GkGH7ws\neBlQ7gMPhk1aEdoJivwOb0Al8uRiyf0drO5s2h49khs6NYDYb8/S96sNukyX\nsOCs8qDqcdkwgpC1q2gQwN8dlBsPMkQ/OEhdN7m7L/Yy2GbTR+FIBhtKsYGP\nTObLMC3alPNZuI4RvR9vNWa8rUo6V+Wp+uTB1ude30eQTcBjSOPeMu2sG1bc\nHG78D6T8MWfN0y2hzzulxWFXR7qULWyjms87fUSGAlMbi/avL0elaEabtuvJ\nMAL7QWEFNRrJ2uVVigKR9017xOGSV3atdCCoOxSWJ1n8goK4LEC6z2gpD/P5\nQVtwvM3jfYufO6plZPNQuD8POD8l9zBIgDdA61fzeExcj66j96nYgRN7ZAcE\nuaDjAgrybLLRI18EXVPQwaOHk00p6YwIJ/sB7Bz/vA0gM0AMMCRDTV1NSClq\nw8ztxDtyek/jLw+d8/tXMTPGSl7boLyIiT5iIrm/OQzBWcIHp5YnDvLx2Fux\nf5HYVDAhjTTse4fFQKKWVyjd2QbSMckpYue/+2C3+Q4UCurC3E6nbNXCKpnc\nH7X+grNpV/M20Qt1RGw/we4AEEUBx/PFURUN3ei3/m1gmkQHcYG2azed5Kz9\ncdxU\r\n=ok4o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAhKIZcq6R/ueP338fYUaeORfo2VDvR9fgaZLbPPejNkAiEAvXLf2wTld+B3SDYp9N7uV5fGNtfksORDJI11zDMVd+g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191016171656_1571246359910_0.16304157026989996"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191017203928":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191017203928","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c893aa2de58eaf25190fd9500a00faf3b76df17a","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191017203928","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-C5M2Pv6EMl2Pz9zXoJumbAZatcEdo1xPh/Vxw7FSLqGZSl0xvakTIsZ1inSdNELYSTr///IggzKoRPFmV4/7kQ==","shasum":"c31ee7e2c43b816a01eadea0a0f05dc789ef642d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191017203928.tgz","fileCount":1227,"unpackedSize":5516072,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdqNJKCRA9TVsSAnZWagAAyCsP/3ccRdyXb+SawRwBkIUy\nMncYWSR8w93lkny0BOkKFCNPBzSMsGiyJFF/tb6hskouHocFk2Q1M6w3xj6k\n3is/hzNmtBkQeWDj/6Mu2GP8fBdCMTuamEMakmXJCXkoTI1wKAhIdujtDO5J\n+5yTZHLHe/DdVTzzUWlugVaF6F3IMHTVGQSVS22+thOID11CW2QqTfKKnH0d\n51dJ6YZNkmaCIPofLLkEFCb4jbK/iTymd9xAmgYliyAYrgZ8Sr/58XEfDf9U\ngJHKpX7yxwop3HeiP2puDRzG6K1R0Bb/SSU2kWvkdp2f+IzfBiFNMbEnBDe6\noZVwrZgBKpddTK/6+mD5cpOmBU/oEb3D8q5gmuui7LIO0y44syUSGI7aiFtT\nkhl7Ei8m/Qs5F6JhUfbBVVKP9ifiXaG1zI0/GLHzDs/BXi3D1kpbyBse1t26\neIcLHn5xz1DROpu+OAhUcXf5BUeutiMkteS0YAOhNpMnaEMpY2X3WZWW/CcU\nt/iFV6fJvO1egQt9qFBtkD414OBNOGCmsp/ZhhK4pziAGbn2L4ukL8hdRpqZ\nSXZuAq4f9cuUAsJAnQSBl9mY1o/2Af7KiS8OK/jVVaAa225JfU5lfs8oTW3I\nWgy8BFWqN2/GAiGkirk3ujrZOj8nGaYFnxLd/Lq+uss1U50d63eQQMqYI4RA\nzvRQ\r\n=f2uJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPutv7a3cQFExbRNN3AJ3GvyFZ5Z6sR1ANQ7QLtF2XBAiAfy8F7JPpt8YIg9Pch9wTlT1TK5sz8Diczy7xQUgNirQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191017203928_1571344969477_0.5976398374588847"},"_hasShrinkwrap":false},"1.8.0-test-pr.20191018001935":{"name":"@atomist/sdm","version":"1.8.0-test-pr.20191018001935","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fed2b0603af96337e53d7dd3078aad49895b3f77","_id":"@atomist/sdm@1.8.0-test-pr.20191018001935","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-OTZk4t4WZihTaQvmtNN+8pA2q+Aw+THQALk5BSpeaoELEC1g6MkWrcsYbGqua8zaglkKZ4iwTiASeKWwO1uqbA==","shasum":"cc3ce06625a4b56268e973f9bd669caaaf47d125","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-test-pr.20191018001935.tgz","fileCount":1227,"unpackedSize":5516075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdqQXFCRA9TVsSAnZWagAAhREP/0oDMvO5l5Qsom1gq7QB\nRIUSUEgmJbaFGQGqTXfcbI61eY7gNwpYD2j6qwYNUcEGkfFRagUf07RNkXIM\nwiheJoqYoJcyaW+gwykXRjmbAJb4YuWwaj++JMUf7fhO9W+xMqbilAGtrn8M\n0g74kehsTE+9oPuc1bgp2XrdDDiGN/aI1BoBswr334zqndf+T059Z3Xd33Xy\nnzpx00ZNi6BJHYQr239JsPkqZnPXUkuGBDG5KAJ3uKc0qhMJ2Eoyy9xk0cj/\nw4PJBTwGquKR/+7FzUzzB8Pfka5MnpdL5+SMH3k8bJ+nxM378yaf2t9NSn27\n8HK+XFUdp7gjlp3Zc1kjJuyC9MPVe7sDviN1Xeb19Q32F2J1m3i4d5PpFtY4\n4vJbce5eNe1LdpYeKWZSty2X7NWAO4+/v1SBte0XvltonCo8XWc3DqX0q0MI\ntfzZ8dmTgbkuHIoEGYRBnskHrCDDLrzalGZMqZ92Nt4Wgbzk3nSOHgx8KdPV\nSMpt+nBDAR0noAytqwLWs3ejuUo69aORdNeqI7FB/AYIl2YS58BZpc2E0jJm\nD65cTNvRttQWXygzyonGuzU4jDaYj+LW2uzR66jcWwtFnXwOrbM2NanAxDfn\nOaKvTzenaGvkWiI19LidWe5QKlBqnQuC0ElfhQpw7IkBAzEzuyTWWcBHKSit\nBp2B\r\n=RJ7L\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxgcYr0t+Sy+6ELOp8UskYSXdTTOTn4ThgSnJS1H/ZsQIhAIkmlu0JRZwYm1oGMGpYb5XBzUbl0r0mZ9on46Pa/8fl"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-test-pr.20191018001935_1571358148158_0.2940314848922674"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191018002540":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191018002540","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3da192c306681092030d425c8219d8aedd299d57","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191018002540","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-nJEjsMf+9r3aV6hz8dYujIwjOJMTtBSajguO4hkpckWMCip4EyEyjc0c/1mCQGQ3uexQZPdIb+7kqDFVzFXq7w==","shasum":"69f27bb1b182a4377820c06967354374daa0880b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191018002540.tgz","fileCount":1227,"unpackedSize":5516081,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdqQcnCRA9TVsSAnZWagAAaOUP/RLx6mDUj66/y/wfcBlI\nsc35Z50kcMqQNHxLMQb+jCLvtcXA2jXE0vCoyfcsCQqWYhyOBo+q/CUSO8P7\nfYzEDaVz310igcrdtmT5lL2FcfBl+HUJ+gXuHrmF0VgWED84K3svNgv1kZU3\n0fAHPTWQAxK10pro4RRWL5b/Z/uQWfvUMRoaS18pW95tDhq0nuRvKJ+Czrm4\n3FCWVP4c52HIfNKo6fU2zjyipzCy87Rp6wWXZv46AbU1+6Ei3Pc6aSaTMdGw\n4L0ulGyfD2zr9MjMXgJYoZ1kalCbaFy1oL7FmwySRHK6AI7q9xR1ZEGBa1db\n1yOAgAB9usk+pi4DCL2UvD090G7z6FnfSktmP/tHnJ/Gsl4i16mvKNthQqw5\nY6ms0mYZLRj8yiXGkeqh96qqSUqZwcPMtPE9cfeQzrzFPIAM4isypobDi4iq\ncKZJZeSf1xA64/tx4gz5yBJzqZTDthqI//LxPryvJ5/J51EjI855RR4/XBSr\nnqgd3OQWmqqPqB/49bV0xO8tgZIzYAdam/h/CxaOAPisXQ5M1HYIeKvzCVl7\nIm00njHwnyl2owsszAJFp8B59qnkJxHfDVw+8UKVUHVxBRBkuSRO/+dK31aG\nJoZzNGhUsfrFQGlyQA7VH5EIpFaF4WIZ/4rD1XBlXGnb2cCtjsEgMNaZZ4KI\n4Hwi\r\n=buKt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEj5ci8slnu6jtlYgaFM0iwnXaQgDJLBnFnr6UsAcDvoAiEAgKL29zeFMe1Whb8rcIIQxkU61hqLXSxM5SDN8xJRmBU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191018002540_1571358502164_0.7565839267747854"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191022214309":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191022214309","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0fb4c7f5b3effc47de827f86d775c1b51934e5eb","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191022214309","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-YGJ3sWSR0wcXy6fT9RFn8GcrL4yikN3BSMbbFyNmCaTv11ZN9Lw48wj72Txunf1p4Vj5zYCK0EkU+vbXaZIyAg==","shasum":"cad5f69f5cae208735055109ccc37521b62a0abb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191022214309.tgz","fileCount":1232,"unpackedSize":5521199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr3jPCRA9TVsSAnZWagAAco0P/iKMC6SAntlxJMQxq9pp\nsSBlgiwH988Yrxf6S8FmM4Vr1yX5C7looXfmGK49rjiZu5pzWySNeG/Xx2DU\nYw0zsbBj6FChEOD/+wS97uCmlOuh9QzqthHAvIUNOjSlq1ch5LdK8Yj2LW61\n6+DoLMI99daUD9QcrBNLAxfd0k3TbV2JEs0/6BgSKFYvn6v/bCfnXV7OlUpy\nfem5NnIG6R7+Suo6lQJucx7XXkXtQc36JCI2ocuAmF+M+/ANsCP6Jx375U1g\nQ2qhinSMl6cnsQyr3QldAyRlf459UDylVcuJ7uVKlheDuaRWFU2JXDY1oMrf\ny2VNalisIyxE0p0Sl9urrkObcNYc8hqdx5S9tf+2r56DofwguV9cTMJRuTeB\neXG5v+VTSQHtoq1FF1NS5hzzd0IgriiPZUfvXUE18+UwwbhY7eaemQanFbgS\nwT2cB+2rUTtpPKr0Y7TDTYbeDRu0wYpG1oI7k5ZP6b6ko1t01Odn/wnC3TPk\nrcfwjNSNiS1CAJg+vEzIN87H/JiYDUKnIew9B7VAlEjcdPy5vWDfmJuhDf0M\nrkbzBR3JxaB2/24fDV4RbOFv4RZyfEQuW1q1Q6yPjQmWxRZZkmXR6sPaxQkJ\nT6liFWisX1su3bgwSFiceoP59qsiE5VF8QTFmKF9AVN3G/feYAsyqP7A6omE\nZgM0\r\n=Xmi5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCbitcANMjbc61+ZVRJ3KfFC6y2ijn8M9b/J2miY9YkAQIgOVs9dTrCAuVbXve4096YS+HzLi4B3WdYBxG0SRjKQts="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191022214309_1571780814689_0.7318286428973595"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191022221055":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191022221055","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1a04bd812b4189edc0c2cf74fce7df4e824c3df6","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191022221055","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ElG0tXakGnYqWY2vyOE8m8fPM1+r8sZ57eW8wzff+NPxbaTeYA+6heodIIaW2uoFZ6qW1lBDdw7YcmuKzQakGw==","shasum":"054488c3f621d6b59a25d1bad97c0fcdbe4a7498","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191022221055.tgz","fileCount":1232,"unpackedSize":5521212,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr38jCRA9TVsSAnZWagAAuVsP/26HL0kW2LGl5xWF9Xqt\njD1abHJAS06VBEQyNfdwDAGnPu/oqpXtW3uqRXBqSHVHTZ+Wso6lKplTZHJ7\npCeai3HCAbREZZu8y+Saq5Sgprj8l7Maa7rAYn1K+pD3wQLpIYqvia8a3fyt\ngHICUkT3oTNWCRCXNZSG1LoCdZrXJp9lkaVo8tVNYqiNoD7+151CzNbfH8Un\nuR0qVGke6FhrI5x7oeu1mpC7yEuoahGVezLi9MGCN6Zh7RNhiNaMI7J7dMlu\nbLiYuiYGb+xxb+9N6+6aQJj9/1y+oPyOYzBCJ+gQNl7gIF34JMQTPCRl3w1J\nJEp8T6IUjcn4IyWjeST29WvpFTQlNDbkfOOwsKYm9mlG2HpVe9Lj0iJtYhTg\ngA3++2YXuu2f023yXLVqlvN0tZumOMV0DbaIELctkv6ksrmpPv+VB2mBmbkG\nOQ/Q7LDlumMiXBWzsvFVySGU4vNTNBOxtP+KQzTsD2BVeq35Y5DvTZf7kBmm\nhDnkpZ9j2vTgEqnqFjN3QNRyRsb9cHfdG+1xJcBxO2iYIdngxTemEj17xskB\n57Unb/uYs0DXKZnzt9/+hkJjTDVCx/WhgNWINz88/PxLJ7UGMjvpS3I8XHWb\nGJusN9MueJ5IPa1zmHEDhLI6wzT5oW0GjoyLVUZNJtv6G5qAHbNQ5tBGAOfd\n6wYL\r\n=46pF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDfyRUBiye2lyxHY/Mt6yZP22x5zZYFf7DSWWw56meK9AIge714D24bmogh/Fxv0UbUroZa9HiN7Zxelg+2Crw4rsM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191022221055_1571782434988_0.32671480307750245"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191022230656":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191022230656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ceb65ed96eb7586ddb851681c03b12f995460aac","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191022230656","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-43l3WeyPrRtUZaqYwL9QODKZf/wEQTqCgoQaMfccdSx7R1zUdSgwXt0bt7l4uI+oR4p+Mtn99g+imy2dZkw0dg==","shasum":"2a3ed9d1384b03a76d98622e2521ce02a912c744","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191022230656.tgz","fileCount":1232,"unpackedSize":5522832,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr44uCRA9TVsSAnZWagAADbsP/2DM76m96yi8pnkhiUMD\n6xsrjnXdy5ABdyFGKXeJf6g0OeGEOkdyV4CsIeD6kLlSoImaqiKMk9eekX22\nrpvzna9tsbyQBeJcMPJdXrys3UzhZN8mSu+WwOI1qBUgbX4UA7f+UVQ1q71D\nf867BuR9jtx8VOs/ow6CBwC7X1UknW1FL6hIdle2EXvrd3Hgmy6GOWAdVJdN\nRAeWtSNi2QIdBGfUEJ9s9S81lD8MaZSBGr2IexWAfHS81ZOJfo2WBpBkn0+W\nFGl27HuK9uXz0wWe1pwbbuztxkFvjTAO1lB1ilyR0ifN0ccl6d2dvVyoeem3\nrTxR7tDZhrYGfktc/TVoOvT+sIULCWDZFLabF70hY8HRIWAv5yNRTTl6GsHR\nr0ipMEtQCBEkN4Yi15MPqAlsP1jNjf7Ji0oZ7TxV2kQKtDxKuB1J1J2zIEZh\ndShdidp2uCni3oXcn4C4wey8xSb8IbgiT+4YP4NBVjbQqWjtHsfnMD9qjr6N\nMxcuHAfv8j+cZ/u/EsJtGJMsX390+mNUPo7rhBWlxprZqp8M7DPmN+gwHwCO\nkhaTC6TgbBKsnaqDEyRByNUp813OEwaqT39aDWOZL5u1rhmlqmZSaPdBNrVF\nCnhZmjGVkFvhWkWa65Ke8SFttGckxFjtBTo1hIq5bUbMYFxXI1UQ09eeLmMG\nkMrP\r\n=pAlf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEVApwNU/ehpAUEM/2hYrgdADtqEk8hy+Z5PNyVHluzYAiACjiPnHMcgnxzwBR0LDJttrfUr4cALTyqrorPAn6YRTg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191022230656_1571786285510_0.036946730734731315"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191023001720":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191023001720","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1508273edbe293e0085bcda32f1ee9ba6c573b30","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191023001720","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ZgnUi1oeONJISS+06tZPdqMakQWy8rU84w/Rmonj8pbsyVOHSLtvmnT87vLWhghHRc5p2qHZRZRZ52WUOALsDQ==","shasum":"d0ed505bc4c2c0d217b8c7a05fe69f3805cbe2b8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191023001720.tgz","fileCount":1232,"unpackedSize":5523618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdr50BCRA9TVsSAnZWagAAeQAP/0lS/5jU26+BquhCmJ3X\nTd+tL0WBV3VCFx55I0w02aaLca4YxlMjppeKEpGtvtOQf7mnrqGTdwCNljt2\nRapkc8u+ZEaYRGyJ71b3Pqozw6m2jFh/BMpL57JTHzSn4iaZr/CWzqPdO2MU\nyWwEbQPyp3VamR5N9Bk4m8Si99Ea4h6kCTWqYFRiFqlptEp6nBn+wrKrqo05\nD1fM8pnZzNFAGmnoOsYJ4R+iAK0NqneiycqOCJU0zqfiaFfgxuJ4hXGEQ4ww\nKvZSRUSuC9m/e8lr/U4Bg7QRdVLsre1u0PuCk1kMQUGuXsplirUK13j0Cf+4\nvWBv/zlNdr0+nOlc3kvMW165kX/jR19AQgT5FOaZ7pVnyrqs2z9hPgfHnML8\nhFBK6Vo9aP9R3AQsDRrgGfClB5K3FxoxENqJOIPbMqpm0CHGLSD/aGHJ+9xU\n+9LKXoqkzzcbsqh32AzhyuG6J1/Uq9aW2rvMI8aUaYhiqSSeEM+g2zOhj7uL\nok+MYx9UGRQ5XPOFI73gEkANWiXYrdMuy9j8OgOmatIcgHMqlxE2stN+7xmO\nN8JDzCGLr2OUwdUylv2/0q9qsB34399JAWuWcHZ2POfFWWCPKG6BRBzu+Vz3\ntlvP2AjSenWMi11DAsAEEHRrCSoxjTFw0BSOgxZK5FodGKEnAcUtr3/xcNws\nDPhg\r\n=ZBmy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBnPBQ7ErIW7yEnAhXJZD3SNxg5aI58V7I9VS1CA2eXvAiBo7ypuY2Leq68vGu2nnrf1iKdNfq+a5NU0zcMmk9rJ3Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191023001720_1571790080601_0.4478710984010852"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191023164640":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191023164640","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a79d3c7c1912f60aeffd1d1aa60857c6ad0aa3c1","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191023164640","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-vbd/3PV7WRIc1p1Kbt4TGvIY7vjURVtQYrT5DHGoPdAapy94As+LsgiLSDLR/X6Sw0hEJi3lWrjHVpGtWDg7Jg==","shasum":"bf171f8b1a0558e38aac7941c5d2d74c306f61ce","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191023164640.tgz","fileCount":1232,"unpackedSize":5524151,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsITeCRA9TVsSAnZWagAAOaEQAKEPwyjn1YxzdktjAl7Y\nwLAAdFMPbA0+qRMhC5sJ+pmZuB7dOXz/5xVuNnyQXuuBNZsgUrNleKtvYg4j\n9TjYJPja+v0bKRtpMj5Y6Sa51FKhmVNUQoJ7u6yCeSAcusb5VAli47BvbfdZ\nv54UAaVTE3U5QGVD/QVO6s+g5Dl0ecpsmp684mhx5UJrmTmO2NpNrJN0+bBk\n0hfuPTWsAwq7KLYfza2vybG8lJ4lO4vzRiy8RY3MOGGjWKui63dpdGtNaaiL\nBF89pO4Ozt5+qD+s/kLyawDy/rHG8wYVzldibfHPcKfAr/R5bBilnJseUpxA\nblTxzSKespVgsXYk0ZzeyZMJ69qlr722NMqOmJwg8p8W+opjiqHVMf5aJ7NE\nEMq+C6puco+xi526/PkGFbeZ0PmjDpBXZkMwA4EHygtwy4+j0iHD/9E0m7+3\nkXmqaoEbW9+GEZC6QLW5tlECRTL5XdjypKQtuE9mfihmGjjUQBk4f+u34D/8\nu37BOt77C4bsHEOpMCU4DbtXM8uCUmkS27TAfFvFDRs8erp6xB2Siii4v0sb\nr/qV/z+tV9rVGEuVkkXY5lBIc2y7RUK0HfmDt2njyPi/M2COzi8UMFkoSzq3\nfG0EDQlLUaDdd8/sp+vi2HXq8M2ptTez50vA3nJiDZ62S30z/livsdqRGi44\nEjKO\r\n=KXyp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDL8yneq15fHQ1TmVSH24fsJ9jF4N3nmW5bMjcWThg9fAiA1eCcV0FaJqITeBhwO86MUybsdgE66IffFkuaL4OZYiw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191023164640_1571849438094_0.977865878537475"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191023194056":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191023194056","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"06f8e009f4e113c89bb3bf6aa4c3f960c92257e3","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191023194056","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-VGWPsLwBeM8xVF+taGcCBYdf+ifytZPH1Vfr2+E+4he3hQ2n62bmiTd+iXgpI0+tQrBU2Qfmh/hBVUsfThqpCw==","shasum":"2672aab056ee33fb1c2c1bcefd1ca347f5937354","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191023194056.tgz","fileCount":1232,"unpackedSize":5524343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsK3oCRA9TVsSAnZWagAAzqAP/1Xb7g8K+XuORi8FcKy2\njoqTRmIJMp5TyU0Z0Qi0C1Oh6JznxEBnBrW0hdtFarmKFVjo6uEGwlwX8qxN\nPiNTjAplIxU6k86vLVswph7z4Uz57YmkEEfwrEhATcfuQ1HztuT//nlUhL7a\n4oAhLRoCEahD8GX0gju4WT2Iam6o0lEp4+rJEUyfhQycDfaWMK9gKY1SIIwb\nOWurBE5bwbRxPmbtQvgTlHRb9GmRFCLF16iHX68FK6560rI+qZD5TvfQIIMA\n4SBjbmPh2TQi4XkiNa6F1o9kXrtQ9sAXhFXa4PdyrZWCX6qHnr5mLdnEnjLS\nTYnN9nrUlKDMVN269SqLpidvIOkTlvWD92HHHQPIr9YcE0xZ9RtZqe3r2+IO\nqK2/c5QLqpF2+ma/UjhbuE8t8NdYbZM61HdRTOLPrSFXFQ1cAqcZehWncv0g\nr2Y/7XuVsyM4Gk5Kik40hEQXUenW7rtZzqrjR7v2L0WirBNoUqJxs3Hqjrxp\nL5UVyXyuGrKOL7BF4JIvc0hW5vQkp60+4uM2aWWCyMQtDtxPaC+AuywiRggt\nBW7jgNqIU64FEiQ26/k9poHCLJ9EpVoHcJvtuFeMq4e3EbdGJu6ms+uNf5VG\nO3yCR5oIiV+sN3pEB+VsY0ZBS3tgZ7s+UrOwOnbM+l0EMHgv6JHzjDfUOIoU\nKUgR\r\n=vo1/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZH9u4hn/48iMJ7XbY/qQillcvLIZQxxwXSTZO8iXvMAIhAN7zijGWuyN8YE8kMEPwmOPa6FDfycKn3GeBGBW/y6AT"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191023194056_1571859943991_0.049889088527383496"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191023194052":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191023194052","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"06f8e009f4e113c89bb3bf6aa4c3f960c92257e3","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191023194052","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-GDbqTlZQ6vFiQ8MVgzDeOaDcmj09pyQcmtnUNYNl58Im35t9g0Cy+uu07hDdtn+5sjsH3sfY8fYaO4gCNslqRg==","shasum":"a0cbb29d5fec3cbdda8005d2fbb6d037fc221ca4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191023194052.tgz","fileCount":1232,"unpackedSize":5524343,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsK4LCRA9TVsSAnZWagAATUkP/iNTU56fOS7T2uDf6H8K\nl6sDbNd44segueWIRbWfzEuTWvYhDct7RRhRwkWJeKd+jHIsW9UTSRFd8xTa\n9xFjRbZIk40Xosa8+9nbSelzSPHp/v3Q3pdK9vJ1R9bFuoyoMzXcczNRlBHM\nOf6HMy6TjEJcab8A2cNu4hCTsjRqPT3sZ+skeRRTFVpvhHbZ5JRdtb2IaCUn\nlIecuGCXRcR8ppyRKdzuiovvz3GuK5kOP7DConDuoRGeRT4ikEwYfGvHEoB5\nENLx6HAegH/pbXMk6+sNNV+OoN7tEhLFPlpgak0E2NOYyjbShQiiTvMGzG9W\ncarHcF2I8huu1fRnkC6W4BT/1z5ynVqwaD2UB+3NLot3aCPAc92OzYPV9/Dd\nQZsw3qgu68r3AKzqOYTg/OrkY1bOpK9zVvLpp1W48U1MvPlBC0xjZwao2+ML\nMY1luyb0h0Lna9VWB2lqrHvOhGmj8udsFcZoNYU8mShgW9dthPYENIyWne0t\nITmUdmRefFOd4/KmD77Ss9+KI70CVxyfq1nHuLwbuLFDW/+7ttkyxWayJeBk\nNFPCPtdmIOiWZRJPiZ33mHp7+8YoWLBxi8QVEuJztP59JqlfP/L8Mi7Hil3Y\nIt3ndTd2nYtMtd1pzhW9u5YEL3j/EBcuPJKV91jNHwwRB8RBjMTafBcy1nI5\nrZwI\r\n=95qa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA0PGhw18CFCxfaAI5rigcVG0ck4C5HTKvCOVwn0W3EUAiEAyNm9Ss/PXKZiOJUB6WG0en7QakdfqcGTYnAUHhLevx4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191023194052_1571859979471_0.22224238960031406"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191023214836":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191023214836","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"081152526d2d314d9a5e19d14add38db0a653008","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191023214836","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-cz3PgbwLXTC4Qw1TwmlvMDZXrMBesFwCZCz4PJMZCMyMk6ocrpMEUzrd0p+atQvGrDtluanaDUJOPwJEsoiKcA==","shasum":"4613c79750611e1ae4f1909f8bb949edbdb8c25e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191023214836.tgz","fileCount":1232,"unpackedSize":5525696,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdsM9TCRA9TVsSAnZWagAA2eUQAIHDOddEAsGC6CN2XDO+\ngkWIQX6AGNzmtQmJwiOVaRp2clNsi8FZl26pLKOKMzM7n1wCG1CERlf2DLKT\n5cr4Im+XKkkb3GKLlSSPSKkKFEiVJfq+zF3iaJ4VTXoRUDz/4F+uK0d5t2/3\nPrZWS59PiTuOUxi/ucVk+npueuf+qUTAqAMq/Zeb9ymcUAyVrbOkY+0/+M17\nO7Eovnl+s/NmKAvfIZB247RYcpixQDktfXK/7qqHRbGXrM3WcX3fsm4hy/9P\nsEGDesRTI0IbDpB69WAJaZTIFIrzwa6P6e5XxPIqyEL/DqZ7Pqk1r2Fy6S6U\nkWFLMdxEud3E3Ohf1RmbrWReSi1BEasdKTX5bn2S6eXUSUDDSYqhY5tEnrlz\nzWqxzJsFK8rJa399A6yH+3Jxj7TrgceJ+M4jXdPnD71RonhwfV8vZSZtbtif\nMDx2wDXx6aFx9uCvnukr/OXUPnawPwODwlUJvlyAolK9D3nc293TauHs5Cyz\nmGyAIYhIPfBrzCOKiYtc0xoD2IkkRHqC93o+vOUcDqUqxGL/DA7SFaQs1AoO\nldpQvPY30CMYjjpYPRvdvlkYyodYBTD3BDOxLPQiNmBMOJnxrpIftKupHdYf\nIfIK1O9/OqSB1pXNG5WlIWaqIGB1FV9eaG1ZjkgMTy5w7QA4qo9euSGcC9qE\nvZ+4\r\n=pqY6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDxEsRJLg9drasnUQYoxa+xtCk0sVMrgrAqnY73SbtU4wIgDDcVN9EifGwx2bDP2AtqukaLrUyLlt1j3NrEY1+YwJ8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191023214836_1571868499045_0.3194774563584408"},"_hasShrinkwrap":false},"1.8.0-execute-goal-result.20191029193519":{"name":"@atomist/sdm","version":"1.8.0-execute-goal-result.20191029193519","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ac1a99be9f81b63f6ca25997aecd11e933d3523b","_id":"@atomist/sdm@1.8.0-execute-goal-result.20191029193519","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-5sHBphfI1WANhyGRJaOiwvcfalZYeGe42eQbQfW2O37epRBixVDgjjQgwM2QXvlndd+mQ7lAucDtF68GVwS5dg==","shasum":"a67b0ae3e46d13c968a144c633295f8ee05c4f49","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-execute-goal-result.20191029193519.tgz","fileCount":1227,"unpackedSize":5089471,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJduJUZCRA9TVsSAnZWagAA828P/jpjOSwLHoY+srOIeOvf\nbmiL8eoCncYqVlP3z6hoPt0pd+z0AObjrfljeYKAYgKubTx2AQdwWClorTl2\nwXaB6cH8EWH5W2j1RWZoXUydUc0oPbIZR0clRZqOX6QrYBr5qEivExBK/ur/\nD35D4RXCiLOwXVpCpN6AjTfkHIqs6jk4ltjKzLm0yV81v7y/D3aAhE7xx2Ct\nWBz1QWEKNEY6tBIQi0aRLkoE3Y2j2P+6VvPrxQ1DenDIkICWxkyiifm2RHii\nlNP8MN9XyVO/r42r+P7p0CDCgg7TLys+vd+P7BMHIOJroGQzilOaGOhrbhJt\nFPi4k09PSFIgcgjstOCR5HBhzZcrwWNBTDSV3PXLDoYGU9gUygIns3N+dg/G\nHGyPbhltSjd2QsRrN8F+eMCLpOaNGsbQQcjqAP4GZtCoj0ym1aQVJlM0OPMh\n66oLJePAJYLbNDSvqAGXF3+U42oIug+ZyvFQyM1LclX4NLO0f5JSbe92+s2j\nS/obOyGWkQrirTn8VLzwuFxnCmWKaQrt6Yv3RwM2cFxDZCJVc89MjpDCpM2s\n1Q2xedIuQc6iQHJvWnvSCGjJoekfsxI/U+tbxjMcWixSC8q1Kh3j3hM26eAv\nIxOBRcGkNBFw+ttrNUyLb+3EGRUvbaTfJQwAzg5lkCeeqeoPmyHsVCwXuDYV\nDd/L\r\n=dUCI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC0kA3q7Sx6LmgAV2O94+aRIMPXqaKX0cPSJEsFxc6odgIgJ0lbn/LdoPI6l5pIv1cEVqJdI7Fwh0j/ZvJXTjfmVJE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-execute-goal-result.20191029193519_1572377880383_0.5422329787543909"},"_hasShrinkwrap":false},"1.8.0-master.20191101163456":{"name":"@atomist/sdm","version":"1.8.0-master.20191101163456","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d08d320d10e992a9af20ccccae227bcf5a08a9dc","_id":"@atomist/sdm@1.8.0-master.20191101163456","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tunTtndpNBy3ukbIPXNJOyHkdPr71SgpScHYd7OD9bhYH9V2xK9uzNjBPuf3JVDwk0Wp95TqS4BXh0j1r+YgcQ==","shasum":"41772147d7678813aa92e918a007956046cfe9d6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191101163456.tgz","fileCount":1227,"unpackedSize":5089549,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvF9aCRA9TVsSAnZWagAA0BEQAJrMg88clVt9+ssg2gM/\nPeZeQbmCuUXCcRu/RqeaHcr+GsjZ5jOWjPVeb2U9uUMidE630GDSAbHPjbgS\nrBrFNSEVsstqvrT1i4+TU8+Le5lxaALEMJi+lYvgP0Y17vbR/btgnm2z+LyF\npmRJeW2Yq8s5hEeR3nVYWmHASYEnP2KM83VPmX5gL7DYshaF3WlBzomd/ZKl\nNSsTbDvlroyl4ngbOTy0/xdJI/bXpT4cNExbPRantOhdGCU7BFvYdxVGGF+s\naBIXLqBQftG9CEvycTrNmzwCZTVlMIZYWeM1t6uw0dxYuO/8sDB1QutiRgQD\n3tVpAGJJm+CucmRlqyjjHSWGYc9NwOrI1WFk9ctqbgEUXn+YKlvAurfVMCJr\n0/EVOSNNcj63NiLmgjRJnOQHgHT3atk3qQqZo8D1qf7JDUHM3boGWleY2jhI\nU6DLIQnqymTQP84IczYP9NEdlI35wCWcNPCuSXgBHQkKvC6mZ4N6UQaRutut\nh+GvWYJViF/Qkc264l5I4TpSF/8wIKemx1D2M+OWQTgFTejEbPqKnpyaTH85\n+9jPWetpL9jL6xV5jh8gXHGXR1wPO7+Nf4D/J/36/Qwhcl8io2i03XPqtbAR\nttf3+JNMIZJeCUfwUxhjyirjIsa3CDvN5Eo2aRINCp6HXYJ11PwTTFjZykua\n1eg1\r\n=z2hR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGAFstnDApGTJxT6V0e7bkCOIweDMrzTldVkoIs/Y1BTAiEAq7QuseiC9LWFArJfb9reCJ8nREbxCgmscMjGouBhNwI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191101163456_1572626266030_0.9947829953253675"},"_hasShrinkwrap":false},"1.8.0-master.20191102181820":{"name":"@atomist/sdm","version":"1.8.0-master.20191102181820","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1c5cf694e8f5332eed88833086c661cc7901cefc","_id":"@atomist/sdm@1.8.0-master.20191102181820","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-xD7uQuu05YfzB6WiT6LyrdhoQRBqS/kyJ4ukQRNH2eQBmYDYpf0FbUD4H3R53WVujO9RFBwxVztT+LFatfq+4w==","shasum":"a141130aa638b43e28239c32580debd2f101ba5c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191102181820.tgz","fileCount":1227,"unpackedSize":5090677,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvcjoCRA9TVsSAnZWagAADsIP/ihtv5M1N9k+OA3qO5rZ\nvtF95xfNuN8mbHxJYS5qtHuIgXUbpAIEytjd1TgGFba9cXArPh0UCnu/h+qY\nWh6hLkSsd05YjytjpmFMSNwEsUs+D//eHr+GeZbSFJCWe2tcx5vRQf2+9n50\nVxRAyWIyfrqh7ylmFcXbFahE2nGZKhICRBWMgzJT0ynADlBy+K1Z2tO4X4ow\nfSzEgBLQ1iTaiol+H6unhxmmvdotFHgr1HzuuEPdZCQiZUzw9jiRK3DivNkT\nr3OT1JVJErksPvvVWl36iWrNO64qAGvQjY9esF1WLXCH4dtFdAOBfTId5LtY\nmhxwAg+JAuSHBvm9n2ZdP/BDjVM1S7BV26EZCCbmEXdlEH45oT9jPO0Y0Xc0\nuTE6jkUxbRStQ6z9V+hCI3sRpDVpeIUo7adR0DbiHEQiOBBsKFoyRD6JmHHt\nbRmOrTno3GN6bUxOVPL69AdsdjPm9NXxqmKX2rp/KiAq5Y1oez6/vgIVtPNz\nmIpR2o6tlUprywISAgGP663lGxwSyXg/CeEHdWELzmXuhxgDZRP2/bxC+kd0\nFqXJKqwEMzJwWiIQvCgPqkDPBQPOs2tk4GMq1Eaw3auuuU8gYrl3lwRfmDzE\nPNdgMzkTBnhdFtKlfZ+i1J9tzxLHxqKJzYQZLRZPdtJ82S2G9bbLgT94Ytvy\nRbv9\r\n=xpug\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCHkgy8o4QXBMU8EN8e7OQ/rKI25QYP+zNb1+JccghMhwIgEGgUIT+mkfknIQJlTdmDI1lQs2e9vAGMrFQWySFJqOA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191102181820_1572718823793_0.3067419815211714"},"_hasShrinkwrap":false},"1.8.0-master.20191102193548":{"name":"@atomist/sdm","version":"1.8.0-master.20191102193548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6abd86718671f22d9add72271bee3fedeeaecdbd","_id":"@atomist/sdm@1.8.0-master.20191102193548","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-8PQ/yJxFewWBAISDfbLQ7PHsvNwOPEHsyS+vSfoJSVNr6CeWQFBwfyFYLQjkhppq931R/dN7PDQ+WdTXiQlqVQ==","shasum":"cba80f629bfda15a8f620fff6ddd5629259ca7a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191102193548.tgz","fileCount":1227,"unpackedSize":5090668,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvdsOCRA9TVsSAnZWagAAVD4P/AjCZ6jtDRdAIgay1B/h\nwvq2Np/AHbilT/HwskDJ2ZvwIW/ZDf6aF1SE2vdHfelG8vzdtqfL2OT1xLAX\n/1YIuIe9zcmVpcNoqwnFymSsjsPvuGYx6xpE91CtS3pWgB8OHGU3faoMxXM9\nNW8U/K0miK8l5gxDJKlq6omkrfnpMv2jXSxlTegh0ULtNJWtPrBYoZUdFHH+\nl9fsfmRxmc/BX6nvjCee2dgl6DqEbJUZo22FPv4KnzdmspD107xaqsC/R2bG\nXC8kB7z3FQUT8K3ntsEyK7BZcgxwr6CoXq6wqlhpD6y2b+u1cNQ1Y+0r43au\n/MPLrC5zxunnh/CxMgSLa2m3aZ8ww/kCdkKX0c08/HFkytACgy830u5wXNr1\nLzCMVwxLm34tpsEs1nQcDCdif4/wNB8U18MUV5ERy5GaBak3xE0STKiRNK+L\n0+WH9mHLfssxCuR5aJbuMur6ohDc8VwTa966Uovsm2IID0syRsczNPvCEBaL\n8OhoPOaR8GY/rXv3loAYHTjkLyncsnA+Xw5OUpLHiBolf4FCTe3musBeaaT3\n3oxT2CFxtcRW0vwZcqMUPVOpulDzqMyL7LcaFjAkcIa7A5guLMIJWTArCGh1\n4O9ckUKjWfOp9PnQ+BzvQQshoNd2gmP6QT6/cp1bSMIzSWbuEfi1oYAINYYD\n7wkG\r\n=IBul\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCoZvdhLhezSuj1tYcGVWXEK+jVO7oYrSHDZqN2THZKRAIgUc86FvSZn4/64wysTu6IHHK1HcRRnmaXKKdClhSNfZw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191102193548_1572723470316_0.606973027985197"},"_hasShrinkwrap":false},"1.8.0-master.20191102194220":{"name":"@atomist/sdm","version":"1.8.0-master.20191102194220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6e6935761d4b145fe8f3ba5e5e3089b5d7347e58","_id":"@atomist/sdm@1.8.0-master.20191102194220","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-8Y2Z5KiU9ESDrq8HCJjFBHbjjr4fihwqwE4gowh3L7+YpfqsEQSg7dTz6Fv8OWRyRPsG0EI7SAx42P+9HMBjSg==","shasum":"128c678160d79b092ce6d4ddb9095154ca1c5106","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191102194220.tgz","fileCount":1227,"unpackedSize":5090726,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvdyYCRA9TVsSAnZWagAAuncQAJ7RAQIsJv2pGXX3U0d9\nh31kBsciraCzEJhmJscxTm5Yi5SfwP3iKHzxGKBQ2ZpykLfnVYJf25mSouIC\nCj6DPYiFGEflQhoBsglr26wdWZwcZ3ZvMDeinW3XoxbONMh9n23ZW/dr8QFG\nPJMnb3veLEanZ4LhGMlTvu3AeEr/u9wrMg2u2ovObcxO2Z7aTvK6AsUyzZhr\n0qdSO9rsPgH0NzQDsjSwz7+IzwDYznJWyMakayaXkbHsGlxXDu/Q5BRMC1+6\nfmEZE78fpJ74RWdYrj/Ju33AAPqNo3Kjb3bMEacWZfm1doZaA37ukphzAXZh\n3tCmlXslprf4NWZuZ/ahHkvAtVxkPQ59kbH2njoqmBJitUqvi1jeiBDQgn4J\nSm4+wVM5STe3j2v94eteqrcUzhj2SqOb73wk18yfbFRUYexkDdeXCcfAV6lF\nlrec3HaVR/Dw6ACHEU5kxG7P6qjtT7p+FYVKR45WbbxP7EWEjlz+1IUEaj9Y\n0wp55KzKTiGpKoQLM4mbPtyIjgSDkHA+UXDjgWnFWGfdwP/5udLtK1UoltMx\nCt28DCnUmHO3OeYeWmwTBYqIRSjAQj3kLJ17ZKAOhB9ZX+Znp/cWG3jSeJmp\n3eUeFuw4f5gIYrIhMvUOEbNxKPTNSz81uUS+XqT8C0FjcLVnBobTgRkzP8WA\nZl5T\r\n=L3ly\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDzT1qRHXto0MF4XaLxsfbAipZWvxgg4a6Do/pNI0aK6AiBpa2umMowrYAajH5PrwuNjC6S2l/Qz8iOYqaeJyBs6LQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191102194220_1572723863564_0.21508765379618278"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103135949":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103135949","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cc4e6acfb386fae55c4fdd37a2627f9b00343e38","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103135949","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-WCpDDVz4jLvaUHqcVe/KZrcdPcU/TqOgs7ID04HlLmtFlZptHyaLIeS0Nd5boEh1IA6F8YrNjRfXBbwQ5YnpxQ==","shasum":"b7606d3240f57ba28ad570b7c190dd0fd6ffd1f8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103135949.tgz","fileCount":1232,"unpackedSize":5526356,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvt4JCRA9TVsSAnZWagAAsaEP/0CALAcE9WbwwAUvx4cN\nE4onX3s6ulhpbKDpP4/pqOuFNfH4eozVTXz2Mh4l/XJtew2Hm+xoGdoWo64v\nrrFclrRIBI7v25Bjpk9mQllmExHUvZ0K7jivEeVjj2quO+g1aMRjXl1Bg/o2\nx5yJsPI2pZXdDCB7H/+ObGFjTQdYe7y8el7TXPlOrthHxvKQ9jADfm6AIahl\nA9N1mD3fmf316oNG3bxeOaJGWOCaMtQql7n6xHgC9ILk2FUFdWkI2+h7TP9W\nn2i9eaRc0NcHUiZ3UbdmaCa09J81ixCcpfBoFUNsUFkYrpWOxQUV7GXmzxI1\nW9S1m2yNSE0N31IrmkM50MVtKGZCB17DYBRzKWQqe/eAy+FMCpZgoZ0Cb+hx\n8+SzFAIj8Ef4diUiPfza1uPXk+IWwZX/7KtdLP1Q1TzTQXJQ5VyLfLMuswlT\ncIYVrV7+g8FxoLu6Z5f93OQHONq8gh5CPhYxjduBKv6+Vb2yldM9cfQ4H3uh\nHDvhGeOct9P+JKSX8MVtqLosja59kQm82WfZm7xxCxPBLwsmlHCkIfAeppkL\ntEUKaGaNaOT3bqYOtaAoXhEK/gmMiuZBJn/9Ts1wCgK8OeQ35YpvUa/Hrf3l\nP4Lxm1cOzd6AwZIoA5dHSoPIv6JVKgrjvlNkPL91gcH+TjWlUZeTmdqDp3+p\nbOnX\r\n=3S9X\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDnNiwaR/zmwzbwpL5USEEQpPpcUG4ZY1AytaP4QHUqwAiAE+x7K0KVv0IeaUoKkT4Gsd1yrylmGL4odF0C3APYlhA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103135949_1572789768894_0.8147818584859872"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103140112":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103140112","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cc4e6acfb386fae55c4fdd37a2627f9b00343e38","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103140112","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-eY9YmpjAI4QnnbYQ+dsHBj8uG6/oOp5ScsBQFRsjRpe7Q60FQ7N6uCRyFROaL7O7IkPb5kGbMHRtmDvUhnR/Xg==","shasum":"2f791c17e8d9639b5035c7f23c7340d7b360f2db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103140112.tgz","fileCount":1232,"unpackedSize":5526356,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvt5BCRA9TVsSAnZWagAAohcP/R8qK/25NSTGDT4zylgM\nYtbc6pk1T7WfZsahlBC4g40xNiRpp18VYvuNtG4oGFDGHso4Bp5xVYxCYBAB\nkSjDxqmVprsjzP0B9NljqWTqVw+STv5wNQOq5Gnz0HDU7ky0fb2IM2eqUsDw\nRE7qF6+5eNO9Vzd/QqLS0q2HVKWbOzcTSGloesCC17cC6Qq3ILxGgFklt95j\nTxJrWs+boiDD63R3x4n3zvjjezUcBzWj6ULf/DHrUgmQC0U/Pu26E6NwuffQ\nRQX6MXeIJ6dvc22xOJ4JiwX5HreTtSbNrDu3VUfMAgDdK9zmkK3FQWYPxo+I\nDPiCwmdpb3DNqXiOSF1kuC6lhW8/3cXVK4wd4WootqsuU5ddUd+mhGia4zyi\nSp9sH2uNfj89ngS8lplOpOD/DaU+P2fg89tl2ofsvCpkFx0c/gXz/zEdsX9Q\n0sjBQhwR3S+9r101wy/a7h/mjgA7uD/5Lcomd8R5ghcAlQn0enP/+RUaXpMz\nV0umHUAYOrfw3fa4OddlDa+tR7huySLyGwjt0fs+CK9F7dlsUG79OT16qXfM\nYlfGeUu4cB33fFyOGXOI01YKmRJLWB8qtgi76dZ0H9lLzrdxwRMNd0XZJ9AY\nitqY0A5KzJlnj1yniFpihanqHpNm0MRD90FR3TA/uo/aq8n2i4LoEEC/3k2i\nmBl9\r\n=nOE+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC43mVN1vffyYp0WK6FGbx29f3ooSK7ioZTkQ/u2seFzwIgaE5GJTBILcrOSwRMx60utCK7JL1atF3o+cn7CYbTUF4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103140112_1572789825129_0.7450035819941259"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103141020":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103141020","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.7.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011094832","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js{,.map}}\" \"{lib,test}/**/*.{d.ts,js{,.map}}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.1.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f2453641f95918982f922acaf338c7ea31efa670","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103141020","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-0Z6dpZ3MsPYzf8KkVopgDsGbJjkWZNYRWG1I1zQ4ydTq3a7BnG3U/x20QtncXw03sVyTRX8LpDK3LAliDu7/eQ==","shasum":"2adc0553f6fae3e423a593e6d30cd18f42c3fa7e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103141020.tgz","fileCount":1232,"unpackedSize":5526193,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvuBuCRA9TVsSAnZWagAAQWsP/iIqirTQhhEUPFtyhjL3\nV4G0IJb58Hblyx3h4axwByc2rm8Vewpf9EVTUnT+PXS7TkM0l74R509S/F2X\nqNyop3Amsc1xKNLFPpHZoeDqhNECGdTC/K2SCBwijwp0t8UPBLiJY4cEtOb0\nwtqfCOMuoSv4nWIMPmwT7EqKB+8UT+ffc0J6kot5T9pfwkG1jp6JMOiu3Rya\nkPZ9p4GAfMYTTkD8AwnRukq+yXrAhiRlGl/wil0/y7rz3SMWCMyfzT+LmDzX\n4udm5vhdGfduHf0sznoh2X5ntZYWJNA/K2qGoGwfvqaOLAvqglTe8s392UCS\ngiLFFMpl9iIN6hZ/JM43GQ4ZQCNzN9A24PDplWwZObHkBWy9CG616eNNunbC\nklxrAP5rojWnQG4DyYfoNWThVAmOKy3JIqHVyJDBegQFM6hONkBkjbe6shOk\nmD91XA7KBj7wmgWXE+amakkmZRErQEkQA9CfAVkEibtv95tKwxo1G1UpleSM\nGn5IcdOZ5DKQFK+GTIAzYkeOp0tvRCXvazoyL3+jpADwh8tyEqz5CeOkMLMl\n5CPcUPa4oWOzFHJbjdL7FAzK1dzkJeYMPId18l+HYcXrIxsqssxq0Edn2oZ8\nHGaSMOsaVSBxgmBGwEgSJSQW1IKRIZw+c9smVsfxAEs1Qdi5Mla70O9ilR3Q\nQo5z\r\n=1EaB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE4Ed9AIZC0yBtloM2r2z5yB6kq0cmnBdDBPb7oSmFAWAiAxvoIRd2Gt3pqHLus9MgijCWkqp4lRY6vDCDWB6i4TRA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103141020_1572790381420_0.5027475753565147"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103163255":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103163255","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"68493ed9c9bdd01dd08f5e37af4e03b9a393744c","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103163255","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-HBu7UKsNfyemUnBufQvkVVhp3UfsBFbAoXHkMBrqeFl7C+pkahZlGU9P+AEu06Hy0VO5CdldJK8veOFfuUtk1Q==","shasum":"530ec3c7a51503d5c05544cc7a190062897ef2ed","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103163255.tgz","fileCount":1232,"unpackedSize":5523099,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvwHWCRA9TVsSAnZWagAAZ/QP/jA36wgs72EU5RZh+Jet\noa6DejZB1RoagghYXpHZs0REUmE9bPsvFgBH41CZHjuoOSXjsZdLVmJz0/nW\nzqHuJIi3l9Ltk7+SHaCmKdAxVt4XH6+wBNkQUKQyRvhHH19uUwDpHJ5HBwve\n1k7MLdiLzlgWWnyeXEY1UvJW/kyl1O+tGUuu/o40mTnThowalXC34alQQTjC\nYnQcNClDrzZ4jPjHfBo1UBhN6Bdmnc8rR8vYCNVyjr4C8JqIaHkvPQaTAct9\nmITRPppqVCtOeHKUjw0s1nNwqQOIq04kO7n3YTuFnD1q2HH31QFQoavDWL5s\nfFfE1FYE4sw7iFSLvKEp4nPj1mKVLFnsJP1vB2fWX9WANwlqzlTF+Bj+e+3I\n4RNEFMQSPgElSuKLsS/AHb0vILzlYEnKTJm5OkCfYvZI8pg4C4IjKxHCZ15/\nBIioXtRDTXFXcPFjEDKkPn518jsziNzLyRIdwlVfSKJFKE7+Y5zlzuyLNuw5\nCbXC4gCBTVu1DrbJ4yrCXKQTX/roQ15dQdcnyngjaO89WPfdZTT4/VyHZc6K\n89Z8qpDtu0YzESM7QYcJVjU0RSWCDp0DDGdE5UdWHF1uhxlHNFZ2BYe+aZL2\nXbu69ijpUpuVt/eIlnsoZnmXQCdjvEz/Me6Xb7mNcQQdkEwVNl43HxxSLqB9\nNZ/5\r\n=Ufp4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICvReMKSJfo9WidXr98ubYZhU2I7rHf2+C2805Wr4FHuAiAdftezG5MYqNs9k78edOzWcAzT0nVGVr+/23e9TtJ9qQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103163255_1572798933321_0.268806546131918"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103163724":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103163724","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cafb3b07fbf10575da907e61026127a930b4bceb","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103163724","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-XwqVZrOP4e/A6jOEr3erZZT4NW7W6LKUGIzriRoRr4VDNETPmVVttADDespv7B9465kyLaii7/+DM1R8vaEAXg==","shasum":"f71b111622dc233714fd500db1f9789af3ab237c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103163724.tgz","fileCount":1232,"unpackedSize":5523641,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvwLLCRA9TVsSAnZWagAAw5AP/iah93VKROoHOZucAFeD\nOfow6AFzEqb8e8HNBlUdus9shn8+zPCokAAIAhmq6/MpkllLR+LlNjkla/hb\nA4PA1x9nRGVPRKRaCTwYHYV3Dg474mywJU4PjVy4O8cwT4XtbewqPF0AM+N9\naTkhM0Pd4E656LDfW79AwRW3BowI3UWX9VnFyuylQKt6N+jsH9ivwP7ynq13\naUq/xEnu71zLt+OT7hEdaxC8AJBv528Y/II38frq+WVFIZQLb2DMrZen/OfR\nBqBXMuhcYoa4zWiQ8RGl2d+JrFphLiH6fwLQcFWvwFexsD1Ft/L+fNiNdxUg\n/16iE3bpJkv3pg7s1ffAV6yIk0Ck8Wmx6qJ++VsQKssO23jMfUMrgD/7Qy/k\nwujI/pXyuIWMsEBwFDNHgggqxWA341aIzxHTZLQOY//kqz0BwaoB2R7BK7n5\nIDRrx4NLizxz7DTHuMzrK/uGYQ56jXuKknI/D1+/rs5w9JRTTQIi/S7vqicn\nGfQnAeyOD/R0BGYsfuMY1KDJPUsYtSX/3E7pawlEuAGyB2rB6khaCrJ3yndj\nTlujf7wYZ8WfepDkDxDHXtcKcGMp3AKmegbvM7grD1Uj2NBR4vmZeq8SaPW3\ncSiCyPIzZJnMsFWQunL7IxK7dB5ce/82KwT2we+kLiyKi/6OvGtqivOLKvJD\ncZhQ\r\n=8cFt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG8QLVuYR0ZHc5HjoblqdfEp0kLQbJ7THg3tiDn2ISKyAiAIf93IdfmDwtWLGJWEWVuVmgq0a0Vpy6rx0sk+SzuGJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103163724_1572799178980_0.6855365385333396"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103175627":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103175627","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2f229736f4ef5cc6ad027256fdc6102d2bd30c89","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103175627","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Gnfk/QAbEZcuxJbY169zQjCkmMFYZjE7zFIgG/UryWOLV7VkvZ9C2mff+SfqQLYLTO6p/mxg3mE4pUFbEuuDfw==","shasum":"ac8c5f870a133276c4964fba4ef6002de5a81486","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103175627.tgz","fileCount":1232,"unpackedSize":5523427,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvxVyCRA9TVsSAnZWagAA+jYP/AqEC+GlSmBMJkGy6pdG\njbIQVJMjNpW+/xsO3ZcayXqy7jFo+TC+q2Awe9nWvi7Ug4s3mXjA6qO/tRSE\nDELAckerI0vWq+c9J0I8ziFAfB8xs+EJlYQTFnqNDs6HcA1nQ1Wxu0g95InW\n968NmU4l/4/amj0rwHjOiHz+FYugEeCAHkPB7bj+9HvZyU3YOLsZfM6oIykk\nX+wTQ3IuU1jEWXZ5rGLmdAlZHSNVFdXpc9dvw+r7JVhe6/fXyCXDug95XFn+\nS93+95o7PC2xzQBw0OSw1sONX7N9cVeTmwtxz4P/d77wGAdzSUwHrVLe3BLp\nmVtTLMBRR2/F2ZWVXhwFv+xshtbE5OpNDWLWvBvkQpTIA+DekMBA4xp5UAvk\n4fdmbncGXou0ciReexnjgts7qgpqg2s07uAuAvxMwfzCN9FdA6En4kQRqUIv\nfpyYcrcJMS3kRuzL7lyxhR+/mgBn8HFk+btfsiKZId2X4HCFRNQnZC+Yh+PW\njnVMjTgBCYbO67YA1sfeGM/NL5vYTOoLRPzp1tr6eYVyL6fLMYmUBzca1q5j\nOVU3d036qCzi4DBljRLHsm5JUgbYqzNohefBXQ6h0Lmz3bA6YNHC1oS92lYQ\nbOEU1OAGbaZEoLDZ7WImso04M75p7Wawgi/JRmIatCdWwJFTwYExhllPka6N\nZFY/\r\n=DlNh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGEphH08dr88z+lGKQapHOAR+EI78jAdn9Mr4yr+uKe2AiEAxE+dvf2+QPfMCMPmCQewd7fSSf+UyQ8Wy64a5kjyZeY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103175627_1572803953392_0.06401175541743487"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103180327":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103180327","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1eb529ecf42cbdf00c2d83951f3a637a314f213e","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103180327","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-fBeBr5P5ImHMYIyn35eKMCDHzDq8DtQE6CGwV4+fIRyHdc+xFZCZaJvEoOqHAth3neC2zC62aIxEr6xEmi/1Dw==","shasum":"857d6cbbefb6207c3748d70489692298fad8beab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103180327.tgz","fileCount":1232,"unpackedSize":5524119,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvxcACRA9TVsSAnZWagAAZwoP/jtRHxKcFNuNFee0DXoH\nCEFRXUhwxIXqd+BjGktm++cHbiYyiZZJaGln6l/2EV+P2s2SgOr+U9mzAjcd\nMVE3lwn97+OIxq41Ha1JXWw7+mwinaI2OVlORRoqG+HfzP4l1FSjaQpsaqnI\nAKABjNRS7KPUNh7ilyh6yg91fJcztWt1UtS4pB7djMWdWdhATcQM0BtgrpEQ\nS0hCfkDSghnT4GzvKkl+ct+ywy3cMmWmP5YzgS+qm/2RdvG3O1/OqbJOWPJS\n4/C4nqB9zCL4AhbDitBhj7bPuh7HgAT4FIOo1E1DXX+DYPh5s68fmznTkWbR\nfkzvzYxV6tD6AV4Z82by8iKyQMp/oaP6qqSKhUbNbll5c8y8NZf8oaxNVTVd\nOFAwsrw5Uo5vcx5wYLneN3u/u0k2pu2Mx+9jpM7tfNnw6oaE76EFlxSyU97F\n1OQIBUOMaGxRrQN0H4GHY52XCIgOpN+mz5r5syRf8eoU9XqsKe1Mk6ntPIR+\noI+ob0ipIskz3s8l5jQYX9AkPL4sP9+nwdR1wtPagpnYA3Bl8MDThVsUNP3C\n06DXBD+74hLUBTYBEv9t/vmO6L0xLm2RfFoS0hiIHv4MkbTWRK7jE3VBQV2h\nJeMfJx7anouLXaPQnFPeAX8cdtGOCJHgPfbZPrse8K9Jok2NxIBZkC6cU6Hs\nU63t\r\n=IMbN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGWvkNYbx9nYV4yoO6fuQykv/x47Bc9IMqEwyVWuYEq/AiEA9TzFsLgCMnU1DcPgXWu2CVBXF/8nTVUQGTGNnDzvBEA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103180327_1572804351536_0.1933739757848516"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191103181155":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191103181155","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4e7169534c076b2e2cf8700b2073439ff583fec0","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191103181155","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-AryDVhuiI32+RHr7/h/+X2LSkHIkdj0GezL7h2RH1avjRrwtva5KKU/r219NwnSRYhvbOPz/KZIGpgo9o9czEA==","shasum":"faa89340d26311c5ad500e07b7186daea74220a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191103181155.tgz","fileCount":1232,"unpackedSize":5524468,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdvxkECRA9TVsSAnZWagAAZcAP/Rc8fOgBhwzr6T7XSoyp\npBO4oHJZHx+GVITq2EmaM9D+D7iTJS3KFVOxzRiU2/D3srZqWp8uFq4wvPGb\ngVhuVKqDBHy40BdCl0gS7LgMQE6LYDsdhKtcGDn5OiDc3LpzTI5CADci96zk\nETzI+1AEUDtVjM4hAnI9qnQEDbiub97D8cAjFvCs2T84Z/9GI6IVP0C/6qgh\nL/ITcfSlJZ7HTCm6Lq45r483H2CHHfy/np+AtJQR/2FiTQGyawUe6Z79Gp3N\nd5sCSsxYAPjN64JMlpJT4AFCFuaf4KOU8WWLvL3ITsgj5T4QwTKi2GGcyHcr\nsN0/UQJBfeS0AY89G2yKLCifxGZimAY8W4sBh3/ACpG9uascRUTtv5Nq0ZSd\n6YVZBsYSeONCnB2mqLJRSC4mRRPUhMVvCBgSlwG9hA5Pgeef1J5LdWXbE1jt\n3ChjVqGmAd0AF5gbNUIHfDs1l+T6J8JLMILSKcI5Cgb7y1YgpFHz3iPumGTO\nY8QspR8Kr78ubHBGrIreBEAR8IJJDAom+hP19CCkvdYfY5FwFMU3L2YQby1t\nDHs6KWv6K/kWtnRPwuig/CzHCjvSrItLT3lNIHOptm93/vKnOvwQVyKtpMH9\nMoppISO5MLwacSgnj3NVQO7uHp2HWHIAQN5u7KCZET/t/vKSiD6JTy+yMkJh\nck6Q\r\n=ZC98\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCFTxn7MpmwSpP5xycKH4Mj5bzzOw1RDxGQmpROOPS5XAIhAPVyuyIwz7uj4wPQ8qmn3+dxsl5vqjKPr+KGEg3qFQ0A"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191103181155_1572804868343_0.22713505390112587"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191106115038":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191106115038","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fd5290b8e36cae449fdf386aeaa22bb7dc3853d0","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191106115038","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-eMNhW7cMRqpdOHMeuoQvpIcL+uiUzgSLveMN0uuScrAG1u4u8yy/UoU1ht/u1qXIi8QvGsgbILu70fPsWTsDww==","shasum":"28ee7b50ffa66fe3871304dcf254259933e1cbef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191106115038.tgz","fileCount":1232,"unpackedSize":5527402,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwrQcCRA9TVsSAnZWagAAQY0P/0j08sYPV3E8TBIv5Py6\nxC8IPtysCMv/Z26haQB626H2H9ipgjTZIHm1CF/AFxS20xdE3Cs+hDu/2iRI\nThbCGq3hsVcOgICLIOMSwmfaGbCzjYXqUTyV2YK+BEWzjF2+GLIOQE+YPLaV\n7iUHeEx+KQH2RD7l9/nuyoLoJNxO68xQyVLAOHrlezJxdp9+SMuBz9Oqj1Hs\n+zGEk19zEPqTE1Oeta3HbceDIF0HwLCDKsfrSK/Qx5dTt4IkCk7im6DgOiHV\nS2yLl5h0yCs8bq0nDyuyCV4ebflHBEGE/n5jfgg6MkbMsoHDDSyzBDe+NgRc\n5VHDgbCChyxYtX694mkBUdH1NJ8GMH3aeB+uCluFuKxJ5xIO+P2EOGQRRJiv\nkrGQGV+C9iJzbjtLJJCSZGqGXE/ELDVvzzcAmWb9MAUiWHXTsbaZwgIVHrx9\nN5ZKwxvMuF8qDEMuvKc2YYBJN1KSDnCxtuOToWjvFH9DKpIRb/YA+JtqvP4a\nVodLTaxyEYOzMooGYo6jOzpxiaG7z1iegOte0JFiui7MmJjnixwFfMp3pVuT\nqoAh+RoRwvH2nDBJsxO31nlGK2RcEJPilXXdChgq+zgDB+mf/wz3DEWVtwLS\nIoAVRbqNV8oX8m8r3Ekrx1xeHwZh5xBHhVe9wZaT0r81p9XNc750XBNeEuIp\n7Vzy\r\n=rXf7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID/TphOXIZdGCKT1Uojof3HyYl2VVnxnoGIDyyMhdx+EAiBZeTZ80QFUWMio/sLtiaqnLCYJxoRwTgRa/RbfOw59fg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191106115038_1573041180200_0.372848182045451"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191106130028":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191106130028","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0e0507b424ac9400f889c6f70805a6ee7d93dab7","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191106130028","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-GgRDpvqcdXp2GlonarYdXkxnI6O0RQPOO7j/qc+4Iuy2oGXEhk2mj+CeBKba6IT39ejcjMOZGeAPpHGdoMHAYg==","shasum":"079bf545273e0fb335c413544eae37004933ddef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191106130028.tgz","fileCount":1232,"unpackedSize":5527386,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwsSBCRA9TVsSAnZWagAAl2sP/jyIsiABh2iYQN9hJLKq\nzqow+lg3cYeEB+tLH1fjxItyvaHGSQhSjT8wVSm0Ehk7kjBVYSUVaG17/ZYg\n7bp4rJHG2BRkEXrEtHQZ0RjS1dtd3RYaUgMc5y5JTDspc6gaCFfv1kNRBTxS\njcwnYIxY2vDCEUUAXA2oRIxGy51uzbyWW8ezRcQty9Kw0Y15pT5dj3Jp5PuI\nKWpxE44Qj82j7Sq1MPC6e/6rYVMTEAvyVd1YOUEPqj9vxNWS48/uOF45Jtx3\nSIkV0O+rbRA6u6R6UXTOR/+fx7cWn1wfwvJK3/TyTJQ6MNEiOM5xeHDsuP9c\nUY5mHb+Mosr7kIR+Pjee+IllUbozFataqFxxmefawhvJ52zqVysqYWXXQMSw\nYI6kPxMcA3SMKQkfzZtGGFXZSTv3Do7XYhxboAIxpKFnVy2Sww0ls2dJGc4a\nKS0QylHH8J0C0J9fb8/eQgolhWH16ptvwU+r0EC3EEYa0aHHuQjcx6QiZ4Oc\nV7QunFaw4RVrE0aOKUvCwrLp5j0S876OY0nx//Gb9NkcrD4KxskLqMFlskDs\nOgoFJ+Nt6kDnRRMyNon5v0Gtlgap+oazlEFrDrcNBQOaSzqJBP8Y2tXaU3DT\n4VOFlY/RZnGu853WQXnRKIoBb2Nnuabfi/mNMr3HHLyBNw8dbzuKj2LxZY4t\nlee0\r\n=8xQc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKM8evIIu4XFxcD2XZEFQitD8Q+SrwawIBwpz8rCSyHAIgZj6gDoWFiM7ensIs0F00sJj+w30ss+N7omifNlkoJZc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191106130028_1573045376624_0.9751013371021051"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191106170334":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191106170334","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d6beb9a34ca4a6ed3291ae3b707e5c39eafda28d","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191106170334","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-AEaqsYLGmNYoVejcEDte2/bRH8hPBY/T+5L1E7qvdlp/TpKJJVXRTQjNeX5QXzUCD6ND9SNVsnj2dk2FkxHNIQ==","shasum":"8c8838883a9c76734bdae9825f5262d89e7414d7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191106170334.tgz","fileCount":1232,"unpackedSize":5527388,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdwv2lCRA9TVsSAnZWagAADZgQAJowxya179xXcv56jMeY\nk4b417qD3jKmgpAncz+zKjvsK7lljQRcOd161t8SQDeELvp960Kz173wDswl\nQWGKn55gzFtucuPLJETFANbNes5vxm2cTDM2XB9ZyH7wmg8vr6tTbUe0J/o2\n1Y+YaQZeyumYDp4i0FSWL6+PdxQa/LLhzArgl+peZ7KycK2Qy7V8NnwQEay/\nVoC/b5Cq11Ej5yTcL7ZMy9WxtriKE13ivHrc1fh6u8mIRDyipV6VipBOCICm\ng8+UgIVtvXl4Owq6wmuckaHe3z9jfLCDJ4v5E6b2N6jPm2oA05xKdwZqu/W4\nqjk9Hf0iIChDjxhabSR6Xi4/ihnl8Tp1pU/1TDORlGKMJizIgdZuGzBfFQie\n+IKu7ILAqYvToGYi0xyVf8NRSwVUjrJ38+8Wfz62fk+6pUlEWhozUPquL2hX\nPd51k+IIjBpw1/cPOjCHNSJBerhY2SQNxydADNFn3ppWXtF9iVbvVGyBZcWw\nqKkrj5wXHUaddxbRdxpUdd+9wxU3vvhUa+P8A004s8BIk8Q+Poo+y6VsuliL\ncAQqnggwJ14XagCh26RYArrVDB9fESeqWvTtecWlFhq/YkizVLS4qo9OnzP8\numsJebu019gsqgqiP1DgeORh4EbSn15QLQdM2fVTtNBIr04uYsNJbeM9AGbg\n95a9\r\n=OtSr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGI4SLcZ71pzCZZbSf19QT13thZ6OUXWbg3vHcwOEZ3YAiEA81fJUImVq+m+hMcE0gnzOfqoc4KaNa7sx/FwYDlRzZE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191106170334_1573060004773_0.5312698144913095"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191106181220":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191106181220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"671c1262b3e8acd96f71ad3ded3a1bdea6b378e1","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191106181220","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-k+X3gplvUKCCRo+nLnoBK7Bxm8HBmnS3KCvKjzmm7y2viC9g1JR1zu8hl0JvfDZvWLGdQ0qOLSbOSWiGKNK+iw==","shasum":"8ffb499a419ca4148262d02e93c5f5c61c38a40e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191106181220.tgz","fileCount":1232,"unpackedSize":5527421,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdww4YCRA9TVsSAnZWagAAQa8P/iaOV2d1FDSNxgcYqgrY\nYjDnhmRdslOYc/GdGV75paoTMNYlWN/BkBIAae6WJhvOeoVyeYUAo15qP7yO\npUhi+xyGd5pmHQfgxz15LX/MNjzAQQT8fhUEmgwL6A5mBcJGYBSM3XYWiIoO\nShDCtsSX13thWwHS7eay1lJFHnFpYrQ4IQinK9BrF9tflMxkabMpldA7/zPL\n9j/tw0eHk7VzS2KDTLh360rkNXF8FkxHdDO0EmTC0dyzPbZZwH5J+mYOHRbh\nOpVdqYQG//LLrOoxnt2Q1jqFDnDzjqZRHgouczW2gL3ks4q6BYeobMZbM6hj\numfEQyh0lIz+8DqEDJpkLQwq1V5vzvr9XW3xVYWVSVYUVHI3RLnOPIlrLjgt\nYk6BCKc1BxA2ulnyRVfREe8byVcFeUYpxkapthWU4PcGlPMlmkTR8fisT17h\nOGiKuENnuRz7zNkTq4m/4KlrBjdcO3vSPPnAfVv0O8qt2no9584ugjFUGbS4\njMuvDcfMwh2FNp4gmlTJ7vTD3GWPxrf/8M1S/YGegB89GvZnQwwyWxkcHcRm\nmTk5Qo4aXjdG2MzQzbUeyz+FdafEkEdAHSDJ7zpPDknMJKx/M2kSLrPrV7rt\nbmkLbWNfYAfRX0RHC0qRgKyS48oH4vttfPA+gLG0NRIJzxohQkhREW3xFMHS\nTFhe\r\n=jo8p\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDjAEW+3SntbPJzjA+UxyofXjidmzSJyfrZF4WtITtQ2AIgLt/hsnx7VL4PayUUK6bnOtrDVHqquoGbDockrludj+o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191106181220_1573064215550_0.014371096766923275"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191107230956":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191107230956","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"dfb4d491088b054a5c2000ae001294a857c7a69c","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191107230956","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-T/ZVMxEV9rciLOBrLjZdQ2RaZqGNmh8+BvTEoYo1hR64dZnz1RooEDg/1A0/EbO/qET6yya+e5e69Y5t0KzWhw==","shasum":"18a58d31e3c0c8a3a537ee446fefc6112ecdd7fc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191107230956.tgz","fileCount":1232,"unpackedSize":5527419,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxKVfCRA9TVsSAnZWagAAkTcP/3omTRoDeI/jT4WTR5vi\nYSC5lzxRwNV3TJ9X9kk8SBGmwr3RvX0F7cHY6CN9wZd5yPiAzBQ+hGtUDBCW\n0hoe+EZsnmbZmvPZCf3DMXyLk6MGm0+j3g2AnuTJyHMuqGeSmJzdD9c2aBt4\n/M/sajQ9zVxI7kS5W6VhknhJf3b+VoQ8HRfPP6HU3T5RbaKh2jyy3ocOZhXU\nrWU1AK0XtIZwzX8pPUOy+fYZnCXrGfxb2UwZNIkM+3CqsB7G9zddPi9ZY8G7\nKmmcry6gLRl9orWiOX1rrBdjl0dk0FBmW+ytevo/1Xm7GsrgRY85Z+yGaoI5\nB+wavIZ8sBgJtwigm2pHEaFwqYCHzFciNQUwAuvkwJogrDzBDEX3roKkQKDr\n1UHTT5e6zZMWsdiZrCxAqvupd+sECuVGppkEzXn3tAoThMqH/db4xirb5PVn\nInI43uqHHufqBBKV+OG6PAVmRfw0U6gc3a9vDg3jWbGIlIwNKcRjPgoP7oRI\nbLH7nmZkn1zXUlinuoAIJXwCJr9bdbJw133K5aNp1rIZaZ7YO/UlkgVRsqV9\nopH2RS457qJp0n5/kblPOzSN9nH5iU/+wFqhQS0/wAcCKJiZ4EJ6TH5rEYPp\nKuzfA//1hyxPvtVaXW7+lUylWjYG5Fv+1GZv4CZcfCEypgfV4QtFpLxvXA+S\ndmLT\r\n=jebU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFrAP9PRDRYkYD9fvDL3ko9l2X5C+1lPeVad6EHRod/8AiEAx5EI1aJbOVrKKadel4STnXD/+gkktsfz0ri/cxQZQnw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191107230956_1573168478927_0.266710907489351"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191108140247":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191108140247","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9612faa0ac33209a8875e2c5c7b5a0923e1ec953","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191108140247","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-+V2aZhRl+QKZV/mE6/+NSRo1eEpES2cgpwXVHhjbGznUjErlhd4OHTWcUjIoTb887YjTjlEOLTTowvS89ZqZCA==","shasum":"73893d09f9a0caecef8e288ae0f5fcaf045e47a0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191108140247.tgz","fileCount":1232,"unpackedSize":5527812,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxXZ5CRA9TVsSAnZWagAA2yUP/R03sYbTrg6/JKadm24k\nNjJ9d8ehP5/eD2fed+JYc+5o4bGylabfHvMMZbUTKAEJ9ZZoND53g6RRWFkq\nEeQKYvfXEWsl55QFY40b9k+Fzf3O1TpLuwSbGraNEg2LUJAS6dPYjHDeEMac\nOlwWtugPi5yqexUVYzv7N3Wbxb5P02xkfd/4U+CtjRk2wYhkuonOgMq+DjpO\nm2P2adIYA4DwADa2abd0/mSzaqPx3xs6ARtAzYf9e5/8bp0AbTbK+9jLImJT\nE9cNOv1TZaxjL1ZESTF1wAerHoYDHT2oCSIMBxUHsepq5SnAfUh47khiTv/D\nvauYMevEjJ91ZQeTMzsIZaOE3mCd68U4tJferYI8QsGfsByUVAs13hQroZgJ\nycf/dpBEGgxkC+LgkbFRvxKvOEnrkuQ8+GGBs5POc/BZRN/0IgXPNUEDtlF8\nNBNVLy3Y+grzENf3J6jw5vrvjqUg9ZmB4HrnaKNSrz+AVjsZ61fjnduk3UfB\nhrN1jlnp0AOpb+nDNBmLadjGXV64lNShrna99fX0BFzw90J8V8PflmEYrxhU\nWBnQHbc75wZG3Zk/IBcP2lblFRv94TwDx5eBfEGzIxKU++r0+gPp6RS6nTnX\n7lSFNXxdxcWNHJ0zECHfvEsz6mgZUILKRJ/oV27Gys9E1IFFtIFuNOlXT6pf\nz2IF\r\n=BXJE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB1ggDBJ9y6BlujQTfWBDGi8ry5JYKP35bfiL5vYIxtZAiBMf41b8e+RTwdh/RsnOw3meD8ARPHw/u6LWHdSMEmsDA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191108140247_1573222009067_0.7148791651036617"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191109122945":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191109122945","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"34c6fc43e4def4f7646dbb8bde1cfe19a0063283","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191109122945","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-b2UARitqAfpvCp+m4sS3OwjqVSCtng/+DbROJlgP3iVu6RopzdXvBUA+FFCt4f2wrQGOCYaQhpESL+c9QBn/7Q==","shasum":"e6861004fcfbbbb0d98c9f5b28a69692522ea9df","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191109122945.tgz","fileCount":1232,"unpackedSize":5528488,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdxrHGCRA9TVsSAnZWagAAGbsP/Rg8kI8uW4Q7oVxqQMaJ\nP9RkgpUB9kjz7SUDbuqY11vXwvmA4eWp9MhymKjdpMshgmHLTu2rAE4vNhoy\nN76RpXxOmJgiH9EdwUHiJhrVIsJPUloPdCsbj6T+qJzYceJwq1+6f4nwkITj\ndylyMHtw2Ny2XmMbS/CqPE2d/uSvaMqF5wKhNTm1wATensHCpDAbiLH6tz3M\nF63v0QPsSLcrf9GzUEtvbIUm2a5P9iM6UiRwhuaY0cUhJsJDHOuVf7mzJ5n3\nzk1q1CcS46fNrj5awIQkdEwrsBDg5CxxZTbWX6QNIQ5/NAkD7vEr9Rr1++uw\nxjlKjrEFLavMhl3GRc2OTjrHnKMSzfdM7z3BBFjVTZ2uNSH7+T3QkqztLVxQ\nr8Kaplg22xlmtVcXG614FxqsGuKSHZ5Zi7xyY33oJYkHY/FxUtSlvqBN+6tJ\nOhb5Lqe88Co3WdOMjcqVCWpK2e6o/WeuM+gO5mY8tjBVCOmY3a2rgFcTw+mN\nFJ3YoYrF1ooPOdZGJ7n+nXhs6Hf4dPGpUtkfTSovQb+jXK+DNpwXtEp+Iqt5\n8ByUIXOVfhdy5XDMcsxm7kI/tiws/HrutxRqkpbgFhTeOX2RsvwfW6hCve+x\nMzH1QOvdLAUcDTnZDYAYwgM0cJ9wmv8ge81T7OMzWLeINUG4VnBk/k7w2V5J\nml6s\r\n=5TCL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH7jMJUsSe7kz5jwp3ZcJ4COq4z4q9OPU8wz9VxzWNbCAiEA/5TXy7BPviuVl2MzEim2aabAfmlNFdN+pOTLgqHO5tw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191109122945_1573302726125_0.46720802963304786"},"_hasShrinkwrap":false},"1.8.0-master.20191111173823":{"name":"@atomist/sdm","version":"1.8.0-master.20191111173823","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4e1f336f105d92cd582164679b3963de4cbe0210","_id":"@atomist/sdm@1.8.0-master.20191111173823","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-YljEPLL1VdrSZhVmIHjOIheweGMqDlkjG0yOXNXYG/mkjQHfbNTGn5gVMCLWJHNvznudN90oaH+KcMwijaUrNw==","shasum":"bf5709d947f005fc4307a650b27f661361f6e4e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191111173823.tgz","fileCount":1229,"unpackedSize":5542387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyZ19CRA9TVsSAnZWagAANncQAJ89pNL0BjuxaAP1dlo/\nQF0LWl4pks4rFkftH807JVCaYr9DRWAESyVcdf0Ig++ZIw3ZwvCdQ35qjT3s\nxSSRZqqrHoX0DrtfEMaJL/HZn1vRZQCHGtRT6dflobu0hxsTgEs7n6eLZFxX\nGTA9XIM7cKLm0pU1G/HveEmejvrcB2gZ3hYntPL/RixNbfAZ+cN5O3tIpoS3\n1B0cVnKespIyPZW4cjvPDW4hJnyMdjULJgGT8y3oW36hPY2xjdJgASOFNM0m\njo8+AJqQmp/TQtQgR7yT3PLcG4Qg5ikmkqituU3T3hb9Eg0yWAt+r75u3Uox\ncb0TDHghSDX76q4FPsaFv0DgBtSRhnRhFVn/Xo/SiDm/kKvmjqy2b/ERzXLE\nP4tu987OBkTBQdk679n5tWEvqjXB/P62Xho3VV4w/JXY5mJ02CKra9X/5Wn1\nJAUNAnS7vS6ctX3A1Dd39TVcpd2LfsPoL8LDulhwq5FnSs7+kvzWKa89cvvH\nVzpkpA0VmUip64ww2ONs+JKR4mCJ0ZxpE2vL+bNPeYm8OpXVcDS2nivAxYX+\nZ/bDljCDzLKP3CuYqyRGv6sqgoaJnvFO3rdyHiFND2sfL16nTyH3Y20bXEkf\nc6yqVfM2gD+lYP/FdrPImvPJt9g9TpkkiA5uGynrhFfEYwrWQjZ0pFTkj1mK\nE1Iw\r\n=lUG9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYs+69BZoDOCS3/lF4whrsu5FTLLg8xFk85FjAi797cQIgYKA8lQltlowiGQ0tTf9xQAAhwbLpIevkNNKztuglh6c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191111173823_1573494140752_0.8784320726198944"},"_hasShrinkwrap":false},"1.8.0-master.20191111174038":{"name":"@atomist/sdm","version":"1.8.0-master.20191111174038","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4e1f336f105d92cd582164679b3963de4cbe0210","_id":"@atomist/sdm@1.8.0-master.20191111174038","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-LKe93mU7Xn3jW98bKcWIALsJyYHEXooUecpUkN4jmt6mVH2QcIvg7KjOkC4iXxW2foB48aM6ALW/jY150xgQGw==","shasum":"2188e26af6bf5042310cad995ce12698d8fce6c4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191111174038.tgz","fileCount":1229,"unpackedSize":5542387,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdyZ3uCRA9TVsSAnZWagAAoHsQAIGLz+ennnHVPhO3cIGd\nplecWhAdeIsalnv8F0UgMDwTrMX75XeWyRvKip2O1OVKUECaSlU/KVY3m83h\nqPhPEJHgaelWDnG+wslHoj+wjNt/w2y46b2tO+YveRaJg4c7/L6NJe+AQ7ai\nZu2hdilKK/EAnPOqNtWRvqYyyI27rV9PtR+U5tS6uAGCO+cgVCt9/DVopmKJ\nP7neHYWCTjY/0R0ukSSfj553QahprFvFgkF9vUsK5wejoC4Pa8ny6tjydroI\nHPt7O0AEw734sjKfxDN3wbuPWBRdtTR459xN1f6kWbgZFgbuUB3xHZjUCIvd\nPVUZ97juqmhzec4/GRmElBareWIWbzmy1xEW3+X338nY1BOfBUd0TTxfGLTR\niLzyfXsYEdQqJ9xw8LWRoBishybsHtf+936hhdZqiDCBxgO2pY5UuX+Gy2k4\nE1BCduac6UTG3WhzZLKWx0jI1kPmrOkcA4j7XUG7SXf/SW1ktpuEcHIaPfEd\nCdRPmLgC69UIipf2qJMaVDKdrowW5CiTVo13/weob5J0i1baKj6QPB+OYcpo\nFehv8311+ZJaaKvONF6hDbIYA5PoSpHCraGrniiFL/SJ5J0UqSBQLa4pR6Xc\nwZtIjm7LkQ7B+GagYl9KsjKNyj6wZ2nO0ZWiI6RGk8hNyRbP8E9Dxe9LKVlA\nUoqi\r\n=BvjT\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEwWEiUcMdJc/1COUBsngR1vE8qJrr9JvcBTblbdaOv5AiEA/7kq1HKO5o5lgdjxynf7KvHHlKFtfCaDpPATDeQotxM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191111174038_1573494254290_0.9306479104363283"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191113080433":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191113080433","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b303e1e8911841743cc1aa6aad345b4fd760826e","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191113080433","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-9cPwNJCzERc+tGFE9Omn2EnVkB3wwoXvggBgR7/ku7K/cDs4xIttkppJjSfO1+hC3jmdQohYACoMtHRovJCbnA==","shasum":"800f4a4ded71660639da9c3003d95152ccc74b2c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191113080433.tgz","fileCount":1232,"unpackedSize":5532397,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdy7mzCRA9TVsSAnZWagAAZOsP/31fn9xpbCNdX0lFKFgf\nyZsaDb4ELdbHZ7XKr0tlhZdzn3JNhVkwdTfJv5OKZYnPQStfVncfsopxO11D\nVKQBtOGbg6HcLSHmSbn06W4QcpdEqwH1JheK2f27/mbCumuUmh8g4XsssNiv\n8utbJMQxB6hdhlCx3Lh823mm4vju1zAZLUmMwGAxzZgi3DO9h/Tvbg7LgvsT\nYWRjvYtLsJ0Yl9wTd1YR5vEF1Nd3521b0EFOGV/NJPTyMuFx/D742U/WopJR\nsJZbPwIVFSv6XKJTJPifKDRt8x4R+d7uCC3S2MCD9wX63+7uIKwgHyW+2Xew\n9ZA5ZZrw3bNQHoLZh+21WfT4FCHVNGftGEorUZ4DWdPWGbYmrSjnigvvhnqd\nVr9j/XG1WtSCIdzcB3mxmcTBZ7q1MgYGWAweY9Q6A3cedsVC6I7644QoSNiy\nCwF426NO0DfhYB/lm3eJgH11WruLx411lE2SJekM0BoY+wFUIRsIlJWN5UJW\nNTC6fRNicRQriYNtYaWdRNoO+GW141MceNTcrWXY6oLl2FDRLDDLABhF5nZC\nsKu4CqDvLnMVGL9slal3AKUYjlZta5KziaDzCjXSMIn0j+suz4w/OnPpRCC2\nDkAogql5BLWQL4xAsPmTtv9+ODP4UqsTHvJvvu/x1pnbhFXKI07+/DFa5X5c\nhAp0\r\n=5rJ9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC1LzNWe6xQYUlCHpRDxSN/6b4T8tXaEix8y6QxNAGSzAiA3oPypJtL6FogmkDpkWH1OyuEYDrNqCUCib8Oc+Kp4ow=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191113080433_1573632434473_0.21903690269957887"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191113081117":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191113081117","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"618b72bed017ce0a5b8de849967a240c81670485","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191113081117","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-pGgtpWRkVM/rgP8oqPJD4vmFHGjpZZT3wYWJlQuZM8cKe1VVIq/1abALDAjPWut+872cyg3aXUWKZBALLItHbw==","shasum":"292d9a570961e30e03f5ed3c6b221b41647ea384","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191113081117.tgz","fileCount":1232,"unpackedSize":5532398,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdy7sjCRA9TVsSAnZWagAAp1EQAJ4Bk8jaxc41pfN7WnIy\nLmRm2QfT9w0HInJt8zF3YcLs+giaSA3OPM7R6kZKcEdX4DD2oxjiPlBS1rnG\nsb5VoswistmeQnldkjJtkSVdZL88GhPmloGtEBZ9QqxGasPZlA96CM5rLJn1\nrcqIz3RRumCJFdB2BBfXk2LLhuUzKrQtGwzeysjrXwa5tpK3iaTWAOfyz8gf\nF7t7NDpBOKUpmpn6/rm4UlD5kpMHwdkDPJPz/YiZbkhSRXqJ8ln02KVH0lHK\nJOpywaC8wMXOFuAysmP1nWnrl0IOyVv1JdEvqjOfbakxULv6KpuZNWUMHaCk\nioRX11WZvyDt3V6oH2YcIFdA1GyD78b9dyRLECXw5oo0LoZaEExbiDHWf3JA\nEvEVMLBXYaY09NqHrSeYfhVU1MhwrIX6SkAj0LyzZtmyzd3Cvd3tYSzkftiP\nAIoiHK6SG/zZJepN74NzJnO0qkwJwuPFjhNaiIJMArU5z1JLzZ3Z/Li3PmT4\nAI/66OfeoG2ZjMoctQAM8JnX8SXR2qSVhJdRyzdLtpHLuWBiOsFebCHut+zH\ndA3S7pgzBEO67/yigZ5Ly6HLPR82ETUP+QVTmMhSR8nEZTmOh6HyAUAKDWXC\nEdiCud7ay01ugKILLIbpkWrQvo8FnYNLZw83ZIpI3HKkfG6iizEzwHstVEem\n1OxU\r\n=gyWU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBRqdGB/fcWHZf0dJxF+gFj0feCQxnGBDnreA7+uZxYvAiEAnDrpvaB3tS2kecyMh/pHLCyVqER62PRxHvrYKnYQPf8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191113081117_1573632802890_0.712628980914465"},"_hasShrinkwrap":false},"1.8.0-remove-deployer.20191113154207":{"name":"@atomist/sdm","version":"1.8.0-remove-deployer.20191113154207","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.3.0","tslint":"^5.19.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e5d8071a419a9eacbc94113e7bad5ce154c929ee","_id":"@atomist/sdm@1.8.0-remove-deployer.20191113154207","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-eqrVlKHtoc6zf3cBWBlIgIzuzqi/k4VOwitB/JicvFewURQ8WPzl2IvUQsUq6NlUyvifOFAgA5apQmfVztYg7w==","shasum":"5a89c0f994d60d9ce8964441099fccc5bc8f4e2b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-remove-deployer.20191113154207.tgz","fileCount":1194,"unpackedSize":5495735,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzCUICRA9TVsSAnZWagAA0rQP/A2IW6Mnpxk6sOD1fxcS\nrrOygrQ9CMdEZQJm1fH19zdg4SRsMTliYRx6pxy1LWhgqHeb7V9d9sHAAj8E\n9/TOWZR7lDpuhEDp/OpVagHfpbtLfpGj/6p5nSrIWKn4mOIsDzLWv1B6Anw+\nvf2j6JtyrbWJFI2x9IVWgN9KTJ3eVBImDt/Ayrw1GNCfQF6hHJkpIMKdjCZs\n60/ecEiUtt5GCxmukliRsxQifo7NWgWrDXh6PwSKg1V5nYBastR2qwi0hC2Q\ncVMwXR13kajfSXR/BffpqhbXsXrlpZFz03oO6B05QQinaiE+eJ019Be1u0ly\ncatjSvjmJTaTceO0+z3ujQ6u5bOjlqKVDSmOEk9vzTK5I4Ql7d4dSXtaLJGD\nMx3tNvj+LzexVyX69CIBDHpFSs7Bj2Qzt8otAYivr6fLlhbHPVo5UA2dKy5E\nOXdkUqeXcHaL22v+Q3EzC6WC5IExUfBClwJUGN5SBISvDWr792sP8Qssp7rU\n3C1ljdmev67BPQT1D0OsMykKWctNZi+TUkPiz7NetxWO+kyaEBxtk//fa2kO\ne6qlfCwN4vXiyj/Neqkp6patRW1HBgbpx0iMdpc+DdwPgPIwu3uP89xWWzEC\nwBofdwZEUKXKYc5eAIWyD7TGd57iizYBAe/5NOZZ6y1pysqpSkjllCUTm2S0\nmcuS\r\n=Db9Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDvZwQH8NEERW63sSJzV/XnT8Gf6qReglguQOeIzH2tLAiA6y9o4fUDUYPj7GXiJMDoweIcTkGUrvFD1rLbEyjqJcw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-remove-deployer.20191113154207_1573659912290_0.7034161819482929"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191113201158":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191113201158","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"645b0d5be2b97d282cd1b313b9ed49f016243b0d","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191113201158","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-36NMdh8cYH+NFptwCzieGJUheFfs5KikxuhnKz9aVt1sp4XKgs+ZCNmdoPuM6aZp/zlWjywtdFhjCU2UvZP3Uw==","shasum":"27dfc83008812a8320fbcbd9c5b46152f6a53592","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191113201158.tgz","fileCount":1232,"unpackedSize":5532398,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzGR8CRA9TVsSAnZWagAAgdAQAJiwiLrGfP9XRcQsMBya\nmADNtVbYx+7dh5MLBCpF4p1HK/T+ZMx7WHXZeEydONKeZ0jWRLoC5WakWvRp\n1ZzzlLmx92ZoqmQPF3FZCYY7nk46jc71L8ZcBthYHZHqMNmNamBD0jjGAzQR\nU05MZO+X7rSO/OT9MxpfM4zNCZB2xnxuDkNb0T7MGEwh6sBajy/l121O68Vj\nxVf5UZ4pBWCK0TGRj4tAHrVejVFaWF7Il8YJ6OYT8VZYTbQjJRkJ5HY0RRcw\nKKBvoubSBETAHmI+oKIZ5JUIDz8ESG4lRID/kzznGKZm26jXivpY8wGnCSWj\ncYK+ezKoN1PMEPwfVCQ0c3Hq8rVGSkC2f1T3IZFZoBI6fLbttaCdzgWFEFMk\nLqS8D54xZ7JCsdLdAOIy0N6b+IdjgVpR2gyoYfng9JxbxwACxQ0/knXn9G3w\nl1EcYbzPtDwxVJhYkp60v/duAse70ZjTerHbd69KHUvr8oddCFahkhfmPCK4\nmxNGRkq8Xjq3SeVr2LpjQR9S6kDLOBO/7XityzGZAdMesld5VlOXI24e2MJY\nMq660+3I5a9gyMNPWMYgsAhKnjST6pglQISU4gdkcTeUIqNA8n+SpkxqFO8V\npxNvlP7IGgZ7oCG5ldwILqVrLNUHfqJA4YT5s6pThA0vUO+rKSM/mheX2Q/L\nyj7R\r\n=k2A4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDeut/GtK6i+tyiMuLNz3oAgQGF+ZJE9tB0wqH5l0W/iwIgPnP+igMdAR5vUoNzPGpCWvJnz7wL804B1naWzjlDwno="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191113201158_1573676156318_0.8856250463222353"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191113203602":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191113203602","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a7d73b485e6186914846172e79e3b40a0c3e1265","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191113203602","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-pVMQUquyc5nnfZJUCKWGOK5mAy7Yl5kWV4LgcTIjtr++AX6x33WGPq7fafwn4ZtcYcIjFXdSGxwDTGMZAPNdyw==","shasum":"0ba71f99b88e99ca750affa66f1711e73905efa7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191113203602.tgz","fileCount":1234,"unpackedSize":5585731,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzGo5CRA9TVsSAnZWagAAtrMP/jWg9UV8JN30N0wfJPF8\n0yG2GtZyfWoz0b8TpiFfd5Pd/OZB7BUiGEZ6uhJR1FIwAufEDj0ApFneeAmW\n+rpsYMnRbWY9FOVyFnlTXVcoq1HPcKYHjW7W2Z1piMWsdNVEM4CprpO2TxRF\nBi5kBeOCqKFtnJpx6AmOVEpBAoLwJ7xzA3cosY0KGqI8umlKcFH96o7UYVti\nvhNTOn5cJqPuvG1cr6kAi9ec0ohRaEDpiZFUihqdyZQdEISczQgCIjjnLEpX\ntLny+85kh6hiIVAV5BIe7ADydvH32KHwZIIfHbSOdNmHPHj4exqlXvF8xUtl\n+MpUTzR1Qm+gayHwioANtFRoRBFGqjIj81LyVAyZWKhWddy25+T50iifW+Vz\nL83lrEgW7NovaUY7Lx8rtbyHoVSQ5zfr+GPiQY0j2OwjqZE8EmtOkmarQnE7\nvaRwnuJxSAjw+fhfJRU30lZTDeak12+BF1/Q7p/49DwwEjJqeI4ZsaHthDHf\ngBsCHYilAfyNGcbDGlmnm2B2ns1BB7wneUmQ99BxWL0achR5SIagFmj6Cmgy\nFYvQN+w1m70zyjyvbMiPiZFDzAAW4BekUdzna+mf9Dn5fTCCscKmwGdZEIwS\nV/ZIqNgbMTnkrtE5MqgNJRr66yR3sIAwIDFp2qY2sBgbDmleNNYnNmyjxWtj\nxBB5\r\n=Zl9Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC59JdqieT6+DAEgQRioJEkIDEOV3kpC1sIEY7LMvxK9AIhAJMFjJOPnr/7qN+2+DtMKHY0RVzYtafjiEphDd6UwSOF"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191113203602_1573677625029_0.6438553659823094"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191115141431":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191115141431","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"768aa27d7aac9f0c18f32c6901f1b48387c0036e","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191115141431","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-LPExp9lxwLgyZoqOLbKoycj0kFcXh2qcKD6TW69PMQvJbuOiYxKHqHVPDrIUSAwWU+IZpT4zzzaqjKN8u3+IVg==","shasum":"9bbb8366649443b885acb39580de77e174a89f80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191115141431.tgz","fileCount":1234,"unpackedSize":5586238,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzrNzCRA9TVsSAnZWagAAiC4QAIuNdNWg3yJV3x4N/BfX\n+QjyT7I0rcy2QeH57HH39q0cx6tWNHWlTVcXxOoA3CDI2GelMppJSXmRkPbg\n+R8/PvXxIOJ29WWTX+d7yHZJX5NoFAOt4BM4YAJN28lgz1e72xV+wdIABC+2\nd38/c8bTpkLWGFaisO3w36KRphqzjmcRKSkhbb7PDPKLGwtm33+khYk2DQlu\nTi9iVDDoPRU+GMJNUjOmLXe9ph5cFVBPyyfLeE5aFZD/a8V5vDypyKTGnIAv\nJ/VVTXFH2u3Ar2rnwFe0BR51D4XQ+CZWV2ifaED4vWOaFX7ZveUUYSIQnINr\nQIoKg/rH4gHU9iKxw6eAXd5ahh/w0BciYaCNfkyK32cR0JRqUjlZG+4GZyrA\nEijUNZ/YPsPUDSrGEiBUKsGtKRcdFETd0uF7zacDC9R1bIDufT0wzQAjPjmJ\nsrYN1yXdm8x4qDXcnUTOcn3USlnR7/7uWpBTdzcxHV6dnmycV5ZNJ5q5UvC9\nt9w5429gwLdoMUUdRCjxDLTyIjpyxVB0IKPXsCklpwftnC6LY/v//P1Jk2FJ\nw4g+2A/cbaAKnX3+VuZKrCcBOAOt4T5Idm++t1sdjqvm/qgLBf00LvsxAGFR\nOBtdDxiY+ayyiyoUvkbyvNtlJZAEmzzsQXcGRhNmj81i9ma7yQFuSlxmf9NL\nptfo\r\n=1HUC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZqrErK71S55KkkuLXSxdKEojp6HNT/mN1asiAgw8nvwIhAJsGWiFspj+uj0+ckQIt5Bm5W+LGvPYxvY8anBNOxLdQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191115141431_1573827442450_0.7635795520062609"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191115143623":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191115143623","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1d0fb27ef12fe50d6e95588908c7f69d76386429","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191115143623","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ZHSjIVBz9HX0orKaXRmmKXvPZuJnTcap5T/A2Tg7w4RV8LO8xyc7hSRSACZ4nqseqXjqj0YtsHBcFpYtbQmN9g==","shasum":"b754628a0ee3c3bc1570ba056f07dcb90622f110","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191115143623.tgz","fileCount":1234,"unpackedSize":5586189,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzrhzCRA9TVsSAnZWagAAoUYP/jZrtmS5wJCozmGLLff9\n/3hZz4rXB76uZh196IbEYTfwT0FgzP7QmYc0ti30kQ//X7lBtJeGN70sU3TZ\nj6oNoS/Dq258BrWD5wgmICWHM7ScmG+zZWQlIgrFEbHmJiimQByH5aXspouG\n5UdLTTs/MdgmGSdE0f/U+uyh4K82iC0iUnh7MSOvmZGzlxlqye6DRhI/j3T6\nbmTUiYCdbJ3lf5PucLHNHWJRuuybB3UU7zJXGWBnyHbcd/HBJbujzAY6jjNz\nXgna5zMtMumvzwEXdIKC4FAS2Nm1CEzVpyNKgJoDdbvsS+KEnUi338yBCXhQ\nxZyEkUdOWFv9A5hzDyE9DKKt4dGDfyDgKdid78VVHghaZtALZCsl2jKT5rgV\nIaiuJo28Robd7S2a2WXTI6eRECwfMqsPWj5O+TMJzmvL0kzD2SkYpVNxTTqc\nyH1aGh4LJ2TI/LvwE4mcOcoXDPWGtLEjibt7qZJXK0cDC2BbZrJrtvL/b8xk\nXCLOKdxWyPne6N+kgtTI2TavOiw0DxGqI3gALTXFtjb8vXxW36sFD3wuD4mo\nkDysuRXHCp6LI676KbI9l2NfjfQSP6retNogV+7lshsPtnX7AtggOvu03A6h\n6YZG9yjQ8VCQ//gE/YovqjRoS5t673tulB6xks4gjH4yZ7ri5FhnuK+YSchA\nbMLL\r\n=23JL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAUcASY/VNQhN5GcTO2XPGQSBT7wUj7SlYDLe4X7RYK9AiEAnv3mfEp8fqnYH/00nqyed0fxeoFe32m7H4oYLZiyIrg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191115143623_1573828723156_0.714982817000333"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191115155618":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191115155618","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8538be847d471d24cea706b23666f79c32edbef6","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191115155618","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oQFOHkqk738ukkkrytdOXY2s32XwdgGQJU6Ajns2CxNAe736FeeikTHFTGGNd4ssYpQmLpHdraTwunlcqiIkUQ==","shasum":"60f7dcc82370542e4913d503625d156eaee41af2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191115155618.tgz","fileCount":1234,"unpackedSize":5586517,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdzssjCRA9TVsSAnZWagAAU20P/itvuW2mTwoYGbF5z0cz\nlDyiA/TO2lHL5U7uS0jrxaWW0Zrglqi0DEAqNvkSNwRw9f7tIlrIFb68C2QL\nkZOeRKV3nLU5Rx3X9JflJN4K7BidxYsvfauHRlMbswBGuYG6LaZIOIBwB8Ui\nK77eKvc/4k/7Md2uKlJDi4THZu9nsdV6YonBnvFy53923NdWEpm1JU5UxKQD\nKYXpyXUpR5HBkntXqcmpqETvM90DORaR4FdMc/yGP+HDgTchuuBZvKm8oPHI\n9anHzPQjG5er1JDac0RK+np8VYEWfjZkKk7mnL2OiEZemjwzqpuM8KmyDdsP\nXjQyL/6hVFgegtOH/wLP4Isa+njRMl+LSZ/1ZDVLsp73HW7y7l/EmTntDnIO\nb0mITUp55G9LLscD7j9hEOci2aID/wJLj/GSwh9MscsaLOiOi4kcimyW4eo2\nMEy1tuuJ903DkcreOrUzKjBhHTq/22gHpQE7l1OTrIzul74VpRznMz+jXmxs\nQERom/3Ex+Zl8HRBuODcEPBwhbNm2JKosjAXPxFaard27S57hwcqZ7OukvZz\nJE42jcgPoUU0ovLQs12AVcg39S6hvDbCA6CSRmb5JiYNHdKw5QRFdmwCEpbY\nSA38WWYyj/D1r+ANctXGk70dxkkfAmlLcAdhILotp/PVuiGjVTkxxH6TAXdi\nTEgs\r\n=9sO4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwNUvdWLdsZItofV7v4QzaKsW5l1vHLZPgzhyZ8U+4kwIhAOz7u8+yRtkS5pdVIPShfBTUM93hBf7OIgZaryfCzUT9"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191115155618_1573833507175_0.6194268228539792"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191115162021":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191115162021","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9762da71d81d781500bec98adf7e933b1ddac217","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191115162021","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Br5P4yFKdQqj2rVhOVi79OXR9/SfqHxmscasspLNAjQs3SJfYFeG69VXC4ATPpPuBwSSND8ym3qtCvkFNCIJXg==","shasum":"6c67cad580e5d07818065994e233475399f7d01d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191115162021.tgz","fileCount":1234,"unpackedSize":5586810,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJdztDHCRA9TVsSAnZWagAAFjgP/0juQ5uq32AuIMeu+KHr\nPt+qx5LeIu3w5xa2TBPkDZdgZL3pejgYwCQ1s+/CDa3qNgY4ylFOOLTLKfxu\noiXpta+I4XjV+b8n/7YZNtAwNL6IAwbqBfhethDZcLsAMs0C2V4dFNQ/CYZw\nEFVqz//959j1G0FddyipFWNjnycYMrCb0EFO9mk0TzxWFai3rcVUfQtVVkH2\ne6ihxbjiyLCI+pS2/4k6JwxIZcYzJeOvmr5E/NXjzLMFBEPljJcULSqWsTnG\n0jt9rRUv7G9IypM/6BxtwrDVIIrF1IC/S8pn3FlYgk5eCuQhh3B/a9cBe9X6\nvlSELCaa0HU2GrTuNKnBMY90FgCAC7RVsHjet77zJ0lXHfyVT8FzyA/NnO6c\nJoqSeVn1XN41535Z6EzSYPJ5QPtPTWo1932Gw4YBd+OtTiW1jdbNwRqTh4Vv\nXbunlDT7r5a2Ylm32ukeDs9BucFUz5Pqf0WZ5olBpMkcv2WIT0zuHD48lHbN\npjUaC57obQvExKVj2GIG2d8rpRpkLxnLzE6GmacEx+nCndois8bNhFsx7wjx\ngiZnX/K5OWoefJfTiXIUHMJID7Ds+8W4dLjJphZCYK/SkqJ0Dfm/KOWD458R\nUO1v0A/ZKGvrM1RfonyBMVZwxUlS/us80CG1RH8m9XHtYoYr6Zo1jOQ1zYzU\nS0Ae\r\n=7pZx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBFb+LeBF8OLPb341mO6pvOMkDkO5S8SlRPdfRxQCNZCAiEA1B0izpMYC1cmzT6NuZq4DjXMgSMPh1B783SShMSXOto="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191115162021_1573834950674_0.7323965366335121"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191118084837":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191118084837","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.1.2","mocha":"^6.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.4.1","tslint":"^5.20.0","typedoc":"^0.15.0","typescript":"^3.6.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2a6def3106fd73fe406d46d66cf02f2ed569d2b6","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191118084837","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-s01jkecxGTH7BOuUS4IlQpog4skJNMwKfLfaFn4UAaUu1rC9lnm7E3oE36DLYmUPqOrAina99iC4zE9CsszIfA==","shasum":"815ed24571544725cfc76b0bb3a08a4a5f90237d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191118084837.tgz","fileCount":1234,"unpackedSize":5587689,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0lt8CRA9TVsSAnZWagAAfc0P/0aIs+mU7VXkAo/fVf7s\nTkAonaIUfZYwHNOOy80E0r998TBAxAc58dy2EzzKEYzmcoxLIMsvsoH+LdOf\nQBNmhOPRK+mVl4tUh1zh930EU/ULcjKGD6s/uIFgRTm/f96dKFprmwsiNMBD\npeA87SgFKx0i5FDEcnVZTToI0lx1oLf86vVJlCbXpnke4khbXCCsv+GKN7Q8\nqPuEchNDa6P2ptCFnQrvmPN3dx7/itgw2adgi57LOqVUdfoSJW3p82N77UpB\nsjjfG6G1b2plH0oLeq2DagVWOVA3sGlwEfdv7wwri4rIiq7BqJcQXvlXsxXy\nslcRSzhGNkMQRq4Tm1SCm8ZPE6+faoNLrEM5jgnCWaZpKVj0bWlRZbacewer\nMmKlTV8WMbTxqhd0bxmEu6tWEEUolXNESzKMfT2YQ7pMoS+RLj/GRNSP8OOc\nuRmT3mdfBwtC8BDnitwbPnaMPX4V5ry8+LZPakTBL7udcOJgT2xsNV1xyUX6\nwYx/cWx6zSILq61HZzE0tKYNjIurjuHm/oxtDK19NPSlNbb/j1ENSkvnCF/g\nQWfMPonRmp1Oeq3fordAIqH+A5LFTTSYzeIE1EgflSG4Zom5eu2rhml1ROWC\nO2Y7lnxedbjFfdmq9MLiE1JpdPAe/fb15VQAmjepPxsVkmbIdvyvDvTyXuV0\nwSh2\r\n=seZB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEMLMCv75liakqebERcw2q91rsxCq6Xm2yAdEYOFFsifAiEAp0KnZw/EUZjQnxcj6m4U5+J2qmAPHs7ByWkmyFhq9H4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191118084837_1574067067554_0.3759277113547359"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191118103208":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191118103208","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"54c17f5615eb77e92c0e8cba54c642279835c822","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191118103208","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-gKeynMQq39k45+HHKhokqj/G/MwleNOxGbILfXkvHWdSbkOAu9PWHw5euShE865rZuySunQ9zFFkR7XA1d/Vlw==","shasum":"9b4e44d28bb57b3f73303367196533392755fc9e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191118103208.tgz","fileCount":1234,"unpackedSize":5589656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0nOqCRA9TVsSAnZWagAAVv4P/1XUit+KSSrphkXBHusF\n94Cedj4etkf9AobtfEQGJvhfJDevmuTdTZDt4OVbc9W5ywYFyu0MJ5NHNQCQ\nnuKJ95k1szrsN9jfg5eBCMpPX9KSICtF6x/NomfBEHa1IIit0RJofExk6hrE\n/hJxrfilznLBDE3J76ZB3vPexaUScDFyk+MiN3bmsc0g4ZOMXx4NYz6kIj7x\nti+J6DGE+Zg64u208Dm0om/98c0Wk/56Vvv9kTCIJ84ydEknAk0CoInr+C9P\nueWG/czH2vceylKRaAU0HuAqJCLQYNSqCrVpw21dx52ePT5cGePPfPFDP0SZ\nPadjHOpL7sgRbiNKjWxAL2JsUIfarSmeZjbJPHOaVw0Q4OocTXO5FT/01V3U\npRANPH+KES5o5UzRxoibzLSW0PfZYXky9LCVwvBURAlCkB1Z1EdoCTLoI0MD\nqKeHaRf7d4jgJqS15ppJuAvn4hsIEFfbO0RM4Ljol98gFNKv3F5y4jWnLGPk\nihRoKxuiznKH9OUM4vOdL9A50AUOdbWcOT7OelA6Aw/XA2qOI5CZ7oauynuL\nsam87jnqDcpUM1eobyECJ0jsIxRXT57PlG9uTaZPUaEePW5sr4me4Iv4XlCf\nK/jJ74KMmcnjT9/N4bSAkARVABrtM90Gkde7E9ql95JflejyqjOfsXJZokcu\nVYZh\r\n=lWF5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDox1R9qPdi0VhYrCcvrSCMP6sujBabbzFWf3z9paJyaQIgPZPPCB1hrYQR+NRG+1TgFg6rclQbWvqkH79qTq0nW+0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191118103208_1574073257837_0.9063885167165573"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191118104426":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191118104426","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"37215c581d6e1f7a8b54ea2cb81bdcaaf52b37e0","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191118104426","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-lLRZa3cfQj3qvY6v6Jxv7AHeGjFkv4yY0pdhbmlebyNgxmZFe2CjCx9cl2aJNTDj7ijmZ6dxj4nHqSDd1tlrcQ==","shasum":"9ba7b1f20e682d698c3304aac1478b4ce9938389","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191118104426.tgz","fileCount":1234,"unpackedSize":5589700,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0nauCRA9TVsSAnZWagAAMZMP/R85xb+YwVhBIkAcc63Z\n8AA35psstbfrU09EQXH+joXOzH6AcXmgCHiBGGNVj8xm1Du46h8TgmbrV1fO\nZUuT5hzj5138494xiV/3n/oHDAmHzUzMy/D3M5JkPZnzDd8Aep2BubQzGkNp\nBE9gHdKyBDKKyMdltIbfXiyhdIkBzdVW7RUWvWuCS+mR98uehvRkylWElTez\nI6+Uh90ey58Z/3wYW4X7fLSoVSMfVVwnDrxLAPewyBJ3s8Kek16Bdf8XjuY8\nQwF8uH8aYpgua+R1WvdsqLEFQ2B1/AFRuGm50s5eXtaE9RmXd9IabIfp11NQ\nqnrQuFVHi5wQ4WJz079LyNc6OYNtyZfXU1MYjODhveVdF21rea4RzVJUeiJ7\nueIeoAyv8p5eptTTsnhf/wgccdzA2Ps+8w3gapV8gddKh/fXk18fx2lUbflT\nufxowGhJNjGyIPkPZq+I9OawBQOja2dv4bPhJOVa+RQOSyK2ktA7WKdZJlMK\nmEQKnfoXbkdgK2EK4TiJng7qS+9erExdiYHVimNxuNzO/xMd7R7axN55sPee\nCkxN8RPdtUiX4v8pOyWXKYT/Q+cKhy7OTiRansXOeh5VyAcOdRUB2v5IJtpU\nFsV8TJZ+XavuWBJef6EyiHaeqW2ZbBXKJCzZAo+AmZrACqFuroXnFJQ5gyH5\npUBC\r\n=iGXf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCzHNOe8il8uDSbYigZz5MTbHvA13KLeXzmDn7jhTOfiQIgaMYpfXBcK0grAPFFp6JbzQz4/0duN3zr1+lGXYEd0AE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191118104426_1574074029647_0.5377793764728136"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191118110012":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191118110012","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f8dae0f5d25fce4f6c36fdcd1026b75a80dfaff5","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191118110012","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-7mbdGGqpQOdWwvUr3mfF9IjMMn5FjZ7K2LO5tUqIyRGCGysRKEWkXK0OKjpMMNcCiTl8tx7r2OhwWqVa5Ue0sg==","shasum":"203616e9802a92c5924797092fb6dc84a57cbef1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191118110012.tgz","fileCount":1234,"unpackedSize":5589940,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0npfCRA9TVsSAnZWagAADGsQAIYB1++mZh6iRIsWZe9e\nkjle9xozAnjtIW49YlqP2guB0eLwdp2sT32RuGOrrECiuhY7ZJJhRHO+Fxk0\nbhcKcuBWPyEFomdR90hHcRfxviwXJdWxx8TRdLJ2mVHU31Dthhjbcd6TSlO7\n9ibUDUE5YwG898qb4LD0lrfzWTIJWStPY/dLsxxVltALjO9Jz7azUxnIDaeR\nGQNkzFI9Kc7m2UdzSVtl2rXZiUcgIXRpzjL5UFcmbHy6c7q0Ay03+zSYn9Ul\ny3Xhv++x/L7iWdjGCW3lFiAMVcZqqXqPBWJPBDNqZiTh+eWjVqss6Ne3ldFG\ntVv42Iekr3fyZT5XdBnsnyeXZwjwEMz3ZqAVqu2oNra7XZ0UCvZ+kdLh//5q\nVIcWC615/W0HYoE9tvm0gjU0k5ad6FyPyokSrW3l9rSv+txIP1EbOzM/PEXc\nzEj96NpJ6rPFH7MJYHwU1Idf0EiBdV9YKlQxRRC/6mRfFiJwCUpkrYBEMHIr\nJjch1QYmKyrun4FbB8yH5KZgdOoMMW8E2JJCCPU0wIZEpvfrAP3B73kQz0GU\nMwpFOEvzLgJuFUDDV3a99GI444bdu851zqwfIQs/cbx659fz3YtIen22xm2V\nkzKoahuCdRoFYYxMCvvLIYehFdzTf8I+HVsyDoj7RgBDyAEgJjWXx9GkvAHQ\nT1kn\r\n=vHeN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDmIszcI+oEx+9C1gU1UwqlEIAwwKyLMqT9t1HYiXdg0AiByxt6RLuZwgISZp5qBqXCuh76fWXYKM8Y+/3KesIFKsw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191118110012_1574074975063_0.0507462613628229"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191118110845":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191118110845","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8994609a4c44cfadfc4797dc2b4a6b850ab4ea31","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191118110845","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-LTwMCBCUM8Hyh8UGo3s7hq40oI67NkGP8cS9m0X9oP9Nu1D8UUkocrb+E/yiUNkQEECF4ySP8RzXrvdLywn0Sg==","shasum":"3553710d09120b3094bf921527982aec3f1320b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191118110845.tgz","fileCount":1234,"unpackedSize":5589932,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd0nxkCRA9TVsSAnZWagAAejkQAIEQvrk4Ex514AwqRtz2\ndhUpc/atJaRsxW7EYscsXFsaGbCv+VRT6QczkdfawRiHTTzF9xBWsKPFjhKW\n9OFlerWi3nqnVpP53NaAyKMGRLr83TMNJ1gpgBQcAYfnUH2z/I5IOp8q+ELM\npqGRIfMWi4HSR80HHYmaqWMwuAuAlSDqd95eFyc2Tl5PILKd+iH23tdlLFi/\nzOraMMl71L5LunBhpbKrCfXj6TI5zcMwJPIHR28L3qfWouXWgf3bXmZDu2ym\nd2QCyHiPirpdOETnpzgrMsmEATVgYHCIyQylSSUfXnjl6M5Ivq13Jd0ftoMo\nSXa4PGOsiFKORIzEzvLD14aLfvTH6JSMgNE2z0eAZAVYDK//6mCdU6nAyXzw\nsextk+rsM1vwXsTYzSuPF5IG3+sEQX22lQTSYp8pcocknz48caD2C/tA9Krm\nvrXxRYUBWDnTQDI+BGUaO0fnolES1Oy8Q4eamRPWtPevvzWIMOYvE6IkedES\nwcLNAItcPSabYVHqJeo4P7HOdhlU4zzVe3lJf7SdtByQx8ZP2T7Zs+zFm3Ux\nbSZBeHw9+xaFywozbXtIrLcfkP/1wPosGhSJ5cgitStzN1jc51J8KGHdsijC\nCcl9Mkg8Qr7qCRA0asQSB1oGimRPt+gjwVaCXGh5Pmwr+9IuhLxVvlc7pHaJ\nGdok\r\n=G5Fa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIETWPmrOInGmET6gMkqxOwaz+vdFOpIyZBDngnhMh5NDAiBb5/KZ81ZAutppztgmLHlT3D6o+lWLJFKxNCmd9lJ92Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191118110845_1574075492071_0.08189209135655329"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191120223639":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191120223639","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"38e030eb9444692c3e48da50fcf10d9777e94052","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191120223639","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-40oR7Q4LsJx0mzl3eaYbUGMyTVfoRtmzDHXTui81z4KISZb/0jVB8cM1lP1ARq/jYHt5BOs9M//l64El4dITaA==","shasum":"99688d608464a669691311c7c5d02da2f4995ae3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191120223639.tgz","fileCount":1234,"unpackedSize":5588611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd1cDTCRA9TVsSAnZWagAAFdoP/iEHwrzREhV1+nVU315/\n1qQnhVYdgkaYaiHoa7Z2vW0qgE4dC9ZQjjVECbjskri0EMBWyKeKAaCKr3QN\nHzOdGiYPRm1cdUqoE7LqwaCXgDUnBtsccMQ5hGU8lOkQUcXsopNC8oVRKRl4\nCWaBkLLESSBVZqvIeDJzBB7ilH9WrPGFmKqQpbAeofbu2SVqUQVDlHV5Su4S\nVdVSQyO2YuwFi8Y0yC0q9aUpRh9zqL60rYgm59wv5Z/GabN3c9BAAFwZk62T\nMS/lme70Q9nOHpTe4gJP3ol5hX65orkJCFQPu8vyPx1UUmdub1yS78lv2el9\nB97j6DuidDRvc4G5JOATvA4YDxrKAwn+Z+cLweXUNJRLfWyJ/EV6FZ7TD7h9\ntljB3eLkagLS1v1ojF/89g6KBKAZSDr5xnEeTVwjLZpoJ6iSt58mL6ij70VA\ntg7F89GCgTTACLSnMxrv1OHtRDfCvLkrZMLmAoZfo+3A2SvdMRggVBQBWagu\nMXCLbE8ND4MqyI3hf4nyKWqT90X0CQWkyB8D06f3dR7BXDiuDlzuLjlzx1Iq\n2L73HoKAf0V8TMNVeky6prhVELN0BIthnHAEFkubZBFxKxRjM4n+1W6AnfzA\npdaulVCPn9knIZfesAgqNfy2h0Z5cItYdcNLUA9wBNGt627tOgoIO+EuxMoE\nKQSn\r\n=ucP1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeLVd7EskFrv/XEs90/kXUz9wAw/mTJLLJo/2ynFwEgQIhAJPDvynpB+sTVXQihfRvWbFDjqqq40liYCwr5ht08f9a"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191120223639_1574289619273_0.4294555251180585"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191127164826":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191127164826","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6fad9582adc0300c7fe0ee781b78fbbec53a4737","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191127164826","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-bf0o+6jeIMDh6kaoWj+J5wirM8cJqK+YF0zJIsnkd6MerDRusXd3Lrzhrvf2lZTGiZ4fp/9oLvQJY987+BphLg==","shasum":"64d2e94664ecd1e1140bc1c846a7230b7cd7a751","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191127164826.tgz","fileCount":1236,"unpackedSize":5605959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd3qlsCRA9TVsSAnZWagAAa6AP/38WBT/d+ZFIJWxvCOYz\n1yX1DahYA4KHJI5vbotK+FKuM9+XHlHNZydg4C+/4UfHZOO3R//w+lG0GV2M\ny7RaiaSBDgoH22q1donn0JuM2gr8eoE08zsXPnzSUnf73Ntitw3viWRBZuQB\nlyC+ItdsPGX29pkmdvxVK/VMMRuvRLdiOk9JTnPLg8tWYn5FOI2WCLhKwCkc\nRmtVsTBlfjV3guxlUXSGpqrEPfMC04zJDvgqr7T/6MVLaTDoca5fcl7xgXCC\n1Omr7Hooaic6OF8IEnm3UoPtopOVrJrjhKuQ4dkYfut+Hdi45NHxUx4cZCND\nIvvxenzh6n+vUKq5J7Ep7td7X4ORt8MAhbB+mk+b38EMvDvWvHa5BjbXXEfM\nWWzafB1VwsEvLUgnw8cu2KyKPkf9D9n6PEnKBtWMcCkglzE6Lo6anMolAhXw\n9ZIY6HtiFdaItjnSp88iq/3N9hedkskQt99mPe04gH4AHGavfi0Ru0MG5PXV\n7AO/llZLYzeZmtNQQlhcfVzcDHQOjpXVC/KMTh0kL+3z1zzndWfWCJfJBTt5\nEZRMdpK8njMXDg7w1lZL5xFzQ0hBlacfTclO2ron0UGcBJWyciHFgWXW/7Qe\nOqP6RyYVuTbnHExmVbBZyD6Q0sI2cmT7zgz24/CWwNWn3GU6KPIRBOnqXtIa\nQiqW\r\n=1aml\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD0rd+N2QPA6lZiqJ4ylmRR+NdjLYus7Grrk9ZRRfPr+AIhAO8QLhipja94DU2Ad4nduGDilzXyX4/Uafud38FqKiBB"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191127164826_1574873452135_0.853296107088352"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191127165233":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191127165233","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"77d053b82b991b5248ec27cbcbca2f5fdd59aa18","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191127165233","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-IahcgRuOnOhtPg+KqkDiZHXhg/Ss04EDh1FesB+Gq0eRMK+NaLqhKZ+dilLyhXiE+lSVMU3cvyUuRggcRPNmtA==","shasum":"8d7af49994fb9ef5dadce9f110225c388387cec4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191127165233.tgz","fileCount":1236,"unpackedSize":5605556,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd3qp1CRA9TVsSAnZWagAA5k0P/iEgwEXaw4vag/2dJuEp\nWsVDLuiK2NXQk+uuvhUJP/56iFkvuV7uJIyZquQ2ml6/XODYcnVM5GXjcpYg\nZFiIkuKjnhwNjP1igPg8XP70ld2Z5YKqm5mxCoM5A/8aUHcIMv/SIN3RV+et\nzTUd96bt+HR49sfUe+QULkuh/HGDxBNlj/YZ3H+BO3vPnq2B7C/mRnwVIZA0\nSgb7rvdLtOev4MvcyCBSRHBYLB2O8CYjLMADjo9Lcu1WUE3Y2bTxLnu+SLpl\nXAjEt5Zht2PDy3X4Sujl7U/LkykJm+VHlpDmXRCMAdAVly+OgEXEKo0z102o\nnQ5NZ1OmPRl71AdRhnZX2ApWRfw1cZGom53ZC1zrOC3gYZwaFp1PRSNhCGgB\nuWalDpAoNpdIcz+1I/fYTuMmiCF5G0IEolmeS41es3VmgeFVA4MEKevgRTmm\nVzQ5yE4GGIZcv2AKT0hf9/77fM2rdQ7wBrO6cvCglROg8T6zph76ZMSpQPSj\nvQJPd8x4DQDP1N8RR3aAVlVq4ecvgV6vN/7aQQugiALCMkofUbPZ0QUAuRJm\nif6upRoHgbVxoOWE8l2Au15vu4OqSoxHKjutMyvXPEkTdKkmLc3IUwZbBUkk\nqb4AsWB7GR1mG2+IurEdAWwa27AZLdJr43PT5iV0zrLwZ6zP1nyoXnW27Hcn\nqjPk\r\n=Q6WN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDOz2C3SsJMSFzWnkRg/YVtpPybQTUUKCreykXyUuJY0QIgI0Fnj+1DeTXIXKdZIrGpAKKrEb7KX7i6qSGR/iKMxuk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191127165233_1574873716852_0.31629687696258113"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191129124919":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191129124919","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bb732d1d76f42c9bec9d88783cf3471906d9806e","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191129124919","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-deceXDUHofWKqrxOBe7oAqtTOUNd4ymjt3YRIcb+/qG82oknITMjXWqiNu5TJZbabJ+C4G+LF0okZDxZSXDOvQ==","shasum":"f864400b3e1c844e9a5bb65a1720143eb137ac85","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191129124919.tgz","fileCount":1236,"unpackedSize":5607134,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd4RRqCRA9TVsSAnZWagAAvwUP/RTpKZXCMs1F6Y3fbJpp\nYLBEtb8Y4efBZH2Pseqal9sBlYHBfP7kzRmQVEfa0JeKQf0kAffMER3fisHs\naUxuVFfi/t2YAploDmkGq+/Dym40xkrSWdPotszD6SQij6EpNz4gsnA87IPc\nIAZFhL8dJCQevAtGs/WXhcuI0M3YmcO3AzxFaImG/y1faowdFnbhmWIjikWF\nmDiMvg8twjxDs5IGY7x9cHHuz2qrquv6aUaTx5IbnaxBsSKJQat1c3LuHU+e\nYwZqO+6aKteZRG8bBG+5Y5rgYL8ej/GfGavgZ0cLjrtHlRsMJzFF9c2py6wi\nk/YzutxDqlB7fua47db4bBiSPm7dSJgzfgCX78p4RBtOUxXiAmSAs4kS1e12\nVhWPtpiXHEVZtbUi63K6VEsF+V7aQl8RWb1+XjiRs9V2VpNzkZPHUMFbKQ3e\nT8LufLV2apLKedxLVfsSp86hBwV35KZKOwowKCaietNooV3ajVC47Y/yyoV0\n06JCD8mpeAwqXQYWbbAGPFQ0naEYXjCo/y/PBozZpmykx+TwGcbpkVHQ85dT\nAyVNmoBccAM9ar53sGJbHWNp3Dz1/R/6WV1G526qF4KWlvfvMrZOefNL8GOx\nQ8boGtRXLz8vDdr8YYjyUP9u5U1i9G3QHT5m80INBfZo9+Ydy/rYutLIkUpJ\nYNn8\r\n=8Vvp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxbSrtFDkV2wKnA0qD5dtBgCmRR5zRbwsR84k4z8i2igIgLQxmDdwdxrdloj7lUrLZYYZlWsWgAis2oVygmWl6ztI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191129124919_1575031913520_0.005021993980848238"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191129130631":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191129130631","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c5a554ffb06f92f8e638185aea937052a02a1a32","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191129130631","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-yRtLt8vMXA6+cCd8RcihIazFpKI1ufLI6Uls+n8bC2RX6CFatJ1VhgK9GaX7aDpocuPuZfopiC6jge1p89z9MA==","shasum":"87b7dc8a19c4f6008c000146a2e6b6759ba89cd9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191129130631.tgz","fileCount":1236,"unpackedSize":5607197,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd4RhvCRA9TVsSAnZWagAAKQcP/jTWrMJzlGfJHdEHgHUq\n7TeTj4cxU0mVbF6+X8gSjYzEhaEDn44BFRs+4vPOjjbceLDnn1b7KLX223bV\nzhZkffYB1FgwkcOZ+80DF7iluaWiJfh6p7LiHw3448rkQCEwVU9ogaYk6ElF\njZPVpvohEq6C8I+72JskPvbbCxBQ7AOaukkGAUN2P3HWd5MsqyUcAPgYD9L6\nAkVGERSX7+dT/jHELJcC0FxA6dv53lcT/WMJwvZUwbkdXSgPsB+PnOHJ/gHS\ndi4wz2Wr6Ns1zlHtp+Cbzgc2PdxLYij1dlkrXvDqmPuTJKeokgCGXcYqfNaj\ncKVtias8u61+kYwoaVVssUohLJj1dow4JetVrm6kislhokPf+yIiDNz3vYTM\n1LHSpDqWYSUKPC81xVnLr7HO2L0BA39KJAoKHMBTZJaeYA8gLp9iyBy93UNM\n1bBDKWRpDkHmEfDzCL9kzbmfyb4MnJD1mNscirt9VXhkg+Kj69dgWMpQlfnm\ncLTT/C//WjOVLxVxwXL9KLY8dZwFUv6nPzG7zEESuNqA6xGfWDLyTkN0txUy\n3REYAUB4LPHziFrhq5CZbMAFno/2j/4mKQwmcHBvHt55Ne8ke2o0vsaesh5e\nE4CE5inB1jJR50b3K4uLPAh9cC294E16OmMuHo+mVlO7Xp26O1weJIFAsKnO\n4xzY\r\n=UxRX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC94emNdbYnZcwc1E4nqfdbfBnFe3HoPMMdsBLpwNXDZwIhAMgouPRQRPY6nrXuPT/akaGsri+uVyd0R5688cjrfZjW"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191129130631_1575032942739_0.11684839964604965"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191129181045":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191129181045","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2bf196febc7d002ef98493412434bdee9d9ef712","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191129181045","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ez2WKOljAh7wyGIhFKDfGwFiXmXTIxuNv20cX9/80KJGMNqS/6Vsg8O1yom0Tyf+BnlrtUTDR3U0iOpvCL4BdA==","shasum":"39cd1fd99b3772c380dc0a63078655e8122c8a68","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191129181045.tgz","fileCount":1236,"unpackedSize":5607452,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd4V+5CRA9TVsSAnZWagAAmIYP/2t21Oz8P+7CYGof49sD\n+ka+VhUVVwedu2mcBYQwYvtKfQAR2c9TR5uxNxNO5+eIWOFitt6AhchfSF3c\nIhzUJdKzRZDXbURt75u/H4IJr1jU7kCUZSN3qyuKrvJNFmCNLtGHx8Xev4P1\n9whnZzC7UI5WpW0fG1PvkxkEWgDUlextbUBbu47l/N29JNuJmFjI2xbJdN8z\nVENkByt2OoJU2m0c+MmgS7Eyke/08t2QljhwERo7qf+BUTncDe1x9ZEf6ZUm\nMReR7pGOKjiN9R425osPjBksa3zLRmBEKvzXEfIp1u8H8IF39O9uNcKcTuC0\n1RyW5tJj15WB8bJ7GECq/IIUX+DD3ix7t/R3Ld9ciMsuDVrqZmDecukIOte+\n5zfU2nlFNZWOeiuzg4tT+YBfnunzGN1Foi/YZep7wcjopV7WGknj72yIn+ej\nrADq9bpy6SmYrEURzTvaGKMej8lIvTAzkRzDzpC8bu/DAbGeUkBmnyLwgeVO\nL7DNe1LpJYc1NI/NlDCRK8yHkpQaEoPIWFTntmkXDxj8Rj81vNXmYD8WE5HA\nlLjyc2BF5RXEsEKIUei0uSGT9yXX1SM/U1bNxhCC3AYuXVrDDJfq8XiGz2JK\n6ovHO0N6W3gS3Vqz2iaQZcWQZ1y9Tq0qeK7j0P+6Un9zhabVCGKOsgA8wNWp\n1Mw1\r\n=Cxlg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHl7VCZbhMfINzQdecvsx3rFkIKBew+4LpGvhthv1y96AiEAyzcwbJsNyOvVWK5hNP0CVgU2r2npt8C8ONfX2jjMZqM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191129181045_1575051193171_0.8716874515293762"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191129181414":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191129181414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2bf196febc7d002ef98493412434bdee9d9ef712","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191129181414","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-OFwoAtthkz6r1L0/20sQlsBtKgz4tl3tCTBVRXuAgNCalgTQUmfbWlwE3OMmmQ6QjVVW/mDu0A0jkVjQmMbhFA==","shasum":"b97508ad4a75dc8a54183f39a6b31d5a139544a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191129181414.tgz","fileCount":1236,"unpackedSize":5607724,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd4WCECRA9TVsSAnZWagAAEXgQAJzOFEy1buhK5zLSyor1\ncsRWycKv4xr+gfAn5FoPm4SOK1QePh2rj+xLqqUaYdl81ThuQaWr1l3KAGDi\nBAFsP5q1VAYMBmy4qKtzI7KD++pDVwHQCMCfKsmnbx3I72QG4njnWaCvI0FX\nCqDN+EDtt8dTV7iXBQ0vknSVYaOoSEEpG9ayTTLReiOYQciDsDkCxwDLeaq+\n2fvH83cDJiyQOlOoDmSXm3HEQ0FBqIzf5smWj07jmgPMeaci0wBnRHuXXGLN\n08W7h1iQt+G/+Lbx2YwWrq4N8f5KEBqnG0KtlNkEMLs4U1IbMq9iM9ERhbgc\n3h/XdmxtHMyFwPss8xjpXr7PAqCouDq5BihhJ1hUicvytE4CH94V6Sg0sc60\nauvPpl12aFxE2c5Cn6KjoJq4Ry/4atcRLRgP7Hxu4BUw2YoNJgUQc3/lKG1L\nbH/5AJfuea8kQCyiJVup++SXJjj7RlKyck7mVNVn1tWQZYQCZwtS4FQ9sLjM\nIzcN2p9viNteKGAdPzDsIyE5qtz1HRNNKYisIrRfWb1NLkigzhoyuXpG+FR/\nnKjjIYP1k/hz8lHz3T7g6ZO4w7uRty5zEhWEaltqUZWjhn5MfZKBhHr3iOkf\nBvz2M8nsRs+rX8M/d9Wh+cu6DRff9uNZfe7F7tll6jOH7P0rjNYq5JB/M77/\njUaD\r\n=p9Kp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4RWuA4goYHSB9lN33NbjZACSqXqa6PyOw2Ychh8CPRAIgZ8jDoNtmvTek/vC/jOWmK7Cqo1dBYtPPPW7NeA8T73k="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191129181414_1575051395517_0.5514575543533982"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202125053":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202125053","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"890f5bc5c4b971ceafd0947b134645e5fee8430d","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202125053","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-biwroTswyWOWxulybbXWPDb4niuzr8Ws+zh8KFDkmL7rjv0pESN/LsOD1qit62vj4vf5QRiQrozq1G3b8Spq2A==","shasum":"6348b36d1ad37251298bf55d1a9f887f49058cc1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202125053.tgz","fileCount":1236,"unpackedSize":5674754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5QlACRA9TVsSAnZWagAArPcP/RbH0g7AS+3ygvi89zvN\nbc4Gw0Tgwd3HJ8INFVAazwCkEN+IMNeOUABpnJ9KGw9DO4qsOkqndzrbvbA8\n8wj6xwzQ52t8GXR8AHnRiJ/wCeaH2QhJ52bIZh1FNAfA3RLY2FN/7zqER+qS\nnu6bC4ejwbX+px2fZOX2SzrtRQixfdYQrOFW42YrKAQXdYtkD8CIMifxpDd3\nK2ofxsrgiRH2eVU393on3Fd86rHtzoJvo2K8j5c2+qR7Rc9sIiT7pmWDUbCJ\n5kJkekHWagfkl4bS9FrQzllpvLFRr7FNVONzkO03cGHsAWmGC1sN9YVkcIhr\nS78Dz5ooB5NBAF+/99QAIkptGRa67xyaiJesjZPsuHebVbFYMDUinGH+raSF\nQ8JkGtx7fMJnb7oA4GKQPA6QwiGw4fJ/eIt4qGbtbYAdcwNrAhMyI1mhddXd\n+ew3g5y2CPbl5/gEJPz0MCaja3xU/4+4ZwsDBqe+Dn+WoeosmAchG5DIuw84\neWH85ED6MXBZmLrqAUfeH2fvkwZosNyWG9P3qOw/Gil89IO+cmQvFHww0wyy\nJXPacuJF8KVxFzZ6m+V33uuXWXDdp/T34mKBkcWPDhzGmts1NmB/0eFYpUXF\n8rlJxm0+OjVQQIHkAbyca84PLTqk9r1ofxN0nX5HObRErdunY5f70zrXWlmp\nFo9O\r\n=dw6z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8hXk0jB7MvGcdCYdyuLYT3tlzVFUv34zm8G2Ud2MyrQIgPHOhtkJs+BUpQbRMk9cyTWYC1TPHn/T65mjW9lbX3tI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202125053_1575291199937_0.8847776946497137"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202135940":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202135940","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c51e33f0c9f7a0830039aed0dc6f5f840c6020f2","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202135940","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-l/Yk4pOp0w9FsBNcGwjOW9NhmlVyW6F7NYXeZnuliScYOL7V50HwcbgK9dugZentr+VODjtKg75MKX/wLLrbkQ==","shasum":"66a3325d22044a97f534c90ed3345e79f3aa0f45","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202135940.tgz","fileCount":1236,"unpackedSize":5674945,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5RllCRA9TVsSAnZWagAAnMAP/0FdJ6OkyQ1CuFxUIur4\nCGi+vsDrN//ISn181vzwznnD9c6GsuXT2QUXqfguKdj6BEHsl8Q1O+6Pj7vB\nT90pjjTeraj2NmQxBqUhWN6fvTZSu5mp+p3dTO4/uaXcRakXADW2S444C0fx\n+ZCIrnh/r0fCI7MVZIwSjXjeAWpDd/ir5IvxKl6vMKSqerYEXT0K8J5xNwYc\nLCGt/4ARhLV/cJaIBmROmmKdmgz2VOfhNE4SM6XWZIEb1cXdc9dWPVbv8s4F\nn+jfVqan+wcwq2ey9HxkRCBfypl3B3cptHZtvwTjWKmy5LQedMW+WqB3wkjn\nRlJkAEL43vhu/jfTsJ/MIduKeYsFbcYXNhQOTTCwA2YuzSzaz6xORfPkOJbj\nd0a/wDg/3CtPFimQySuB+HsNq32is7AoEIFVYqUkQbSBuISneSCxBMWiRnzU\nsCU96c8fDhq88gpnlDRwPIDKMbKdJHYocP+OsGhaMo/gSH3q3ZpK5XoMeulA\nlfKDwvUWIW4UaJ82lfjWDmk8EUEvX/RnQCT1F9RS9rftnTqE4ZdE7DVHOla5\nDI/8L3I5xYofWlvTpLdLJftKLgUQah3a0Su8P1gIB5GmKOX0cZGpVcX8BtIo\nDNgGBOt/Fw7GUUVSCYbHKOSrj21jqqcS1jT9OWja165DOzG5rAmKA/bv7pC0\ns5iO\r\n=xPsl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGhpcz3XTMx7xFGUrFnpRh5u28hd8V7wcPb2xDb7C2SeAiEAxjEZ3+B/Jeddia0fw5sVPorG4henWg4ehnnHMTgF8OE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202135940_1575295333240_0.8259480983378809"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202150525":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202150525","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b6eb6f947ecf2489a29920bef72a91b0f77a4025","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202150525","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-EDvPTcr/L9aLD8+ZptS5/2gmpInQfRF26HsaUcd0uni4jiWY5FIIfLiJnm0vqFEuu+qK36BPnGJOJ0iY5jhJbw==","shasum":"c0e6f300dc825b1bd3c209a99b482e5397f07e1f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202150525.tgz","fileCount":1236,"unpackedSize":5676145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5SjVCRA9TVsSAnZWagAAwSoP/jr3yBeoihogZtsEdleG\nZsTXHBgbNzJ5nFbyd1+n33+i2DkQbVlSHk2cuFjqYbH6MaX9wz4ZGPFI5eFD\nVmBShBidZLuz7pIgMbhqHLsOUS4iQnFbXOi7CJhMlodQpvyeAy+UnejdMJM5\nvZT6WSU8jdVlMOB+CMkRb9xM14bheyR8qwU/JmRQRenpeGAcYQMUShQFUr4N\nagD/oflmPnbuKIiCZQ7Em1WnroGxAuBjpgiihkKPEaMAwzrijrTRQSP5MtYl\n0eUTepcxX0mfjB+gqGSMLvUSe16vuMV0BBqe4kn1sUdGCQblNMjlac0zhFB5\n97u/HSLDeu1UqtSQT4TdOG7GZJqXMJ5Hxtl+6m+1ZhEJM/4GMGvuW+G0Uwg1\nKuG79Dugyv/r/nzhMjI5w3WohrrGjESkMnFr8F+39WjAXnp9QokoNYWrJmBf\n4fXh7KZ62jDwGvpZFkl8nqFu8wU/TUGmpMDN3Qwjfbh8Ob7/NBhoxISTrhZq\nS3gYfy1OhQVmG1ZSwxAAVRPQDK3ED4gaaEiGLHEDLgBuADZUS8UdGvFA8MfV\nElz4jD5eD/ZcBg+3SAid+PAjVfbnHq47hCIyc8IWfqb2vM6K3QL7Oaut4x7u\n3sI1RGMCFaq2Jb5oi2ch1CSD/0TbLvatVY01xzvf5tn/4rkjIfXwWnqN10Ly\nzHiU\r\n=+Lif\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCd05VcbH3AXp2fPmyVvU6X5MKr2w3mvOOZxQdNBjalbQIgLN2qB8HkOsw6YGrOA9tBBjQWencbrseZpqvYFYJKCaE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202150525_1575299283758_0.5271451404265968"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202152057":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202152057","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b1828780c84e3e9f02cdf421c7836f71013522d1","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202152057","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-u42I3qTLcHqRa4lBuWGHtrJv1JJGZIMuAHmuMvt4vk98ddr80ywbazfLvpM2SKiA4PG7upcgpMwukLKdmoQJPw==","shasum":"f623d4453a4efda454bba3a3db52ef4bf9cc5fea","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202152057.tgz","fileCount":1236,"unpackedSize":5676132,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5SxvCRA9TVsSAnZWagAA0Y0P/AvDLAI83h6JzX2DIUKE\nor+7zVxXdio2I4KwpcsFyJ1pGo+4poKnDt9sEfjLyAnBTvstZKteP09RPRe9\nEHs5OaKR6Fu3EJxcM3RENF+mp9kg8DVsafccTx9mSn/XItL5Z93sLD8wlNXr\n/kZqGA+yydnS+XlL+fZOj1otk89JkF4OWGx+4W2czKn9vIauciQROhA7bEqd\nfIGxpP56QyLAyI4XCtfyYp08bocvpnM3x3wjr4Ua9/gadRyeDjbQdiNI5H6B\nQkoaJdgA9LONTAxm69zy+gMlTaYPfvwwJwsek+MVnDRTmNVWSCRON67WBPL5\nwanDXlelLRCN7nuHZQGnlKylMiK7XH34ly4f3FcUIObdy18LNyphAR/Yb6lM\nmITsJYViMesKYrDWKr2lJVkXd7VtbLM+xSYdbuMtHAHNWvZKRYu0ZK7qIjrS\njQK3c+TctigztiKuA//JiQwLB2Pi3F8bNAbtk5pFyAUzOVrkgSsoNl/Cgpyk\nqanJrfiGWHJtZRh5EHvEe4W86QJzSkWIibRGJ9JWApV8iQotqk/1CdkR34tp\nbbQvsOiCyhmXyvZckMcor/MYf/MipKh5dhqfUNmrWLbfVpfnbh3X2TJ6ZZWJ\n7Ys9RdIkXTeRFd0PzfZ7JnlpzQCqnp5jrJzj61wUzFXWS4leELJpPGgyF/Q+\nVhVJ\r\n=ILkD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHl03XNgalxs4kYHiKQR2D4q9bdVaqPppmf0ILhjoWHiAiEA2C2wU5WyYVAge4B83BlGSe1FfpBI0h/l5cbjqVFaJBc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202152057_1575300207285_0.03755869018665692"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202154744":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202154744","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0cae22d143d454364804c6ecb01e39906c2e8a50","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202154744","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/jK8LLoWoBJHEG4XkGv0+K5JxoOBNEfDpFVHQmqV1W7PQQylv4rXQywmdw+ZaRRPtIApgTOMjHD2bjQmC17TVg==","shasum":"db6fdbb8295079f04262aab8953a968bf85dd9db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202154744.tgz","fileCount":1236,"unpackedSize":5676517,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5TK9CRA9TVsSAnZWagAAop4P/A006wH/D8pZVM8vdTC2\nbbwVI6w749UxIHsgNG8vsnS84JoFK6zlPExAJ1gw2qFdIBDblHD9J1Pcxh4A\nF9wHrdgAMDmgB+7VVY2Z7yZUd9JoSK1BCmYzemQc0aF5YUTq5zdeijQxQDiW\nMakDayzAXEEg/2zBcO4HHMWaeAkmdI5VOC90gU6Ft9bD03qYCWVjyhc0nL+y\n7ft4IvqYiYJX9KI1YpdDLjKkXYF+KXpNfJt/3lagPczKCco84Ldml/VBa5AC\nijSULurkYuOoPOj0rEgvUaPyZyB7jeYT2Fs/oJNVVvsvV12+dZJ6lU1IYptg\nxnbJDihYixdRXOTILPDVlrbatLbNpLKikm6Dp+RxyQ7W0rVtyJI1G04Xe9uK\nlnDFs/B8d3aHZ4lmmbikLQzJke/LbbicSw99uVfO6/pywZh56PeCiAblVaMP\ngSwvi7B6WX79j6H12rQYwHktxruYR917O83BJqMr+5oCpwBZ4RCq5uwSfzQy\ni3IJmf3B85hZ2O9dkxn8WzE2Dmzw12ATzDOx2vNTNz6Qk/Qt4MmUcP77gTHz\nwWSam4jDQNQaT5Og4kU0O7YsIUKfS3OV9ZKhJvejj6hRHfjg8uJVVKHJijIP\npqayhUHkTEqG7qoQKw365WE1hgQvKnVNkx+2r3NMpyxdcGy2+l4apieeiw+V\nHW9I\r\n=wiWq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCovsxqzAWQp/pu7HZY4kRgMEDjVaWg8HV3HaTz/TXITQIhAJOxi7iCFgJn0zsdSb95gvjRZd84BnY2GvnS+498pWaR"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202154744_1575301820761_0.22503359200888573"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202172611":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202172611","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"07ba07f306bcc1fc0141741a790af5ec9da4647b","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202172611","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-SaeE19JyxFnFZk0J+0m+C6as6Qixzqhn2+VwAKncJcuDHTbORCFVTSh8Zt34qOWuhL9aaWiAVU+eqZlhYxRLGQ==","shasum":"0cf2fda4295563cdbf1e3f6521fc52acaaa0ecf3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202172611.tgz","fileCount":1236,"unpackedSize":5676666,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5UpECRA9TVsSAnZWagAARWAP/isjsA5TcumG8Q4GTDNe\nzB1c1wN1KJRO3tTi1JPDQ1vYUFvYQU3nKfr1CaAG9DF2DKp3VldUTjK6dddb\nqYayI+RZ1DO4lVY/6uD44Ph0/Fdj8w8pMFavvJrLI5YjVKxa2mBcl5yQcd/p\n3vV7BzP+EqTUpfkvsoX7k33Lk5NGjw50yt6b6aUOJloI3cm0PK1fLWH2iiCE\n5CiT1Ng6X8kbscH4gnC3q7PfpbaXgU6TFSruD8FGSMkz4aQHU/ay42FALJQ8\n/jz79WAEJ/H8FZgxxmrSaKOzP7+WF+FGySg3N6Cm6ok352j6Yicn72n2SsWz\nscdFotIzvS4n2JerOpQchHsFGYePFVOG03MmjiXgm+tcyZEPMjQZljcq+yFZ\nCivp79OH8cyVp4Ee6yGBzk2xBoumm0wLcRVnfaw6gCm9eWOToiSQe4bO8bIl\nodhCYWm2IqhYS8brLeUKOxEDRDelevgDp/oShUv/CUVrD7Cnx939VViklmEt\nDIKvr8sFjblWbupZUucv969BqPXvCic3ic4VhmjStL1kRP3EzerSc6Egu8Ag\nk0heViqmLW60O8Cu2usJ92VATf2/ZOWS7jG4cVOB1AcEhIZv9uLfKddpkHhR\nQmwJkSF8tUydPWIqK2x7xyuK0Q8F2c/kuLdLEDnqkCFquDxPdy0HSWWSrdo7\na/zW\r\n=VMrX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHw2T7tfSoNrKDLHvopbbAV1Ngu0PLNg/q0/XC4VUHD8AiAMWbRjCwokaRkUbPYxflGl0OVrVd2aE2J1JRcL1RxO1Q=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202172611_1575307844068_0.8237752771542428"},"_hasShrinkwrap":false},"1.8.0-dynamic-goals.20191202175738":{"name":"@atomist/sdm","version":"1.8.0-dynamic-goals.20191202175738","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"1.8.0-master.20191011121753","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.2","tslint":"^5.20.1","typedoc":"^0.15.2","typescript":"^3.7.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f8cd50021b20f0e03e105ac8dacc655eb44c4aae","_id":"@atomist/sdm@1.8.0-dynamic-goals.20191202175738","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ix7IHzcmxSZTnbGiMHxDoNND2QBa/wGAsr9ic/LCv7yGAuxTMa3NBBhwlLNsdleRmN27hl2J2BzGH+rnHW3Zaw==","shasum":"bd2f0b2424f2925572e6dcc8ba695e3824d72fb3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-dynamic-goals.20191202175738.tgz","fileCount":1236,"unpackedSize":5676493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd5VGYCRA9TVsSAnZWagAA7vYP/2nrgpge3ak+WECuZFmv\nyNKt1Wn+6IDYPNdWPaaYK/OWFn1MkHAoCr948Pyme3WTb6RU6pBW02NQBVLe\nWzFZ+VVYhj/Cd1seQ9JumX2B9T02edsikEjQlIrOzrUTLteMLU+1rpOfnkDp\nUU+m8mpHn9nKCUsatb6tDhmVRctj9CCzDWYp7gcKncjwsns7OOoNr4rE8ouQ\nsiCsdGcRbcMlPQVNsqbOQpzQy426PZa85Gny1DncH72ec16udvNQiBKaeLTt\nY4zRsizoBS9/Fqm8CGPcjOuY+51fhv16yQs1qRhafChZnHpE5L2wWEoyumFW\nwQoYuKgkKCTVdfG+lNUmUqLiAm5t4O6F4SzPFDqircUZYVYa8VNc5eY5YdZJ\nOAKoYR4SdusNsjFQZJ0xWSmyOMlnuJYHKdpxGIoV39TtR2BTkLT9BN0W55jC\nAZT1J6REw3Q9P1ou/y12aKGLrtEUrXUAgURu4KhISJ77PEz0zG+pOEsWYD7i\nTTRvoqKID3cZyXzlI0rIEiDwK3arN8BZXEOVxoStRazA2HI7rz3LdBMwB4h9\ndI0j3IjS/QLSCtbfR3ur/KU0ja9qtaQArSvSHWgviWCfIIa0XS7YIBww2Lsl\nqS6vSGO/gsG5iYWWM2fPker2kCmHoEVSckSeHAgyd9muQbXb4bhrwpTFxy8m\nOoiL\r\n=ifQ4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDC6KPs9Vvpq5uSisBkbwGpjaco2BkcQLJDJG4QFm5uVgIgc1sCH8Xdh+drZagLsBPLbnZCMYyz49ZhzjaQ9qxEHKc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-dynamic-goals.20191202175738_1575309719738_0.006389325639795329"},"_hasShrinkwrap":false},"1.8.0-master.20191206133846":{"name":"@atomist/sdm","version":"1.8.0-master.20191206133846","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":">=1.8.0","@atomist/slack-messages":">=1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ecfacf2a7bdde627dddb418165120875c3065b77","_id":"@atomist/sdm@1.8.0-master.20191206133846","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-KWgRXScxQJhkF09s6Xx7VdObcRcO+mCrPwzAqBXCZMc68Eg525+YjkSp5hwDrovPgiss/OhI+s0nd8h4uZ0JVQ==","shasum":"6c61bab3e1690eea0ff840bd11e3a9f10e05fea4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191206133846.tgz","fileCount":1229,"unpackedSize":5542564,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6lp2CRA9TVsSAnZWagAAZDwP/jznnKGO4mil1VC9ZReM\nTszhDs7XsvnUKUtDVIm9nHAh7uwWbDmf8SswaCHTfmspHKNPGdW0XTMWIVmz\nlxwrQt++Mz4eIivwDRj1HJ0dpUESKu0vSsjS8THxFWUkMF78Xr33dmBq+zxD\n2KDd4FE9h1i20uL07h4qiMAQ3SOqtMTvpn0wEXzVdqcA0GxS3nYUtiGrDAjj\nM4YOpV07NKGrizxWGTyzAgyKMSvteO0gVvJ7YwZK4HyOEeDeLZx883PEje9t\nNYF7iJRSe3s44eSlNXZAAuZQ+lLHtLG3SQKHbb82gt+cS7ENr+TZZUOdrQdg\nYiF671d+0HydFQX3aOX54zgXRqpHnP4cc5iZvvB4GWh4ot3xbXxs9Z9/Mnd5\nv9E0//PzVFVfenscgFoM2CHKy3h0vsd6d9qxNUj5m5qD2b0nldEH2mSp9h9s\n0B+W9Ju8m51mcPpxdF0ED9jRw7oyPg3zgbSowQJdeZTfRJ5a95atbIK39Eik\n7v8fwVLNZkzTHc31PhX2NYNS/61zCc3Gr42E2QsmonnaT2r3FR8tczxurq31\nwBT7gx+UjjU0hbp3LOGrMkpWzQ7ln3z5AKshlPWCalJRyBRXjOnoeousptlj\nmH8tDas8ldoxYRDEaEmSrS5ALwCI9f/G04Wt9kKlRGtRsKmJ5lrWMT0/Z1Rk\nMk+c\r\n=72nJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDok2a2/1/0xsjtvuQuxx7N8DpesU2JF7DNzmJv1hH5OwIgD/TOqJldBVCXapXHXAir3bsU3tS9Pq4OSzUKqS/eBrg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191206133846_1575639670106_0.04856834007257982"},"_hasShrinkwrap":false},"1.8.0-master.20191206134419":{"name":"@atomist/sdm","version":"1.8.0-master.20191206134419","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"24dd0e20904b5b1f670d041aebb8f349e5c5aa42","_id":"@atomist/sdm@1.8.0-master.20191206134419","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-/rFtTOqpswZ14Uv8ymHgflOYnh/V2Wr8jU5ikz2RmkZZDLYeP2V/9FgbGpEevKU89ExtoPy5ZkhpY7JsD0PVbg==","shasum":"a1dd5bb2f5c13789cd30d5eff558ee25908298d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0-master.20191206134419.tgz","fileCount":1229,"unpackedSize":5542562,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6lvLCRA9TVsSAnZWagAAASkP/2JR6Fd+mI08uskrS4Ev\nvsTUHlqpki2vzFdgERuO4lJOqiEdi+1ZrCQLv7Kj3fTgEmgxyjjixBLM7kJ3\nnuTitjWsqArhNmGrCpbqsrX7M3GhLiwq7BZDWeAp/UBGPy3qIpSb7LQsWQ0e\nfwJw1OnV+vZWMjuyUlHim2uEsx00Oz3eHlpWOQGxH1BsC1+xRms4TV7S/4Rh\n2eOsZvHCHumwSi5cMdRSuq2dM2rQFELtmKt4OCROH+SfFdBiklgPSYc3+8g2\ncq1kCg+YOIEEUL4/Iekd84PwbFnP9SV57H1ZYjbA6jEDnVSWUvhuwFS/rikb\nkwxRaGKZw5G2/lC7LqepVgT11hRxFCI+dstBu7c+NYJ91T/sWqeh+0wG09m1\nMYDhzwR9gYcafGYP60GKKGpXauZ1HJ6hmowMjFp/2WE9qq7NUFdTi6jUf452\n+JHJXHLPeSPduyZSGxMBQDpIOB+fsDQv97bsZIFmsWtX8yZ3UKsGrJlyWDIn\nBBXJiKoDqY2DszHfujk7oQUPc/rbAaMWO+f+FJLB1Ms6ycdvPZCubU+qAd6u\nStMzcJ7WOdwGOBpq2YWfGVQA5GpNa0TGySr1TsYihqH7DrM+n9pyIJFwGxld\na8iut7e0AOeskx2u3Csb1WtMRcQF5IJ1GOYAkAdyQVAGQ2eiNiNeXXeALxVP\nyAAd\r\n=g4uL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHi7Xjkat2/OU0qc0vB0zzIrUBMG6U8BBcgTFUIWHItTAiEArI7BPjmENDmOIFAXe6/621NS6pNBPynTPezdluB6AiU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0-master.20191206134419_1575640010466_0.4871273104710805"},"_hasShrinkwrap":false},"1.8.0":{"name":"@atomist/sdm","version":"1.8.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.18.2","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"gitHead":"fc306310571bf55585451e63ae17f0b64c382e11","_id":"@atomist/sdm@1.8.0","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-054fWpFbL7kR01lNai+g1rgh0joXq0HlGhVOjzWXxG3p7KwWfIzVXvchFFk7klaJCjo/E7QhuoTZ7sftIyt7HQ==","shasum":"4d348aab3a172abada004162083e9f9b573a069c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.0.tgz","fileCount":1229,"unpackedSize":5542540,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6lxTCRA9TVsSAnZWagAA2jgP/jQb3dberx/ReHnYoT8H\nNpyqwDzCZoAwZFbXgqrjDqZxX8V1AaC9ROE7RWc40L86/ukBtDqjGc8cAfVA\nJdPJO/teI8BFodnPTq/kc1WYfTz2IUVkjzobjPSR0eTQbCHkdKck4W2+Jlya\nbfxRsdU0iTl7Pjmjtu9JvspY15DlU6It79bLY6X9V7C4W1eGRn45yLYhYGq1\nPBFMvsIUMBLV2YIbco9yT3YdlDyoMq1fIb/TFzUfHLA8f+PaL4oraOEbte0A\nKAr21fz0l5VFt9TgNr8VI6F5n4lNU4C1dzU/gPd/RgdqRe98XqT1sQSodEyC\n6DK0g2WbEX2EnCpalVt36+Wnz0pumF0cCPaKy8DZh2si0IZWaWiZZUlY8zQv\ndYYaCsc1tqaMOyvXHbyVdv+sdiDt0ugC+ooV79iV1UX97JrqS73Q8lofUHZA\nqnr0CrxTKFWMopOecYcMy4UUAQMcTANiRADBXy2Ffsfp0WI9nMjwl7fq8OD8\nWlUw/pFgbJdRTfNF6o/GKEo/WBdN+93dWTWAoqzKjRvd8gOaDtoo35oBayA1\n5SF8vWs0XN1BeTj1oXmUZDfIUJhT8td92LwNttiAAzqp1yCBwjXdBtbNS4aw\n3d3jzVVyd49MRGr0khMgLDd8oXp7sxx/OhuezeMM8CibwF1Cs8ItLYXhIxa/\nMf78\r\n=GTsb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGt4nkWzMQbZcT6DZIyWYZXhIdqAKBVH+2+mWykpq14lAiBddHHD++s+23HXkX6wIzUa397T3crOIlj0yqgky3IhJQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.0_1575640147016_0.9537318918326074"},"_hasShrinkwrap":false},"1.8.1-dynamic-goals.20191206144201":{"name":"@atomist/sdm","version":"1.8.1-dynamic-goals.20191206144201","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.8.1-master.20191206141736","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cde9aa50f566ee7a43a0fbe9a9320dcb65e93829","_id":"@atomist/sdm@1.8.1-dynamic-goals.20191206144201","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-XrHfwVFir8+5aK+wapXlIxgvWliKwC5LJHvCKJcfMZncv9T6nKAALNm1zLNxlXy1ZRpvEwJ92r86D1H0ZU0BkQ==","shasum":"19bd9d2d666d1df926a45fea0b8b43c160c9cfd7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.1-dynamic-goals.20191206144201.tgz","fileCount":1236,"unpackedSize":5676551,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6mlGCRA9TVsSAnZWagAAu20QAKTRo35A2sH67sYn5pBH\n9c3jDMMa1rhfXfFLZ7bcG+vNQXv9RFcKJmXbbrzJlPW9NyUQ0VLOd+L60v7y\n1mNGYB7PTDr3y0jqRHKPiee6ar37WuaK3a3PObAQTDipxfUXsvZidY7xldpc\nSpd2wpltJ0sDKlmN1Rdub+jClF3ztHtQLdCplf4pqUkejC59is64Zp09GmYn\nQDOaFYWuM/Ci73M2FtFgUDH+Gn31cAM58V6GV3BKpsCHU4dmwFsk//fWnjh5\nAd1jGU7ReSMzykm+e7lzXEco1HRrsTyHryWNNWAz8mHugfmr4xFzpldGrjzU\nZS4djagMVMglMRdq4OsB7znC+1BEfWRNl2t+VC0Bc01n0NWYxvre/Qyw+6K6\n3qJn//YIZ4+vM+BGgEP3+MTzOMLfHvS01VXTSdjXtbvhFvCv1WuQsvE1xPr6\nCj9CWOowVY/wzh5H2oj/Wv/PoH3Y+T5okN8I1tC0J4Exi3tAYBKxe2y1W5JZ\nh6LCnKyL88q+yZkYWxcszMbEfYi6xxzy0EWCwh5WRytSQBmXDBj2/7aW2Ym5\nwz90gSlXuoo+nS3s2Cpd58J0CRiIjA9V4jJEMiAKHC6u+FfBfRn4/d/dFcc6\nmoly2MXXaWd7WsatPjj3nkS7Byyl9UlnH2T/CKGN0hqaBmJakE2detH9MizI\nmgYC\r\n=5An8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBbHBVXZqbr3m2SjxeNKdJZCqUP1iVsN0RFHr17naToPAiBbklLcC1D9HsKcZnLrZpuAtrfdu8XmgyOFsgGP6oLbMg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.1-dynamic-goals.20191206144201_1575643461574_0.6639482479356915"},"_hasShrinkwrap":false},"1.8.1-master.20191206150641":{"name":"@atomist/sdm","version":"1.8.1-master.20191206150641","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.8.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.8.1-master.20191206141736","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a868b55b6791c85667a8379b7403f277a3f1c3d0","_id":"@atomist/sdm@1.8.1-master.20191206150641","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-9KOjIhr0OgT02Jwnyy7lWWClKPjTD93CCSiyw/z29Y6SJLJjt3QtXEsvRQfwas8sVa3elOysWmL/niI96Y3APQ==","shasum":"7561a56db578fece48d3c09f58aa1d941a1dcf3b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.8.1-master.20191206150641.tgz","fileCount":1236,"unpackedSize":5676545,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6m8yCRA9TVsSAnZWagAAQV8QAIXCl8ias3fq21KEjCzw\n6A/YWHdlu2Cqi5EOPu9neDV8HScKKf0lUq6WXRo/YPOPpenDGh4uSUzlWPb5\nFH7ay1t6PhwBKcqaNurHdHRaaMLf971U4zYHqWwyjtHXD46BgOusmSe872Ub\nDyG1rgpfO1gBAU+BCRO8o8BO8q4hGd7yeaHMiotLNL8XuRA4YS7pEPJfv89Y\nE4RpU50wnaxLB+N1Y7cxpvtknzpIK/dGLYHMlPTsTPmWbMzVVkoa7H4/82tg\n9/TMu9320q2qw4tuqr95y5kSSpQ41ep7GzUX2QRkVe/S8d51yLiQ9SGi+35U\nx50b2nLK07QUmwhzMA9iYmyduPiwN2gVNCHGJNc03HeYcRzm14pqZu/akcs9\nl/IWqUpT5i80wi1+OVlzrfsTsUbpBVMtfTqb+q30KykYL5GtI5eyQbas+iSu\nI6euewqc2Unz+C1FbgmIewefSiCT3U8W5F6Y4mup8vDVksloWfX5ihq1g64d\nB4M1wdgR7gkDvRS9rtSsenLBsR/RX9Yuu+0mnwaebfgpeoDJec7Z5y5ytTtr\nOXLThwknmg2UymcsEm2Uckle9itwt5k2XQarNvRSJDxF4G0DjVdtMrsvfZRx\nDoWwrVkmo9fXEMOt3w410Oze3vH/ksz5gYgYjV1LpNIQE43dvOmOs0zxSa8c\nynZT\r\n=tmGc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKE22FI+NvjqMNWJ9FTMI1+BGwV6a/oW3Spz+w7C8l+wIhAMHGxa0tQ4XIXHgDKshYcDQIOzjcAqHncuERv0kKQkHe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.8.1-master.20191206150641_1575644977514_0.49762559242965176"},"_hasShrinkwrap":false},"2.0.0-master.20191206153457":{"name":"@atomist/sdm","version":"2.0.0-master.20191206153457","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f26c5c4d5283a636015d1ce6cbce1d56a0fd5a49","_id":"@atomist/sdm@2.0.0-master.20191206153457","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-JQ3h78fr/Em6OW2mQ4BVPJ0MEGsUrV0xP12jG+gX1FRoL/8kLO4rWaQ+rj97oQTjUWKRSXaduek4ya0tjaRvUw==","shasum":"34ee7397c087a9d608d62ee636de6e6cacef3927","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191206153457.tgz","fileCount":1236,"unpackedSize":5676623,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd6nWyCRA9TVsSAnZWagAAusoP/iW0XPliMcmGfkldJ7QV\nappV+DzPJeMish7X9Ws5e3bJYxwovK+qCu40l2evwk5/T9wAgGkiNAs6ZvHN\nlju8oZM3d2KJ21Bsjc03LLccZw8/yWPc5t52yg6AJ9y+WuL0hbmubgDaLHXz\neqUUvOumfE0kf8A3/t4U0PlFnl8N3hklS2B862pO8ARekViNHpE12dUL2s1l\nKCntg8AnXlapuE497pCnaMp8GzpLkGlvoJi/unbINI6uWOL9tafYnrhPsUry\nNKwjO2Gcp/GfyltxHRG2mJ00CJsiLwo1eVofyl7nbvdDqJBtX5xRqK3p9Vgz\nSqByn6/3lIs0Znz5BmlwULoySyvv80OI6mG81dWJK0c5PZodi4vDkNh1itoH\nnUqhJLXpUsnaKjw+haUlzpijmDGsSDDs7Lir2FpfAfBM6wSoWPTd0jELAYFa\njemah6q2hkulf/+RR61zuwe8JuaJBneIjvtAVGJKkoV75mfXx/25zDVt127q\nfomBql+YUb7o3pIktqggMHh1BL0uYMfeLN7vdHlRW0E9QvyF3+7fIc0ep0qa\nqUqdXIU1nZf1TI51t7/0UB89cEviJA8F3TqGz0Us5v8zjleLciDRcYq8hZeD\nkwwE12i038QG3SB7+txf3p20DvVldGM7rnwJhgJBgT3eyA1HHTWxR1wYvPpg\nrWu7\r\n=CqTp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDnDX0x8J/Flf6zHPbBltt0v7kl0e/gjVHQD2lZr5wFNgIhALobfL11U4HUEICS5Jk9QSkYTo6cU9Y6EHgE0OAp3uvd"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191206153457_1575646641514_0.70960812003417"},"_hasShrinkwrap":false},"2.0.0-master.20191207100157":{"name":"@atomist/sdm","version":"2.0.0-master.20191207100157","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6a6c0b9db547b0bb05094516b72c886721398c76","_id":"@atomist/sdm@2.0.0-master.20191207100157","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-H5lEvjmQ9T6OBSwuOnEPbVjIuf+fu3p+lO/y5N0pDi9cBmtORsZn/97KKRrp1qX7kbmRZw2kd2V5EGI16vhA0g==","shasum":"137d0ab69e9b8050b35d7ab14d8a0685ca678221","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191207100157.tgz","fileCount":1236,"unpackedSize":5786085,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd63kpCRA9TVsSAnZWagAAeGkQAJGVo9zk0oCub33/26z4\nkzsxtgF4QdtotqsSGKvYlNOadebW1DsbsqyQ4rQY9zuDhoZTK5J6t706xjae\nPVXVd8qK+WBbH7M/kZJQigcKXyvxk+AuhZJi+riafZJE1g6nGOBGjjHB9J7D\nloXbU7eACfv8/P0Uqae+3h7swP6+/EE2EZwTrZjiCtPhAphriL0sLuUJuTwI\nx2dKZO99nyRRguSU9hQZzLavXxyFzJvc2cx/aRV7VOMl+Ey9wHCdjIN/6OlZ\nd5J2GoTYhNViCCUXlfVbY1BFuZMx86W9U8F6XPj0pEDiqW0ObB/r6fUZeIjG\nm96OItZG6v0C0XeFztxcwTzJ08Ek+6RpUvnOpbrv1y/TrvHGWlgOijN/vmsd\nUNSmXQ21YsTqyyQN2t0M1HVNuZOhct3axEnIYE7tTXT6dYgNHH9e82WXP6WY\ndTSoqygf/M2juJIgLqSv3BA1TrTqS7Sw0A8TNcBqlnbmCS5XlProO1M9S+GY\nPagErrR4OWHOJOmLiY0iiKf+5z/SsRZ+KZhp0Su58LLpLxCgZKGA7N7ZCGYn\n1Vo0J80ahAo+4p99qEb7OXSYZxkQLjL/TMQCnQ1Q3sDg/gs6tYrEGeIFqtRL\nO+NL+Gr1FAP11rpp8j9qMm/11JRLcLJwwQT+ZdZ+UwwvrJMyjGB6gaYM8FQc\nukbk\r\n=8SUD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEQyUi5CDBAklsybtz8isOqdsRdz61M6A/n57b0r9eOCAiEAhV2IZvAvbV4ve86pco4koXQKdlBFjtB6hoBasOD5lew="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191207100157_1575713065024_0.07831933769370081"},"_hasShrinkwrap":false},"2.0.0-master.20191208190204":{"name":"@atomist/sdm","version":"2.0.0-master.20191208190204","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7a116ecaa5797e4ec8c399b32e8c0ed5a12a9ae0","_id":"@atomist/sdm@2.0.0-master.20191208190204","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-C5SclZuishA39dGsA5rCotBfeTIFHfTamKWyL6YpIetrmd3jfepaGg5GC5QLK9omnTfSUvhzJzwfbiYEtWorQQ==","shasum":"92e5f1841dd4ebfd929305151b16a54dec7608b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191208190204.tgz","fileCount":1236,"unpackedSize":5786519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd7UlACRA9TVsSAnZWagAAbM8P/Rb43kE19U7pwYf+/0HH\nxMQPKJx21ldNeVwTYCIJYOY0J3K26cg/iaoJ3uNiP4w9pL4KCQCarsGHeKlO\ns02xZlFSfIS7jFhVRxtaPeR+QZVDP5yHNM5V7NN264kDAkQBtBlgupEoY//n\n9EiLkJr4yFnHcZf7RDrSoU6QvnLWUPXUxaydFZC3Bxm9b/Tria1xgfcMhFp3\nRAypa2uMXLFxcdGCBI+tQ+B3m5lj1XQ1kxChdQK9NcwVql6KfN/b66Tm7OOe\nyRy7fFHGdq92P3Md2JhBLMYikYlhJ8znag2e9ycI8xH6ahaVqaD1MCaBfjE0\nQ2aS5b6NUnnoc1Qx0s9FmRR0XOfjHu9PFzxgneZic8mR7+fbuOPkFt59nx8J\nLYHFqhxZTiPlfmtDFuA+sCvARhnq2wV3KVZszAhJJDAI5UWn7YaL/7PvBOfx\npvSgkyRni8hsTARyQxZPY9ggE8df554Nq2MtD1RZxoKC4w7p9s85npw0S0Zj\nO+czvgVHGxYbhADklEJkzn3oc//tYUl23CmcLAzru+IE+ZULvvRszavR+0Lq\nunQi3cFD8SVdivkMUH+X5Eb732mVqU8dFZxx25NOMO9d5naP9mw/bGLSh08Y\nUEEWMq9RaVMYMX23AaX2TqfehftJVMnFmiU+RVGznGXkgyMC7E0KkZw4viTc\n5l1w\r\n=jwJB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFP7fs6HcUXwpBQp8xWsjdtE1oivw5od7cE+UNFMUKunAiBx8nXLWVRDNb6C/WCU8+1l5TA5arYVnQ4hlvh9eZbfLQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191208190204_1575831872248_0.3384617825456593"},"_hasShrinkwrap":false},"2.0.0-master.20191209161414":{"name":"@atomist/sdm","version":"2.0.0-master.20191209161414","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3d194d3cb023aa0d3e46a45346747aa4cce94629","_id":"@atomist/sdm@2.0.0-master.20191209161414","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-xu6r8i4c1jR82P1HxCVs36xV5YOH9s9XuLxh8sUrfwOhSp5SH7G9NwcYChH0JS5GIlolTgwbK5OA4Wd7MkGRMg==","shasum":"98f179c45693258e43bdd7805fb82c18657478e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191209161414.tgz","fileCount":1236,"unpackedSize":5786631,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd7nOgCRA9TVsSAnZWagAAoA0P/2sIc3/LSoWyWJCHw3jE\nV9wXSppn9lacgGAtozltfnVUw1e1z1YEzeN8N1UttKhyUREJr4h+s4CvG79Q\nPegZfjTrwdnD0NsgaNLYCTd2lj2+5Qg5KBZxGtGrFKbiW2mSih1Su2MwiSUZ\nYoLqdn5XP/10tw/32VLNPJxRC584buSmW8YNziq8vMQPe7dfcKu12jJodFv8\nAYJisl9OGRTDwwUdERPzAo69RiKAw5dE4awWlwWMhKuh9sQNrxOzqAHKVrMd\nTl54DSUOqYhGW493BDV7L4GwlQrsZL5/0gNSQJKGd7gpzn9Eu3eofWLdA49x\nL3acorhVVRylB/fgkI0F2DMbeMWIe84Mx4ebon4ufgy3U7cMMPirvIR/9HsS\nFMDwo8IwvejGfJXHDN4h3SqZEqZQZPPTTZg9KmvZ529ZXwW/nQkfwJJlyT9T\nySTA9Fu2o52Ne1JrvkhXsd/3e6LBAhKb03pHh8XK3GBWSyJ1rDq+RXp2pmwx\nyNaYZJoEV83bw+pFk20H2FDHeM1BjFZcPLh7dX/pgpLb8HDXYD284J0lVPPS\nfyIiMUwT/CjkOlozyED1KxKFVv9Ja5LBGYTy33Ym6pyZ5oD/aFTXTj0kAIUm\nKJIuBEIaaCm6DVazSXgdSpGHIu9Z9IUloq1vkshAio3BmA5WsKjVnk35PCI3\nF7vc\r\n=/XTz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGPnT2LYeJFOfXs93i4OA59s5RCEhZlX9P75G2C/sFGTAiEA68ieAyn+N+23vCIpxnuRYGLm9V+XKnktMuF9WZq6AdI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191209161414_1575908255790_0.5656664834600638"},"_hasShrinkwrap":false},"2.0.0-master.20191210185902":{"name":"@atomist/sdm","version":"2.0.0-master.20191210185902","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4e9590ddd941c342a3ed3e0c4c5999a614126d7e","_id":"@atomist/sdm@2.0.0-master.20191210185902","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Z8ZdqzO9IfyYsV1c+i0zbRYBR5aTjP6s6jzfI3E4g5yAZ7X5I/Byg4KSsj0dsZk8Ho9x6PwtOfCQVFaXCrrA+Q==","shasum":"1dc45777f04f5ca28733a31852fa26135b64aa87","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191210185902.tgz","fileCount":1236,"unpackedSize":5786685,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd7+whCRA9TVsSAnZWagAAWAEP/RX/fQORfoXW7iUqBe3A\nR121wd1bgsYWWD2heCIMom9SgJ1qhPU3mS/xHk3wgNHAR/qNnb7REPDUvy4c\nlZSm4ElGQPkuxrzAXgMx8gRXnpkTFEYEZjNcnsNqV3bW93mn79rMYw/aibRD\nd2ryZqUcjg647CGxoiDSuyuDknUMui5Xil7P+wXi5cJpJnBlRqu6cwRaNcdH\nG0Dkn1BzWg9XzKWcwqbjzXlrhYjusVIUc7Vx4Tlg7k7o3k0FAkiOpovTDma1\n1ZCi/1MdVDGSzpkMnTXLhQVTq6UmYPebIyU2Gxd+apcg6rInrcXu7SqGYHia\nCZoNxuO3OW3aJfPOwjqHtAk4jGyj9xL7zF2frjNZhXL4k8kMEMEQV1wGOhkr\nbNWp/8tskzGhmdW+YegyPVvStYHlKWVJwN543XHSjuAR0ajxPGKapBhzo+X+\nh7bkg9t03vTae6A6yoYZAH63Swg4v2wO887h7HRIZh7Pug2QIc76PysxaulO\nfY8hHWcEiYMZsmOfAau4txJOclK342JgU0VzNK2wU7akJ+W9jy0ZnneM5o2p\n7Hf/CR4LJqw+5tO5OmadFVC8Kdy6g8VKfm0NuQxpMIUFvIp9vu52N79nJjfR\nk1+ucfuGYD1yid4xm7IplwNZCMOiL7/Ajly0x0iOtW/lEGGAc1ghgkVrhTxs\ntu1h\r\n=5w/e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHl2U+2f5LqunxMjUbWpbHdABmSE5dCBRplrmyn23GqtAiBFcE+BFNHd84cjXZDtxGUwxxLOJtSIfmHvMsXsa65pIQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191210185902_1576004641109_0.32218371300719806"},"_hasShrinkwrap":false},"2.0.0-master.20191212173159":{"name":"@atomist/sdm","version":"2.0.0-master.20191212173159","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c53a5e5901300ab4359f1136cab6e970027dd9a1","_id":"@atomist/sdm@2.0.0-master.20191212173159","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-yCU5NbdTnhcDIzM7Cd3on04XD0FFT+nI8ossarYAL6qwWsDLXegBB7ZBZy3+iXNYT7+TgR63cOvOBxWAygOhFQ==","shasum":"213c47ade51957c90c2d7d591abf1573b1c87f17","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191212173159.tgz","fileCount":1236,"unpackedSize":5789086,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8npoCRA9TVsSAnZWagAAuHYP/2lgCilTsBvLRWvYEYtB\ndQgKFTaZZgNv+f5IOzi4+e0ZqUX2aO1Ge0ge3sJK8H6rb8SavKnMAyfRDXjc\n4pMKx+5R1N3K+JEda6SWLSmdEfOqxPnoInl/0UjRTnFq0z1tgJS2LwxjZuko\nW2+rFmGPxVivj2cMCp96s1HsOBOieKws4xxVv66wRSVXd9rncAXGmEJYmpYS\niu8lYSQZjZSMMjJbIjZg1NBktGtkT0UO0k/alov4R2LWLX3OPSlYDp5JqY2u\n4d/CFOzewy018sh4u6gETppCycbvFIRCJvwPZnoFixx+gwMwSEB1UzuCu6HP\nx1vM05LYUNSfrRSo/1JMWa7P+jV5pl9PyNbYx0CJB6/1MMwiLT1oQxHKUlum\n5jp2aiWbIvpwr2TZAPQnOX/mf3f5+G2n1oaTLu2ekrPVvuqHtHs8mqJai0/+\nfswoQnynMUv2G36gmYzr7nRSJRHUt2qG5qt4mKu1yndD/l99qsgTTQERijyd\nM+pewNs6agmwqgjfx2iqJMXRJJbTIikwrdoWux6Ufydchxq41Htg/nij+ewM\ng2dJ9sNLDyvAEvatGTCH8yDO+7B/SkiLahSQjiiaMxJ4NWP0Gn6lES3EaxCR\nl7UaK7oPejiTPKdAaqezcJQ5Au4yKE6fDVDYX1EvNS1fVteDprS94UP3Im2G\nN74I\r\n=/6am\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF2L7eNX42h0yNzOKS1I10k4CuoR0854Pyyu6WfdB6GqAiBgvASwzmSRU+Z3fo+Z1Nwo5X3VOW2zd8qRgm6RnQu7Kg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191212173159_1576172135561_0.4675151156997277"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213002536":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213002536","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1d077b1c451c477c23df5b67b67a9dd932331c27","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213002536","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-xGjUkG/zVqAFa8TEORpxqDkhAol/tFnFxiLQJRGf81gvF3HoS6FsIg9rQzollYOn102ZMu7e6hdRVqMaA8PgZQ==","shasum":"144b836c9153eb3fe600ae681e1b16e4385776d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213002536.tgz","fileCount":1238,"unpackedSize":5792251,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8tscCRA9TVsSAnZWagAAsUsP/RL57hnJBDDuSbRJQRXK\nYOr1vzATjJiBlYF+pNNXv+a9tlStYRHL+MIkUwW+IQ1z4sgQbDyYBIfeE+Xx\nFlPEBXHJjYnkPY+0FAjeRKcN3Y1B3PrxBlahbLLu5B5bpLSvADolzGPxFfS+\nNHd9MDACQ1LHLkQR0uHEA5wxHsOhQu4NHNqZYvlqZKqu/dkcAWD1DMxBIADA\nZvoSlnY2NIj4/rKIDZueso+SPrbEi2+xY16nq+yb+FKCo/D7zsiU9ksXsm4c\nRolA0mFqqDYYJ/via/ZHSwYcONwXH/8YLE44zkLqNp63btuZ6W1N0AxfGaN7\nYgvKKSs7Faij0S4+D+k3GrbykVbvd5DR1xm6gaJDfz8SlGiAVzMdPzfhPzvf\njKC1BkQZ3rpRd9EsxClNXwf99vJRnE6KLA3wPN4cUWx3KzIFSXHQf3XaGP0g\nS+QnMj83NBkJFRLS6UUClhHRXTZlPCeOuy4zy273bZ+2CJfkJxouqxprx0bL\nfuCFeqeTbKC8gaOw6wYlqi+Uk/YZobSbzDlS2lUt0sCBloc67ZM4Ut7ED10t\n09BiGnaBSt1WjqSCkhF5mk/VuAxhrVrSW4F/Z0/f7lUZ0OYuop/CbRNyYNbA\nE/8/M6sHA9RyTb5fKU4FuQJkauzEWLz4S88TuEw+COAkPhY/D7l7qu4sqBoz\npnWx\r\n=WD06\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDQWBoudxPX7ICg2cRJkBjZRtuNdOKOyXP1xooSoGJ5+QIhAMPnz4ZG8tXQRDC0eUuZtHBJurOOQye+18Vv2CS0/c8p"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213002536_1576196891646_0.9966355175971653"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213003742":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213003742","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1b2028551339e57e23e211a5f4544c30df839dca","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213003742","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-2aNytGoDMx4+/3fhpgNr6I7Db83TiCfuaJyZBmVkJNxwn82CK+APv0A8hpvvXeF4qdHatY34ecqWXrOzNGScUQ==","shasum":"ab80e859de1cf1b3b039435cbf57eacea591cefe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213003742.tgz","fileCount":1238,"unpackedSize":5792339,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8t4qCRA9TVsSAnZWagAA8LEQAIA50uqJFVLSnEeOat2e\nPVC/ZPkTr4yZy2M6kzLe1EsMdLd46ppHifqiVCDyTSH/BFjE/MYyg+9qjgkH\nvpP2mtXnf8sMHnGH5g4NzwCIXGVFj0BVH/GidxJ/z1C+BQeltt2rV60aarV4\nLJu5p/iBSzM7E3yqprbH64TCBrIuyT+DOk+T8QDH3Y7rspw7OdPBYSOxocLh\nxgs8P0GGotFQR847SAgfzs7Fs+U6Sv42VURazPDKovfEFNfeC0nnBGofVTYZ\njN46RXVsMTxl+AuInqgO30fxDuUieTFfaNz4MIdv4DKLDqzi8+SiUoB9PWgx\nt4joalG45p8AsSh39THKoOt+9Bl/SMpRFcYsKwCfHlUE11ruCntLxEJgefPn\nDgaCvOZY64opw7OCWq550nOSMHAQXZ0QW9FVNYGNDVTE8s/sEeeie4hF6jG/\n7+iBDuxTJhVQOjrEHjFXeMfQ6aw5Z7C7rNB426HUlzSzbXisqTa2LtU8/Wi+\nGWO6NVyC21kjl7rDSreM8VS/r58YVrjqSHyxpMPQUOi1Ayh0W1q3P6tQLJBE\nhc/FohAHaTM/j4X7aj5Qc9E+ICPv4ovctExIJ1kUZEEyN1bVA3y6+7vU1Wi7\n7qYprTdEArYTr5ypNHzgPKv+877QW55h80Z2wpktjcxbWrmgwWuv3IAnVIs9\nub7d\r\n=p8q2\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD/TPcXt8W/0iVwt5fAebiM7dhfWLbfcdIY0ITUN5CxyAIgdIpENJ9uHMIY/+ZHq0K6cmLcUQ4wnKcTWSyu73bHj3I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213003742_1576197674286_0.9577322632944683"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213110738":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213110738","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e1cddc4717bb03c60e330c0c1ba97ae113521dea","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213110738","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-+dq3TeZtclHQN1eYn2G2gBygpN7hLMT9/x7xkeHPedkOgkqH60PTkBVdg6PB3XJBDFvFs06qJZpcPSnlph9mHw==","shasum":"1464cc16483606a9040c3ea1b6121c1c13455549","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213110738.tgz","fileCount":1238,"unpackedSize":5792338,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd83GbCRA9TVsSAnZWagAAZ+MP/1wmhjQFvc1vtXkzi4Nj\nkuAWtKcNWfIGZbIwcYWL+FUqq1chK263jp5LM9vKM8++XBEywCgq4xtNhlwN\nresScCVHTis66iHUrvgR9XKrFZUYquhQMyrqKQwf7sBfO7mRwCdfvDKzYyja\n4m3tKgU0zTesq8fJCj8ROQMH47Hn7hJZKErlRiSIn6VS9cZEzhDTXhjwXfMz\nxdndj9HQXm+io6hCFxNczB27CHzPHfwFzuD+RtseDjvQ7qgYG95/RhADvbxu\n5o+W0OcPL1Hiswn8h8SvLVjxHPV6IjfE6vTzoKunrKrbsxEhInBv4mysp48c\nhDTITLd55SP7Zq0u8aNRe+MmBhVR8TX8nkUXJkpliP7CaIn8VBhfTksRHkEx\nks4ZyP8l4pUcjgVObFhwiBDkDK+ZTo2n4YlydVJMZMKO3kMIS7BZGopJ4JhU\nq/dVTN3L+ZF8VwlO8Lh5trccU4Oyb9pchkFFQqt8MnwQf3o1eEnwiyRakiYz\nSlsIHISbfSQy8fLD+Q9vY1GgGhFxGVPKwrG0n6ypI+sPT0GV/kIv8ZhJK0bq\nlaDbeTlDvhCaivto7Mnf1DvSFOs2rakCxCZft87MrtI1Q4/NKVbFWMZJvKwe\nTO4APePcYobLsw5yK5gfLhXaa8p/VLFQCNqfsR0NTK8DRShpskQKbDj0GHW5\nYZkO\r\n=I81Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzFzIp8JY9fPpM1jwcafxZjcAK6UCgl0T9QZYIpC2r1AIhAPCXlZUQrpiKKWyhtdApku7xK7RtkzmyKLp5WwfYoHMG"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213110738_1576235418932_0.2617999716696928"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213111915":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213111915","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"252afe20403dbeff97bc8f271733fb14e08b4850","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213111915","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-MbGXD33Br77e3Ojt8l59QQH/5LNmOYd4xegnhOjr/L00qqc5xNkReCn8/YOyJlm0y62kvLeOrY9WA90PfNTd/w==","shasum":"ffea84fe132b7589189fab977faab307a93bb0b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213111915.tgz","fileCount":1238,"unpackedSize":5792464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd83Q4CRA9TVsSAnZWagAAeRQP+wQBdX3hMXiaWpQw2pLG\ncuu1Yyw8rcobtI1rGFEVrOxZ+5667dsGHKRXDXudTOIi3PEFfHP4G6tMO8ln\nkJj+74Ca1QrrVwlSQOvLVGc4OqW92ZsIwvzVeZupU7JcFIZe611vQUtf1UHL\n9mN5fdgzQZb6cir1Kyp4IFWWsbDA9xi2DDc1C1JNWJ1PTmkoDUvuBM4Bfeb8\n+jenZApL6VCCI6wjDyA5lvQAzEEq1f3bGcJmRli0MCrsbFUNhhe04X+oYAMo\n48+KPfkq1Tw3BnIK6c+GuIWCuBhwbab8IANZc/Srfk/diT2s12DWUorePY0V\nRzQkO6qSOijEhjcjNiyFyUDhCxC6nAsWJ0bOITPmMf2FmYvo6amRTAjqbn2u\nkTDk8GWXQCyKJsb0mGxvnV2fUN6qYrlBJpfs0zMQvRRd2MX7ccAWfARXl8q5\nJDpT+YpfyK8mbOL+cVVozLevucZA+qmLUJKpMnYZELKLmwstutJ9xVL1fpcj\nO8AIo3y0FkYNlWUzU6niEUk+Z0TSyyseennyG0aOT3xIOrhEDkF2/E+R5qPc\nwVKZO2+pEJUUa0Buo7H/fQ7WSnLX6YAuIRCiIrFB2tuOUIagn5d0lIsDBhwA\nLEn2yGAVmYNFrYedwJc2XjMJjSbLR7fz71wMq5fmaPo8OtSt+bKQA3esNyey\nsJXw\r\n=21TY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHCSYhAuqsyL+5af19H514dDm6H0bs0qc5cEg2HE4ZygAiBzUeKN6DFWqpXJ9DNyR8uYplQgxPWGtKNsUUVIg0KV2A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213111915_1576236087958_0.4627907705934895"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213133649":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213133649","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cf1bd10f7817e987fc43a57f03a015c97fb083d0","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213133649","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-HvgTlz/19fBMHqZ4LieVqoJcwy+5lRZmspBKZ2tZwE7QCYYeYkNjHduSS77ozxRYRB6EFv9EOAz22+VkLqKf+Q==","shasum":"51de75f148976e8b7da9b0ed63715b745ee0901b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213133649.tgz","fileCount":1238,"unpackedSize":5792398,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd85SdCRA9TVsSAnZWagAAIrIP/jYhKXtWARUNVciyemeK\n3EhSV8WjjbjrMpSfzg43dPCr19E3F2obClTdsdJzvInD9r6NgubEw4X2SXzL\nMyMIC0aa3cIu4dXMext2aoJuoj9XuvNbUf68KxfSuLP3kaNfX+BSgqRVfHMM\nC8n/NcdHyFQA0Z3FmXvw6lGLeKIFdBVQchNGR+5ORepoLLug4ErCzgmJ/2wn\nr5Yj6NE3qDjVD1EL0znnunGa7VU8aO4IRRPLCFVvWoNNrKyBya+shdBw94sB\nDMJPP3ZiefV8LZxvmAj5rEuHIjwjYeQcVA1X/46jkkl81LHVuv/sIeTwkXRS\nwIurtzPPdv4HgzRu3B6Ed8/87ML15eHjBgkEHQnt8Z2W+EYdkFOIP82B3l1l\nAwFPx4niN+SvOE3qYWhFdY2IpuGrncjnjSpjiwtOC4wn2p7CGVdme40Bcn9X\nmfNSJUeADTjl+qXabKK/CZ82ekViFXfurSt6VqZ7joH/AuN4Okw6oEuCVrHB\n9Etq9gRwGNMD1qlLbPJSKvb+xPMxNJp59Mb7STfcV0/e6nR589RmJAz8lJ2X\nco9c1yAZOlgqD0gJXUgz4G8RN/17jOiefBqhfANoBy29pvuOZG1S/eH7OaQn\nzjwZcVSktzSM4NVfFJBdK64nnD47uao93GpPPpHMhuO+GicNzbwT2s/BCfmg\nN8IN\r\n=iUjO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEhr8Yjlgfk4DvVU6VzANlGv0Gbw1JMxFLTyQAadgDYUAiAur+d/VTPVleEBi20/aG/BV24NwYpGNY20W6bipQH7qg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213133649_1576244380836_0.9908953727440952"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213143057":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213143057","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"144853254c442e02bf75b56b93e4d5fab876054c","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213143057","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-pdDia+HJzfRQVEXvP0u2Pl162Y2AbmyIS13oWbkBGGFCYaEUWG8A9A8N7IrNORuir+ZishCUppjW0hGvYiakUw==","shasum":"5a5ad306954da1342dc56d5c0eb2563b92c152fa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213143057.tgz","fileCount":1238,"unpackedSize":5792230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd86E0CRA9TVsSAnZWagAAfVkP/RpDRbNYwSGKmOGvRFIi\n7RGQbLLgZqLgc2Xsa7McN7x7H71bOB1yQ5EeTwNZeVFqFZNZsXN4G8AtG4aE\nZPfUugxBVd6fPAoyvSngG21zx15reCsugT+8xAz38baod8OQqetlLIVKezqe\n2Z4ofts+tva2dOkw7phIPBgOzAX6fLuFz+Q8CGo+VXC597hxpym1EuMwfJYK\nidVkoQLXY1HMuMmi7hiDRkGTpIE/4BgH2w7XHaIXakEo2/5d6ZJitSJYnBi3\nXTp7z7rPiSDaBPPS/+3+CE2j/4IHmSYUHrNIqYWhBT16Ehl+hHkdYKkkjx2j\n+H72M9B4KpVmDLCkIRQ4E6exSlgBaacsZuKMa1DfBMHGk99e2Tw1vwaFk39X\nETIy3yVzoffxvonjD6WHL2XbgfTRV2UPRhhrhKnEz9CgP6qL5mzQ5Kit1wkF\nEr8h31uDpPZtfzrN8MOKyqfebbTUppNMuAvt8s8VWTnvegtTAzROP574J5xr\nypxGCLMxwT3cvaquLBaT5mOqFOQQdJ8xct5En1xENIcnKzZt5FTDlWHTaX+O\nBxuvlo6FrDQ97AT8IkW6mop/ZMFlqTPCmjSaf0BVnx7IUKnjfkf4SEITaRv9\nN7rpY0/MSv5WuxukKJzMAjE9RO3GPbayn9cru0zoAx7LuGUFbtaV1/u3Pnrr\nDkTw\r\n=ibX9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLBkp68HMQ5rN/Wxz/Q578WG14qdIWjXHQfSo1Gj5sWwIhAM5993FvauGUnXVMkPJcxqUYoHb4gJHayIdNCW+alelk"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213143057_1576247603586_0.19557348743950853"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213145220":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213145220","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/omit-empty":"^1.0.0","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6b7f701ec73587572e45f48ee32295e8b0ebf7e4","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213145220","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-YNsbiR3TgKAFroPSfAe41NJWOzbJOuxShpu0nT8KWJn+HSMMBRiZC3Z6AwHy4BjJ3ULZyGPPjbHugDrvbY2IXg==","shasum":"0d62e30254a3338dd6a99dc76d8066b913ca877e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213145220.tgz","fileCount":1238,"unpackedSize":5792656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd86YzCRA9TVsSAnZWagAAshkP/RgDR1RtfYJIg75FAx0u\nVo7ok2XMevaxgkF5/3eZgGFgIh749WRrTT4J5rW8yeo1bXuYLgAmGVfdUHSo\n2/LSVgUv66QyyhbYmUWIygEYy+EuNuxNKEQQiidkn7YcVppqjOzvUTTGCjJJ\nBv1w3XYy8BDzsrAR+/hWYf0K/iZoVOtogB3dm5H8Ch7Ttnzse1X4e4Os/EfW\n7r+FFwQ1lN3ntyyGbrFZREbkXAgMLjEU9Bpd5278lAJNzAsDF15SDjn0fGLF\np/tTVXnAgv161fMm8Q4pSPfLl4GVk1hbvon7HTAVqP9xrfnFGvabrKDrpXJx\nYPBFV8+RVM83BrCdoONNdCh25b6y9k1eAzo3DnzySf/aRcCHExXQX5ubbENw\nsx86tGJpwhk/NoVXcadcN1mH5QliJCIBbGA7wXSq5ebpIaTJ+4sM02JpGvc4\n0N2bXoTBNY8ayXYPI90lz5JMjaiWR9uH1EpcZfcuJAUkvbP4553b4uH2Z6zZ\nUjzwYCLaKpP5eJAdadD+wkS6ki5V8FJR/A7ZG8Z1b1xchHNuja/PmK7K9i72\nhmM13lW05Q2WUm0zR6BoMyZQ1VJeGxaNa8tIOaL0Dr/SoIVFttpnYiPhYUPH\nRKi8JyyU9D/gkTHOFRfABNNLjklmJVoWWJ+x8XozUZ9vPuPOHHI9j6ePQnwh\nkSoz\r\n=4din\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGNX9NSm0EfFikUfmgLwpNtKAnfFDUnLBZqLckXVJYwBAiEAvwBwGxuCx9kwvyZmRJd5B+uVnK2aoHa/iFONoOWaWV8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213145220_1576248883248_0.5245778329277493"},"_hasShrinkwrap":false},"2.0.0-sdm-mutations.20191213211016":{"name":"@atomist/sdm","version":"2.0.0-sdm-mutations.20191213211016","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d917761417a2c7ddabf263d8d937f04b9703f45e","_id":"@atomist/sdm@2.0.0-sdm-mutations.20191213211016","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-jAOw7dSYqsVHGqCf5yps3uHVYHWdOtQWS7ef2bU1MA0TKZuQ+rrn5CDEXIBSru27XVWfg7u+BA55qkGMeJvI/g==","shasum":"0e7e1e89b5bd55cbbc8e4f82fd7ec0e73fc05376","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sdm-mutations.20191213211016.tgz","fileCount":1238,"unpackedSize":5792554,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd8/7iCRA9TVsSAnZWagAAhwEQAIWjfnSegLb9Qa6gXXuq\n8lwCZYVRq0VyKEz9Ln1xY31ScAf4U5a9LSxRwZ9X/a89iJtYVa7ceoamsx8i\nxuUvagHiVpjXdnV2elXXObT/w3clEXPEPNBvcK2PtWHK9jYkti3u87PyqTat\nmnUhWytGi5qheaPdyrCHcn+NzZfr3Ye4IU8udG70YULGO7lPUTI17/UH+YhF\n4kF+EBXCTUhHYKUf+6Jz9tGDLtZP1DnUofuubXeqefXEZQg5A/+NggV6jGdg\n7eXY3nujzDZ1jxuM/qv3pggRgyBdvObNX/4UjcfYyteTzGzZ8ViRJ9eQJq4r\nPI/0ymBqj5CM7EyTswP0Z/NiKTC4gcXcshptlETBQtZJpbI+mUWV+Hfv15a+\ni0CIXczQCqcy2AYZS5bx+6KDZLGP3y1keI81O47VL0GsL13F1ZMUm1hD73HP\nICLH1+FvLhY5chQdgax/TC+VOKlXqcCFicH1lDkYaCaB1VEAP4kIu1u1Vixb\nkBjZfzccy1283r7HLdk0/aXaSoekejITPZuxG8Y0JvJz8GAFW5PD4eR8Up+o\n3tw/LHf3ewvpeLX6mhjrBYZ3/MqqGSWPU7obn7pw/oFsbgMLDu4zDAUi8bgx\nASz9bAzhxcZFw80EVVQm6ATBHNDBCtjvzbvabTt8Wzce49TnK/cu4wDKTSLJ\nw3+E\r\n=EDAl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4/k71HTZ3OTEuHv6AEYR3k6F8eQvI73Zz5/UrKMXm5AIgQbVXwCq9lCCQvpWgWHupTPeuYZTQWjFcfjMnP5Py4/Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sdm-mutations.20191213211016_1576271585743_0.5763358039727227"},"_hasShrinkwrap":false},"2.0.0-master.20191213213803":{"name":"@atomist/sdm","version":"2.0.0-master.20191213213803","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f41e98f43b9ad7cf20f20c523b4921451a9adcf9","_id":"@atomist/sdm@2.0.0-master.20191213213803","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-LPlTc6lwfcU7uSWWhAJ0Q6Pf+M8I2wx/ZDHRDYga1jF6c0qe0XchZ8AhIuBYrvowMaQtCgJfqle8pdEAzmzZkQ==","shasum":"0b20a40aa83c181ed046221c894c6a050212f3cc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191213213803.tgz","fileCount":1238,"unpackedSize":5792924,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd9AWPCRA9TVsSAnZWagAAuKkP/2KiapkOYoDvGTNpdFp7\nMJZaokk2GVhndHrFoJnfZc6WZqMsqgaAlP+//WM1YTZZ5a/9TZej2xl/VVN/\nxp27yhYdbqNHm1bojDNPca0yCQKsYIVekVgWoIIYCcM8sQN5pisnJeJEtlDn\nSgwSU2S27JX7VP2b1fcB4ktEDb5Dp848a2NgJTi0jPfuUxo6UAqWO2I1DZeq\njlOE4igxuNjdG4tfPTUM3ZAQ491kzVba6F9cKfaPX52xDGKvBZ1cd/TqJscb\ny9NBBsePkgqqr/T1JucNXY/eV6KKEQkYpVhjkD5qp6gPXx3j7Tr7a4g3O8uR\nmYdCb2ox8GkE09WgZmjoE/FMb9YQHYYFC+fQgGCQgsp6b42Ti5RD1knCN5s0\nKR/6PL4nWmBvULUAMhVpdXRULGvH2NjF553MmlYTh1STja97FK+u3HBN3vOH\nsnTAJewFhbEJyWOBACTQT7V+QUFag7m8gq9vVc1NQ/fgC40kWNoNyhuEJh9x\nEYqoDCFBZX+eMV3LAnkYr/XK23zZAJpjo/Hqj0jNbjlgdIIJLR0eOOoeX8fc\n9/ljby+4uF0mAzTi4bLTFH/+baNlDTfR6e1orhX6qjCMuWBZU6jMgHDsII+9\n9CsNyiLE5MZhoRzc5YPqDRbpWkYZdkBhcJcJdeZRZffVHwO0j/TwvWYWAIfY\nuA+1\r\n=7/qX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICC3IMen6f2I7dsTRMtLlTdPA+/Y2T/nglIaE6Ko3x5NAiAHRb7un2Yu5B/qMq43L2B0ezQfmFBnRbW/2aPr5Yovjg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191213213803_1576273294686_0.6242167696586258"},"_hasShrinkwrap":false},"2.0.0-master.20191213214023":{"name":"@atomist/sdm","version":"2.0.0-master.20191213214023","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f41e98f43b9ad7cf20f20c523b4921451a9adcf9","_id":"@atomist/sdm@2.0.0-master.20191213214023","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-kUzJsVrVGfyQIRvxQdOL9wzduB0W7BDeGeLm69gDbXb1X9EtJNJEY1YB0RkxmWrfpO+gA8TmKwa4F+a3YeEfQQ==","shasum":"0bfe5933d665bcc4d5009d14ae9935437b6f3b5a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191213214023.tgz","fileCount":1238,"unpackedSize":5792924,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd9AYDCRA9TVsSAnZWagAAV2UP+wTdyeuVZbBYKV6KnYe6\nQYCltSzd0xXq2XyR/SNWuRPENrAHA3yEKWtZGzBZdYeIZcSxlReXT9Frjtz5\nc1sJezjUwKxEwCfWKQfeZhfHhw/Ph+EPOtWcvQaYMh4/4yDggGo7cQmY2tTp\nQgYx66qLBT2kBB7pMj10h9zhheBBkVbPuIeWBVDPMDGtyNC8dg2b6X5yiLVh\nDlxRI6gpcLmCFTdA5tHR01xlsumtMU2S575M8YhyDn3ii8T988rVj8gbQ9rS\nTz75WaE5R2tkL6XWuxHYFal4hx73IkyFQ9rZ53LdlTd2VRYsH1233JfQBexC\n/WUe1GaFQTGyc+JXQkPOI39gz00hAD7GONgQFRQFEhdNBetV7CUOdeVLcUNT\nd/J+7SKg0KfqcSoXR4kPLaZGCMWYh8M5BVAPqdQDbSstDbzCDuLVLi7WTPzM\nB8s65Wj6t36jBO2xuMk1R/+Rw9kmcO7rrmFitvV9On24xuky9sekDTmXm0+9\n3fsT6NGyyd0XwKj894I93X9blBSbKwiC98amNoATKK+Pk5Aw9Brjri9wV2Wh\nJmsnVQbEbu11MuBLWMoo1qQ7XSSyEmoPhdidTPO9u+QB5c+0r/Pjz2csSWST\nvekYbEwdXzIP/TRKI/kiGXlSy/fGyEC0IqHQzXFx3XT2PwITexTP1dExOnVT\nq3rI\r\n=QxPr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC6uWcly/BseD6pPQzetbbentOdKMIMK7+y+ceIwvE4LwIgJ+BvaBR9u4Rv+yO/wwxmiiZvGbedsObAuiD5tZYTo1w="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191213214023_1576273411088_0.9601378022587563"},"_hasShrinkwrap":false},"2.0.0-simplify-k8s-scheduler.20191217085758":{"name":"@atomist/sdm","version":"2.0.0-simplify-k8s-scheduler.20191217085758","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191206152722","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0c0330befd90a3668f7ced85d3d9e30536cb33fe","_id":"@atomist/sdm@2.0.0-simplify-k8s-scheduler.20191217085758","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-hWszEa2/mVF6xe3h/0XQRUEhUGESmSZmjh+I3V1pV59MKlGTLMbepE2pWpt2sqyzAAwGa9T46F8wcgZH4vSX2A==","shasum":"dc7f4ac18fc954dded2b9add478d762dcf8230bc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-simplify-k8s-scheduler.20191217085758.tgz","fileCount":1238,"unpackedSize":5793639,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJd+JklCRA9TVsSAnZWagAArQ0P/joW8OpC/7fFUYfFmda+\ncPnUym28LRdQIyBNB3QHL81IhKg1AQ/pxgij8Lm5LtNW0ZzkflqiMmXEN2ju\npQmHdFdcQgkkB58Fsk7fBlneYMm6B+pfqKxIty+NNzrhPaeCkTSliXHqq5Y1\n7XOJbsnToe3D4zntaedtGotGzQVK/zo2Kt7iAZXmGPld0lgcNOPusZsKqj+l\nJ0eDzNqcjg2duQLUk04YUh2mweA/JkQ1RmafIimFqrykULGVj8muMFvxwOIs\nhD6ZCDtOHCsbARh54D18XFqyAYfcox8vt1Gd7q2Vh2ZmNsf1PjEiXxF5iT9Q\nRIvtBTSwsNqLFG5/rF5iGx4BR/LiLUdEeDztr++EQSoVkUJRFDR9iiVrEKHi\nnKVUMhe6VuTFPoB3bIa6xkwKtsQkqNCPzszbvz7pO9ivU020iJuIPnegQ7rz\nVQyx5z96tzRpKGw0ncl69qOBeICXjDkyfFbEqWQuXDLnjzAaT0TQ9OJke2uA\nilCdRVeuAT+3Hi3l7ebFxYL2G1WxwPvO4y7RKBBrk3FgniRMoW7VWdh3lC+6\ny7rLBRonPyqhottzcofO96+MFPXwfvxMuSN5fPMRp39Z4OHx/YYVBXJfWbVY\nADmx+GuYveFVcozImbdQ36WngQASaiUEjOhsCJSekkqe6UgcRFHkUVPv7Din\nSs4k\r\n=G9vN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBZqlCIUD8WgFJns2urBJfB5v9+FsswN7026gPeXfONgIhAIsKKUnGYdDRWZw+Md3Vy7U26XbVDtZg4DTZpPp1P/By"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-simplify-k8s-scheduler.20191217085758_1576573220637_0.0730282176433108"},"_hasShrinkwrap":false},"2.0.0-ipcrm-793.20191223231011":{"name":"@atomist/sdm","version":"2.0.0-ipcrm-793.20191223231011","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-ipcrm-sdm-793.20191223223841","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ac92e68b04bd92242b7535b89b8de0019a137365","_id":"@atomist/sdm@2.0.0-ipcrm-793.20191223231011","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-MLqxH2jdDtP1Kab5E50HmNKu+2zJv39W/2v0g0+Bi7LYCIxuhgXZIjyMXP3l28aEDCrGYBax5P5KYEd1m7d9qg==","shasum":"ed4a3095972b9b4330d2ee02681a2fa5df0aadf7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-ipcrm-793.20191223231011.tgz","fileCount":1238,"unpackedSize":5793964,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeAUnTCRA9TVsSAnZWagAAJX4P/ipTMNWpM52i6wpoEt3r\nwTfK1PqCZ9PvSUqc1+qr0I0eGA7iKpN5llzcfJhu5t3CAFCyRLOehj039NO+\nxbXjBMywuZYRm0o8LmmoRrIdcLsPeoHjGGcnaj3UIb8/GiWOpnDLftPrL2nt\nnu1qM7kqpB+aY5U5nmQ4Qx2M6C2vNNv9GWrfxOcpXyzkI2PTn6zTiGmNvUnx\nGibpqqOdo3XCaZlxwlfLE1/uFUswmXdHF7CX0FZRr4e2l4+zmAN5CDMFzoyb\ncLWzZEvtPV+qwKWxHzOiWznHMfA+x+xvj1a0zpUbjpvHdrX4+ZqhgdQynuZF\n9O9mpODyxQtCsprpMYIVNhWSNYG3djmKn/B4RZX6BtK7f8DDALERChpvCIo3\nGnmI7SB/DbGu+e3XnKOrAFftqhhjeaOyKLy4fPFiwmkYFgmZqqv7h8bxGoYR\n9vaQ+pZileqvO2QQfq+Qt4ciQDxGlArbKM7jYUjmXPlzCKQVTJnndky/KsXL\nzg6prwdi/3s/pMT0DmqRgzGKftJyX2JTQCiB4ZN6OaZyA/mrffGh0a066VeI\n+Cw6VtEu6Hkm9+KbpV0QRWglrHr7JChoWRvtqFZx0546y7aGw2sGembbLYnQ\nArKQjTfBU94efKriuZgjjavbb4MM7WvqM/gLgoUuQz1Lo+DRYE0sPE4CXmVJ\ntEEd\r\n=DBQ1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBho1xV1+eUOp4HbNiBPbHXoURHhYRAdr4vSLOLZ57SQIhAMkEbhMfV5ehNsy8/gf2rmOToGN8dZeAqoxvwSkBn+xO"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-ipcrm-793.20191223231011_1577142739076_0.36457846265155114"},"_hasShrinkwrap":false},"2.0.0-ipcrm-793.20191226133958":{"name":"@atomist/sdm","version":"2.0.0-ipcrm-793.20191226133958","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191224225156","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"430e31403458fb4006d93b00732640c6dfc2a1a3","_id":"@atomist/sdm@2.0.0-ipcrm-793.20191226133958","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-QGhXnLiLhYzH7dVC+7XRFueeLgOmRmYshkOjR6IIhvkezGhemI9hUzwi4scn3O8Kr7vlCm/dzL1Srzdqy/FOVQ==","shasum":"d176750f320569c299ae0814f4c91317c81c5089","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-ipcrm-793.20191226133958.tgz","fileCount":1238,"unpackedSize":5793957,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeBLi2CRA9TVsSAnZWagAA2s0P+wcVBll9D/4gpYIe/3VI\nwYLXJgc2AxS3FFpBFqnNljaeH0I3+QZdAbvuLZ747rjDHzesa7Vj4RRI9O9X\nhJJe3k8r7ML9kthpq0jKyvhF1+ZFxD8RzXWfOIlevb9mJKMHkeS3MklBCA7i\nWyR/+sAPVhwz1IZiKQ47w3yTIIIS2veOxHcmaNPhYuknhJ6wiwKvryicY5ID\nFFCxdTiXkMJHzLWdCpfRYsMq2fYLCZNhlCMg1j1WpDWcKky7k9fNnAsHx7Ru\nTZ6tr0JC4R+cnoX3D6GCwJ1YLRHQX01OPOminqkUR+44HRP65FDjZ5OWaHJj\nf0hf1HK7lKTCYhXcp9hS7HZNOapelJ/DPk7NEQSxtz8jhBcsorJpX38ybZ1h\nxKlvM2TJHI3jdTXHQSZ9rjQ+ojQMzqc3+UvBYdA4tcWNxNMAyvd/i19ukTuQ\nLunCLv7T9gjJNsvnIZfPlpO+twr5XDIipyWFaKrQLH5vO3Itr72LbUB6hehV\n8b2QO5Ec+waQ0bfIdZfDGA4I95wpLnLTNAntQ0RkarfsY6Srf6gGFy2OIqIw\n/XsX0efwDlkLT0XFYK+2P1jD6U778PHXSih7GYVOMCyojavvZxnSP5mzS5ZT\ndXF09ZzijKBUVQ0aO4Z3A9rKiVEnGbtIh5CFQfAJsXWalxLMvOHo6XWfPp7y\np1vs\r\n=rKeu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBvEd3axs9Kc2XkoSvUghSRxEDdKE4wX+m4ATKPVXhFBAiEA2l6w8TCL6gYBx+mgwCBNaN8DMqQ5f9Yaff3F0tfHD/E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-ipcrm-793.20191226133958_1577367733626_0.41241962975992186"},"_hasShrinkwrap":false},"2.0.0-master.20191226140700":{"name":"@atomist/sdm","version":"2.0.0-master.20191226140700","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20191224225156","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"929b7e37d17153a1813a18c91f42a8c14e94a4ba","_id":"@atomist/sdm@2.0.0-master.20191226140700","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-I1myEFmWBChXDRAFPg9ej96uMkqfDzi9uNs/GQ79saHZJgfiAteAX2Jb8NXpPCgnr1zw7BX8Nho4T9k6MZpQ6A==","shasum":"964b9fc648434ca1c2c8d6a2104cda6464da6bb8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191226140700.tgz","fileCount":1238,"unpackedSize":5794068,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeBL8RCRA9TVsSAnZWagAAU+kP/RrZN8/HaVNCaoscIXkT\nd7OP3okkI/IURmTu5MH7zADLpPr2pPBa4T4VfKgnFFgCdiHpKw5hknhqQIYw\nTRYcaHHsAj1ZfisrUPG4wWBE0JqNsxYAUUTZqCxx76rhJnoFMDUji1/Xuv/d\nFBax+5OEyQ0QNblq/HrgIe17Z3C2lWK6e20EFCGt+warkFQWr3G90Z/d8rn3\nb7ceOfMEJ1pS/g753Ssi23TvNN0xMefPkq9VsHd4JpIYrren+QLQrHI0vmga\nzO9u6nXv+ossAy1ZKxOTlaY8Wvj1wy8C+9s9a4K5xNL4Onn1X16nmOA3WcnN\n/6IguDD0+2jWCQgf8HZvalcC6js9467IykvQkeMpVFZx5JPRLXNTjKxs8lSv\nYr3wOXGvzqIFwsJeU94XdT+UK/3mp6xL1FXWHB5ZJqsNf1DNDfo4pZhcoRVu\nGuzRxT8LPCrElgGiQ/ndIzAeghazoFWm5iM8ZCCL770u6hHmxID/hEfHrPlz\nHlSgprtZEmQt3lbRV1Hd19W5SA5Sbg/vnazZG41ZLHWOuo1mEfxO5Z+7voiI\nhvj70unQO+sFQYz2leplTIJcX7KpNWO+ezi96c5+RrKKqy+CDjO0WPeEPMiW\nRGdA5HrbUenUtJTjLejWyP8St8EWJ29LjGOnaNwCUeBmUTclJFQ4v16Fc1Xr\n+QZk\r\n=JWEw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9Tl2X7G7ree4IQJRgc5K2o71/y0eNggTAqK2xUxLEtgIgPV8PKkCX1N0cgyHFJo6f6/Ga1IOS5QbZ8guqBI2BY7c="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191226140700_1577369361258_0.17080653620091013"},"_hasShrinkwrap":false},"2.0.0-master.20191230163626":{"name":"@atomist/sdm","version":"2.0.0-master.20191230163626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b706a0a1f86921a4e741e0805be1c71ebcd23fef","_id":"@atomist/sdm@2.0.0-master.20191230163626","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-hOmKezqwpXMm0hT1nC0a0jteTl2ITlTAhyc+AyNHKS5yWVPUKpGTwfrSxmLLAePe4UrJSBnTuq31giKxpusMeA==","shasum":"8a466b2765434366af84d6504331d6a22e085fd7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20191230163626.tgz","fileCount":1154,"unpackedSize":5690536,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeCigICRA9TVsSAnZWagAAabMP/1HiqqFCZHKpJx+JnN/Q\no/smqLQImYAZ9UQWbbVYqM3YWMOURP+KjBms44O1aynLVKTHEziS0DcYt8Rh\n3Mw2yntiAoY1K2SSX7/LXwi+L96U/MQ7ntQ9Y3sivEaUf7RVXQ9aaxhHBAmQ\nonPBWlJJZspHLtxTw9tANJFNoc6KP0h7RktOGFi9uxBceLIMcHewV8MXgGuo\nbCom6MFsJWSktVoSTy4f8c+O+95254Z0ogtXuV+lQ93YqG/4Ri44LPhBEkAp\nQV1vVvR7sG4rSK1yjYKG5iZ6DAE9eI16fcCrStK6s9NYR6VUZUIf/wW5+1Kc\nGOasgXCAlI6W1Ax12fDjk+aCCL7+2OEuFDiSTFYHcKwCCcYadBQqz1Hrp8Ky\nDcAFGINpNHEcUZj0XT1gbTnnBTcm483aWURf3+sehXXXS/8odUcO6cRs5fvk\n5nFwgcSwvsrIXGJpjGoISeffIBjZPJpBEPYzFJv8VmAMOL1IQKfbugKY3YBp\ng/y5APx/DZCrV7HwmqB++Vg3AANHZXDl21QqhxM5DcdO7LbErQnbciAB519Y\n+G8hR6b7Q9J32AqBRF2S5EvvKbALUdlWptMwY/tC8fAxDMpK1OxSfzESPoYV\nExIQ9zOgFzOR/PFWSemXRoR/ZT+Fnrwqr/4+X0fr/cgTWGsTFk5cJh6VRigj\nUYBq\r\n=Q/Hj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIArFkDNm1SRr+x0OloElM8ryGvuyPhwUCvYzw4uoYchXAiEArVJkToXnZlRIzEIrwi0VVLR/4MLDLYLb5AmlQPfff08="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20191230163626_1577723911771_0.32267950859867267"},"_hasShrinkwrap":false},"2.0.0-remove-index.20191230181344":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20191230181344","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1b292a8d5689da014aa864706f927f8d083eb09c","_id":"@atomist/sdm@2.0.0-remove-index.20191230181344","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-dockfKN3uI1G+u5wwCgBt5VdEsIqB+8nycKvpdyLlQ6sA7LGE0sIqKUdsvlht0Uk6+LLRE6njDF4+IxAKiordg==","shasum":"800c60ed6e5be07dfcc13aecdfae2f112ea2c040","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20191230181344.tgz","fileCount":1154,"unpackedSize":5690127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeCj7aCRA9TVsSAnZWagAA7O4P/ij+Q+Jyu0pyaEMY5G2d\nTPjagRr3RnnGmStyAG4gRgbjV9yhzvqMApjyLSxGe9XoUKPB4GYWPuuBN2Rm\nyjAx95qQGNyNCdeDpaZyddkp2ITGqNqMsYFgfxA72kby7lb6UOs0s+AcErp1\nQojUnrjgf6fcl04L3O0Z4mMRQK4nz7tbPE+4bMENj/rB9kN2Z/C7vrHuwO8c\n7tlEtxaIenTGwvezT2US7qaKrVT2bUyH5AR5TkUDbf6V8dSfIu4/ckBJjXOP\nwIhSZ58ylxEugh9EqflK/k/9o8vmNj0EUUqzNJiZNEcHl7D/+5+ZpOTv3QY3\nu89ELY5DbQru6U4ofUirjW+NtCnxWp2EcC8OZtjiGgHHA7iLXyKPxIB9mq+k\nt+zKbXh1GTnMaLjQgkHyfi/jh9L3EC2c7d5YXbM5tT95fyvMW1lznwhlwJ0F\nu6vE8DJiJNrwXQjzA8XnPahdaT2zYG4oMkTyOiNRV8B4WP8Ne74GW+EPdsux\ntE2B+2JpCRquUKJDpsENI/aOeJMgXYd6oaT3ES4kBBxblvoe3nu6I4rydAcD\nmloSkV3SlhsS3vpcRj/7Dg0s5HOlE3UZn1wnJ8+ZMiFiQ7FrtAnqt4kBZaRT\nDEPp50tBZ31BtuV7WaBYdANMg20h56mEknOUYXW4WxdAAuBn/Gk9oe+z7xev\n00Ej\r\n=fPFW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICUy+xHSSDYx+CkvawZ7AyWaun+xXx1jR7o2T86VWzd5AiEA+9sWqjY1AJ2DgzKadsY3Ml/Yka4ETqwJ2zGK8uviofE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20191230181344_1577729753688_0.7245761400911364"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200103222253":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200103222253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a801a3510757460ecc1344f8bf2958644c5df089","_id":"@atomist/sdm@2.0.0-remove-index.20200103222253","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-mftBm+mznbWtUjQ/ZWqpWG1tds7tUUEWMeelwGSc39TmnT//YbOCWXCSGwiaXdrKX0rkDnGIpf+YeXexCczjnw==","shasum":"add2827b718a4476245ef8817fbc9d513be0c478","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200103222253.tgz","fileCount":1154,"unpackedSize":5692152,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeD79ICRA9TVsSAnZWagAAK48P/3g1Nl6do6lVcZse6QbE\nkLLPLHn1xJn+KouFnIqsyf3kqmLyL3PBrcdaXBFw4TIUgmGW37nuTa20YJvD\naJ6BD52FLKYRzSxfai3+iGMf5gfEnF/VEVyUQlLDqXL7KjvABWRXEuxuLhRi\nm+3S9CJ9+ONcCDrBDliCll4uadB3Z2YDCXQ9e2QAiObC7DdQgvdZGt/P9uTJ\nhglJGAGHh/J7SQRdQ5mx6TS3soTZs/3gK8E+wWGUHsdKy8NiUdsZj54XEVPB\nwjTwt6jAoJ2vLCCYuioH1b8Z58i0LcluX2tkgOKWihVTiGzV2EGYMJe0LMkw\nWvLbANBT9L2+LuNtz58DRTRrZbagS5txdbUdVjnPP+YpMFF1sb9u1GkiqnBG\nj/HEHcP5XWlXIh5V78lQjMum6Qu04diHoFlf95Hsx5PTn+mL/oJadHiW11KV\nMY1azSayV8lMaPzpPxNKREzyT71nDCQthKGGPByHUS9XbsyN+FJ6ykr58QL3\nwwddMPHnqBoSrQ1HTMEwDduz1v0jPCE/w/bH9BoPq/s71jIdmYvZjK94+zlw\nLsZojEZBI4JPh77VH7aN+LiplajpxwpZ3mCY5P6jusCGKoP+qq/dkje+p2im\nOMRuzh9jUkg3Etp2hu0dhgX9Ios4eHo2YY26/F/fwumg2k+7VfF3Hx/zklex\n1bkY\r\n=XD4E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGc2SuJRkkH65wTwweD5YroDBPYO0O9B0LFvBCPvchlwIhAJUhv6L7jjZbahneEjhHFomHqUnkrPmIdOSVOu47HStt"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200103222253_1578090311974_0.9864037615796704"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200105145023":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200105145023","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4879bfb7296d3b784817974c71db979a99d986dd","_id":"@atomist/sdm@2.0.0-remove-index.20200105145023","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-7m0266+9HE7B+mKjl0EG5KfXDtCC2eN8W4UFebvkpeuizPVY27fCiCtPidhLiOls7qb7p29sSvlDkLplALRPbA==","shasum":"e5746bba6ee352e99cbca18705e49a65d4adc6cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200105145023.tgz","fileCount":1154,"unpackedSize":5692419,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeEfguCRA9TVsSAnZWagAArRYP/iklgVBiF651zXbgrOTL\nMIKnQkXf8TdWgSZmOamqJy/1H3JToub3IaPqzqhkhA43Px5NUu7Lz5TvPlzx\nHIXVlZbBmOHQATIadKYiD5pPIo/1TTVBbXe+FjeqxkmNK5bvjpIfv3hNyGtX\nk9eHYqEfWEdq/SMCEgItKvv+ybMa3nzAKq8T+IbmAfghNaZkg17HkNT+/Dqm\nkl6ZOWR+SOrUgcOFC1aMFDDnHCQOGC/LDNfRmK5QqliQ2BCKi5mYjuzoCzVm\nYYojzua6ADLHo0+NXUS/Va91cKlA+UGqNVMu5+SZ7+k0LvR7bkdWopRCsR/K\nzDTT+7KU31geB7S3aAcbg8u0RguIx6nyTggySwjcixbIone5hJg7Xn3Pp9ED\nzjBhl5Nj+2PyGOG/5qc+rJ+hmmhxGgN01NYuKgZbW/QZpes/xHeiuXaL7il6\nnsIhbm/kq1YgxyA5mn5p3x/Mzj7TnOBOK1shPLyyCMdyooCr8rRqmJe5Wx3T\nLQR8EaEJt7JsfT2fRzxeAbhgJHDH7j+lwwXpw6s6bkIU8D24cJlOtR7k3iTX\nZQZ2npfckJtD6e++Afi64zN4eb7SYDwTm7CS/4+TnDkou6PgGMrkqkVfYxFF\nTDdO1N3Akr3REVQ7JLT3ri8KsvPW3OwT9cE0ziollo8zXp0ZHa2TVMC6vpe8\nn6/n\r\n=TuSD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCVsXVWvTbCuqyGM8UeWrwpoaMbyNqpOYP3uP/jZ9cHpQIhAJ0e/3AK4WI3szCAb1cM4TxmHL327TkaQNli/0mMuyhK"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200105145023_1578235949997_0.7921505107916691"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200105193510":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200105193510","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4c2a581d42dfef2b6defa1df208b015a3c3afb28","_id":"@atomist/sdm@2.0.0-remove-index.20200105193510","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-itzb/4vNYb0pU41UXGvNXfpSFHIg++HI2x9eGW1/3Nz/LhNb1h7pXFWtw9PgD4JzmldeTq7EOc5qh6/YtArTXA==","shasum":"1fe5c34e91867150d1336d29c0a9115d97f8b3bd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200105193510.tgz","fileCount":1154,"unpackedSize":5692453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeEjruCRA9TVsSAnZWagAAkjkP/R6S3/+qduSoStIWiQ9+\naYD0ntkvBk9LKQSZu5eNhjoDg8XzIVkrWr2CuwEsbPs6kjnBgZsLnPuMEhWm\nBJ1mPHoPjjgXiIAEaAyp3fliToj1Ekg6hmZ3sXvGF4TSuWLRv3O56/CC+FCu\nU5cCoru1C0jBDoFiptm5HyJHopQcEkpt+sqWLbjHZNNx/gxt++Y3Q67TEzVK\n071MHfTpT2HA5tTyVuzGD2tND/IUA8G2wmpC2yyFK1i17duFUqX9lCbt3tk4\nSQlI9pF7SVTWVPTKlqDHq5fmty2YyNA8Ryy+iNMUti6jyNSGcjPocqmB3htl\nYhurkUspVvtYp8gAa4Q+uCIu4YSampAwqCshIkqnuOFXEwYlsXWHDWBPqMZM\n62op54j9Mpt3rdrNfTIHBtwbGFmu7jaS9P8w78cfm4xD4JKfH+rXZOz44LV3\nXebkIvQpzTBSczy/9mB53XiQIHJ26UMQbnpSGfCHOqufetSbc8llGLESSywU\nd4YSvuMx+OR8lqkEEcSeq/d/9AojVQ2Mv0cNJN9LfDzTnIs+tLNFt8EsVZ0r\nqFcGkeMoWIg6lM3XbwIgCgoGGvuQXRl/D8l5PNA86pQ+GB9b9hf3ZWt2Dbob\nCbDl38PmoIbzmKBgcqblPjWJXs5/Dc69GWX9LaD4D3yhOQY/D3qS3YwEyR+I\n+vJu\r\n=RJ4J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCwPXSdW8t6rzPp0dVGHQKCASNrpGlKCoZW+HpHmwnbngIgeb42/jhq632Fbqoe5yWJa8C0QeKSPeBqVcVaQ3I3oWU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200105193510_1578253037637_0.6120286148409269"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200107085713":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200107085713","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"57d66a517345aecab40bb71d76850cfcda69b977","_id":"@atomist/sdm@2.0.0-remove-index.20200107085713","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-RoAXPX6kKbA07sHQKKSZUyLLG9g1OPS1g8uB8haD4W2m5oKqUMbht6rEyuU2Y56F1OX8McmyOFX5zVU4VecruQ==","shasum":"7780d419a5ded83fce3c48a420f151463120dc63","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200107085713.tgz","fileCount":1154,"unpackedSize":5692520,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFEhkCRA9TVsSAnZWagAA2UQP+wb1ZQcOU/FtLE9xkk8x\n+Et9sAmTUsKo8CiLm9ZoYF8lpsdveIwQd+6cdfd8POBgSssiDuY3OCyipofD\nNaDO7FltBMH31ucOiHE+BDcWHdMiYzHwB1iLYg1czQ55wzme6YaRgHQ1aaYU\nQO7GQ/XrtdX2Ee0Yur3Fqse0g4QkSjNrEdaULcLZDKmEl++GMCWcQSz2Bstu\n9PkmSc6j4wjHQ/6z41M+TKRM0BO7wrWfbWf+y2v3GMQcTE3YcfmmC9pzyGL+\nuiyitEXBCSko9swPvUdKKeM4qZppus9/3YPpB61qvqNOnEkB6AKFNijBjDbJ\nDcnzamnQGIuTMYLjB8BuTMaaZQbj3WEntZZlBNJFA/X6kdNV2JsWHR+S7jKu\n+qTOq6NB1k5t9PF4sHwIsmhZrOIYU3wlqLKqBQw3UdijP8vtxy8LSzaBuvmc\noi0mrIfBgkWb1vevzm3lHjG+V3lU7/+qVFc/oT8TLAQU68oKVKs5EYnaEk4L\n19+urW6G8xPP0oQjtGYFXbnryeRXGQPLZqBY8pnvFh+fdP9GiqzDSUtWCa7X\nDQV/AW9W3fiBJN/SWbLZSP2y/32AyEJ8oTObqfTHlMu6l8rsIvYyaDW9Mdpx\ndCWgKoFMyMWh4k6MXGHwaguz7xqy5jZVjgc6lnhM8te2hehh0JUxnj3wgBs6\nhFOJ\r\n=ZMiM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD28vr2EV3v7gb/BYiEJRbnekiZtCuHR49zm27BiRijOgIhAJaabAD8+ywMnhjbo2QaBWvj5yLbRtixYj2Atg//xZ6c"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200107085713_1578387556374_0.85211325357504"},"_hasShrinkwrap":false},"2.0.0-master.20200107202413":{"name":"@atomist/sdm","version":"2.0.0-master.20200107202413","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^2.0.0-master.20200103175554","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4261e34ddf65f7df897db1efd7af5c3eedea6a69","_id":"@atomist/sdm@2.0.0-master.20200107202413","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-kgPoWOXW6S2vnNWBGnLBGa66Ps59b8LxXBwh1gAGpEzI1wHzwC2x+2C/OyhZtIfdj7juqeYKxyXRS25dqTibrA==","shasum":"b035fe116edf53c14b02a5f9c4c73910440fbf8f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200107202413.tgz","fileCount":1238,"unpackedSize":5794069,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeFOmUCRA9TVsSAnZWagAAgKMP/1QSMGedtCMFjE06xO71\nE90ykfjJfGJYY8Bjt08ZxeTSw0sPCol+kjm9UIIXFdtqrnw1aE0bTLjk6tX3\no8gr44x4En3qhjD5L7qhvAMdApt+cxKgMdKcP/KfZcO7Kil/+m5uPknSEYqt\nCr/IO9VF/Ti+nkyEuIyVNTmwJVUb8Ax8eZeUmTVF5LPfWHAWSqWF40U0SX/v\nlqlNXLILMpouVNwMUxTXBsm51mDAlvqwP4a6/o5nlyitA1WGpdHEVAdVZfAQ\nvmw/1KXw4Go2AruocGNEUdHNVy0pKXmo5DQvKY98t1y4IOjNnY+1tiEzyY6k\n2244Es2CjfVyR1eXTky2PEey0rzIqzvQcGmunEZttv9PFr0+zvTszbRQDq23\ndLsfP7OSklPBqMi3pCkRC+sqkwdm+VUnZ3pcguDVPlxBdSZjQWyBRcSQdWkg\nI2C3+iw2n/DJtCFjR0WHvCEzaOVTV1jXHkxJ1j1idmO0nkIK8/G/QhlmYR74\nugAcA7yZIiH/3VznV+yO6qIKyP7spLSPVwRIHh3oQCPje1jjV1zaaVT4WpAR\n0rpmp/KbOZh/1/ae77lyh+6LcTPmfyRTwJFW3Ft2N9f6SqTKYpOtxV0FlRfH\nrlFkpqZZUBdW60vJBAS9fdmgn381blLh3TaM/cdiX8b3uaABNBIAIPFiKBj+\n/Qql\r\n=cxf9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDvnX/cBa1P0SiD7j5vAUxXw/So18mhJkGe9Kfv0mIv3gIhAPMLzMxKSfI76wtn+SYzGjahGgmL0vCsxFrEsh1e2taY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200107202413_1578428820395_0.07571006999627161"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200112171637":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200112171637","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b61744578afec3328ae535b40711357dc82d618a","_id":"@atomist/sdm@2.0.0-remove-index.20200112171637","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ip2ROnFh3Fsi8kdHxWM/FKtbqS4bbouhI4ZvLpVqmriPRTi72Thtcp8jta6jHcOaOIWoozxMj0c6/JsH5FK3cg==","shasum":"18036b432564a32ba0e85ba7d6c539e9f5bda0e5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200112171637.tgz","fileCount":1154,"unpackedSize":5692361,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeG1T9CRA9TVsSAnZWagAAlooQAI7lAaIXnt2akLIf6naD\nSOOuPovpuGmhIw/htLndVzq3/0MLryROIuxgS21c2s0irHWAGLK3c3nDqFy4\n9XlOCiHcKoYPewFMvOq/XXFj7W7gmxLM9fLjmiGhhr11nmR7AqC86wRDLsdW\nKiAiHLX7POyiDFYiqCaG8kQjKWHbJ1d2BXu9BgWJzLybG4cTX9Y4tKmHCTOu\nMcLcTHg0fF5hcDyJQUEStXqeJFsJFsr+T8P+6ktlmIdG/d9uoOE4CNdly79c\n+ZuZdqFD1ikosnwfsXaZ3AklLGbXN8xIxYDCI43FNvSuyH6T+4+XH5UmPhfh\n7VFkYGHWw322tFVKgjlNQzsfGmoR/aX7mEb9g9s/qPrQLM7p41/1R3WDYVQC\n1On8eomMub8MOxDAOWu4sXAWD3p5uEYeOZhpLZ1aU3JTBxXEqA+zk8/k4PF+\n0Ma6iCnooPJ96ZxzYo6TjWyPFgcoObU5REFHjJ4QFwDXcGL/lQVJE24LU+E7\nl/rp8ySXi7rovRFOAlQjJPQhQlx5RJ0eiPrUX18+LUzJJejfY9NtUaxlstp6\nU9Gz4CIwOqdAPq+aAgEYzGcDm8QAOifJhkG/8/+6KgLISXbsuWZUFyD+US/2\n/CegGhP/o2gs6ODH1QkdQTSIdKvS/0zfAFUhsPnz61LQnweu/wSwYxJoMEiz\nY5+s\r\n=uM7r\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZZFlEpfvyyH9fneEkOncddCfDp58Vt2Cn/l00LgRitwIhAJyIre2rqtqIRvOOFRU6zWCWHSih6jwXeQllMQ5YKa2u"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200112171637_1578849533075_0.210505635356103"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200113115436":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200113115436","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"022f2f62c43dfacb38193d7fd3fb59b85b409e18","_id":"@atomist/sdm@2.0.0-remove-index.20200113115436","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tw/He23nZLU+0zfb9eNC89ABVnSHv9ubZ+E1RC7jdK3lGRvGpB57p/nKpDb7fwHlhf5VHbVTha4pXmKC/Msz4A==","shasum":"6af5185209692fcadfc5c065309a6fb415839463","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200113115436.tgz","fileCount":1154,"unpackedSize":5691987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHFsFCRA9TVsSAnZWagAAtGcP/AgbupZpzJ6QB0Zbi7eN\n9Eqmd1LdMaRq3CSxqzsZKajyIUDhZ1JLvbLjhJkivymBJNT81iths6tG6vB1\nE01iYgey+ikfSXbawHxn6+TVQqbqI4uu2WxUOcC+dSKIB9UaN7P6VNeTnRZA\n34b3KLOv46wtUM5jWHRuB4dhJHJfDfRP+7vxKOuejt18xNUw3zp7gh72x2Fa\nQ03Uh45a1DMTrdZh2uPhF7DfewqPyFRckvRPo4HSpginC6hvbG/yIcqv0FDR\n5q4Ej298LOhzEZw9AwkRERck0Ly2zMNOF/+f2g4FBTDrepOv60X0UQp8ITb+\ng9Vu3Dl0+lmnNku+Mek6i2X3TZ/pL6t5SAMbqKf6JGPG373Y9RcSxiBG6Vvp\nUuSp7M3nSqHbS9yZC57E9AZfUR0E5dK1ZH0EfpJHtVIuvPr0w16v4MMtqDUg\njRuyOgGdnfJOYpzU8bw1iX3NLBa6LWYiWDiRYb9z/7ROytBZLxrA5tRCLauz\nDoRUzvslgBC4P1tmUwLUqvMNE6nm7aJvDlLkcwixEn+TUD8dGIakYqHwJ17h\nkE/pseFfnqvLCksIXmiyOE7aO8bseGQrZor3yG5XfD+9ieSv3jSnXRkcu+ea\nEWnY3prGxxqMETQglRLKWWP2vE99UEBhQm4jpVovGFic+JmDtXW1HGcDlKYq\naq3F\r\n=ZCpS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB0ZZNOo1nUiyN624j1aeDzsagZshia9K+66IZdGWjBcAiALtrkEmkm8Wv5ZJzw0GhvI1s3MHYPpZRISJMSJPUVH1g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200113115436_1578916612945_0.41000717404430054"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200114085202":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200114085202","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3a44c9152f967bd1dfcb159c890b5c30cdd4eaa2","_id":"@atomist/sdm@2.0.0-remove-index.20200114085202","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-n7nkUE51Kc1hnGJukATR/QhkGF0gAs0VV8XtjCcGSMrov7/MOXAlNA7AWIsqvU0uq0HHC2xxb+wLO+++hBoxag==","shasum":"7932157b23616d6ac975c7e30b2b97af32871226","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200114085202.tgz","fileCount":1154,"unpackedSize":5692569,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHYG3CRA9TVsSAnZWagAAe80P+gKB7SUxheatr7fTfN/o\nILvbdsSLxwjaVPjhSwpriQZhTd8KoRwQ9HQdbVREgHx/RtBKafkoXSAWJ2lr\nu/glchfWakow5em4HJgA7CamYSgU8lGenl7AYh2yzWxPb0oIshyhxuRdffxq\nVhLLaDD7FvMWcsXymtVcHTetrhukaQ560TLHE4NWHMtAvTeXmicIckvjbTm0\nOuCJ5yfG+tq9HjEWHs9rm9Zi6qyJ61koZvDcRkbBT/BcjtwU0Rnk+DOu5ILQ\nnasFrEy4BGuAV6MHtqUVb4IDURuZY8FKbhDjcjtwiiqs4WjPpLGlyWYi/jdk\nBNs7u5WXtQ9Oz0JiasBD1b/itbYNYQJ735bFBfQrwBNKPhxTihBw2JLafG2f\nhPaIQEt8lvIiPiK5lFYBN4g1J2IBrF4mGRkSOh/YSMBaolA/eqvgTr6PIUmL\ngQ6Wetw7mtxNQvfk++sMTYWOSPx5MwsJMMJ6E0FzVPDKG9BeJpZrxCsi+sBR\nkKhyAPyBe3blasIpDvk6X/+STdnFDNHDq38QfHMjR0/jSdLPnix0ERFqOClb\nkpCJFFiFxDGFE6np7EokMC9czNFoeTxLWE1SptZc5fcUpD9iSS43qhs0VMgZ\n60mwIlC5YoR5mwDxh96p80ZER0RBS7kEAXJAVvfAdB3vgPojVGaf4R4T/LuB\nagRi\r\n=y8tL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvjsuOcQ9KktYe08WF+FF54ocu47UneDEHW+mSMR3qmwIgZVNUq1mmy7XQoNjdc2Th5o2SQKHbbAyFxgvPuM2YUl8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200114085202_1578992055268_0.13166689363478912"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200115132157":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200115132157","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"972cb2f5a8a89b4f91e439fab9e9e1e08bdbddb9","_id":"@atomist/sdm@2.0.0-remove-index.20200115132157","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-AUU7frfQs0TeI4nf0P8tK9ZXaF9fmEzO5/LZ9AWP8fxE5zH1snsUjMt6pmlZXptWpFmmML0yA5Q2kzf3E2ivYw==","shasum":"81f9603e1e128d4a64b5f05dcd38c4d14120ab07","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200115132157.tgz","fileCount":1159,"unpackedSize":5696238,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHxKFCRA9TVsSAnZWagAA4LEP/1PCV+2f4/E+n2nOkFKo\nj41PB8yixGXgJNNPCGb0uPUH0h1TTneft4Pm67aDHGKtdzV4q15fkrcOmJWX\nPHue4R7vsBVABbHcyKC3Eom9t7qMMbN8LWcshV5Tu52OZUp6ODqPzD5sFmOo\nxJne/UgKASfhMp6cxCFsHi9JVDR23c34iGFV0g7Xjq5VCpCYlgMhTcbl6aCn\ne3p0MwRcdjtFNLy5bdmdbGwMrZv8JODjVz5mralWXMVdhZQhkyfWSFjgBxb5\nSOn48k3fNpS26nWrIas3IEjMDipCs1KDRVux4xqpKAXk1fVdaBlJXEZncy55\nifcIrK+zN/7ASJHa0nT03b80MZAknUJbTaGsjYT2adVjp8t+EfrTKKoNaZpX\nkbPm0JNrrGw39EoTToVQI/UxLv1taVVkJYflTreQXEMVj9fJsOSdrA+2sE9d\nYGWC99MOUV2cqGm/y/UBh3NY7y4poNjHb4WsU+L/AOp5+pz4ji4uM57cpMnm\n6pbhLS5ttOjJirww5Ch4DYrX/6+pGJ+EXCOHeN3pq1F1kl/aFrCwKpaIjR24\ns1XC/0vxDyHHZBD6/elRs5lj5YSzQV6bbpzovHh/ttUwrW8OuvjM3SNbinxD\nUWHdsHy4kA2KalkJmW366aorbSXcyVO+UmtUT/j1+PNjkCQ6AKTr+lWsOil5\nHyLf\r\n=/Cye\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCr5mgNTOl8KJC5Hxh74tjUpsJNkuGCo79F2BX3IbV0NgIgNeHhpr33J4/5WWpXbC0nsscC9L0sK9iMeRQNvyxV5D0="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200115132157_1579094661088_0.5234922466890088"},"_hasShrinkwrap":false},"2.0.0-master.20200115150346":{"name":"@atomist/sdm","version":"2.0.0-master.20200115150346","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^2.0.0-master.20200103175554","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"4261e34ddf65f7df897db1efd7af5c3eedea6a69","_id":"@atomist/sdm@2.0.0-master.20200115150346","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oS1cDzJHrNHngBUmFzz1dAe0ph3iCbKlvauXxw3Dg4ORk9qznyiPoE5rS2lG4tUFoHd5GS5u38IkZIaHfhFhZw==","shasum":"d656d2713c04e77db6337f7b4ef1868ffead963f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200115150346.tgz","fileCount":1238,"unpackedSize":5794069,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHypmCRA9TVsSAnZWagAA1CUP/1TmLosWKmBxnNjBll6k\nE6NW+9YVHmM1z31buF5xCnRH73Ti2ue/JOkt++pc9RoTk5NoCWTrjmiScmOM\n84N7jj1lbqllhUMr6AE7+V6hH9KzKF9qAavGysnEaLLqcNddnqdVnFPfj44A\nRYgUvrf/U6zrZLhor4k/VZOhHCYI3z5sGC6N6O9pl9iXAMZyKDeP/rYNCW1Z\ndYXz5Ed7exRPfIsuK4vwtg1AGwh/8Ve55tWBWZzYeaAIULCw6rh9nW80a/j0\n+b0lVW/npiDAq6Ptawq+S23JBw7hx9NKmsjoLoBd6jfDEjiv8AVUXGP9N+kF\ni2lSAgrlrLpO9YLYFNg/pIZUfTG3Ca0+C3JgL0ffn+F1VEPhP7kZM0xuPNNp\nvfIdopPH0jd1thjb2z5o5gG8mTgdDNVZoo3HrsNIe1F9YUISroRFL5gjeBiF\nXiOGhzX+4Vh+jAGqVdPWINuKyEN6dTli66fsC3vtR0IGVFHx5kJIaJF3/ONX\np+7zRhd7meyznkQFKxjTF/yYeYZFqI5qACuKZbH/ZVla8KcWsCZcQlUQpETm\navZZFPyRVBMpCPpl4irPS3ZFa80emrn1dIUDPnML9ICzZPwO2b3bKes3VrP5\npbaMUrL4mfsXtW8vh2fnIEA/cVYA+5FdN7qjorXrZoYglpnBKnmMsMb7/Yku\nXSMW\r\n=S0nF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCod0lL1f2iI+r6JN2qHtHlFFBq1jw+WXhptPRGUvrnfgIgewkkFqMtac+TK89/OCz7R0/8WnjGMqjUMbAPVIc0WUg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200115150346_1579100773769_0.618944435106032"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200115150505":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200115150505","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"050d53bee323bb35346f52547582e3c775ebc7c0","_id":"@atomist/sdm@2.0.0-remove-index.20200115150505","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-RrOeX+vS/qIhKk3ZbKve3AfcUHHX908TpU6ZNHpUz4Bfd5knPUYZdbsoC2ko8fR5VsBXiiDAUuPEaNMTuEFGdA==","shasum":"c121bc22c361326b614ae4a6418a3552e69b1e42","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200115150505.tgz","fileCount":1159,"unpackedSize":5696242,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeHyrJCRA9TVsSAnZWagAA4JEP/3xrGXLowYf1OVPo8Hvv\nfIFJkISL748zo3vzHEKEmJ6RnVqj7mH+HRQWC8xDfc+ZrWM3QCaoRItT/jrR\nlT5zajJNOsIKBE9oYIjnwvuiejHTHVHNnT3ax9VVrslCA3S0C3wgOjUFOyhj\nXcQayIFir/qncI79m4GMONuZ8zClTxS5IP41Hzh0v68nHZ0eMqgNK7lwtIB0\nhaIEMVwrS/7ODYXwyHixk/zHblM5YBAdvh3GQPmTBL6ow93swZFieutv32CG\nvGrmyD1b8AZEnVLWzXr2W0e3yGZ3Jv9rW3wBeYdt2c833XNlLrZJMQ9kWXLW\nxRLYohNXzjlc6RyMlJdKUEnesKcYPvrcw0QbtiOz8KPsEfWbYgykdFCuqPSD\nIA86bJpQLj75qAGCV6I+/UK+wLQlhc5W28dXcY8IF5jFRbsdQFI02rwuLdqf\nhfVli+9U/7Vy7M+UABNFPxAWLRIBPpSjR19HQWz8Rr57GHA9jDtNK5bMADv5\nfjJ5px3DPupAor9b2IbKQUO42MmN8ky0dF4gHi20RT8iQoLf/t428fLU0Iwg\ndAVIM0BCVRaRr33eX6NI+A827ZzgO6LNKRrQ9VshJ0YuodFMXIs+MeRq/h5X\nY67T9ZwZ2d21NYQOovKcbscfc+CxON+/06CdmSwpf+u0/+dHydipvxtyLEv9\ntLQm\r\n=jyf1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIARWMbIAxsTrLRHAc6mXhidREQ1MhacZjfk91c8OMgMoAiEAvfV/tY3dbUe4+DBh3lxFSzIIU8O9dwwKhGg/1JDbwdU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200115150505_1579100873013_0.82362975423018"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200120213952":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200120213952","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c25184faf3e2e1523d477f5ba8f48f7f6d8b3065","_id":"@atomist/sdm@2.0.0-remove-index.20200120213952","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-hVJRoMVE0miUk3i0KPp0kSsfDnSsDlDjRZqpNgUcIZCPKvjTQH8KqaWOVDV8JErfjVMPv7g1+7q7Z+svOgpi0Q==","shasum":"8addaf1cb9c6393911239fc594efddb9b680eed1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200120213952.tgz","fileCount":1159,"unpackedSize":5696704,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeJh6sCRA9TVsSAnZWagAAeO0QAKDxzjYSmeShUn+POiFk\n+AIukJtrKsDzAh8RfWFX7bHXL26e8zArPpDm83OMM9K7PFEG5OMnPsVlCBAO\n2dni0Iyst5ZDLIlFN1dHUj+gZ1zDGVvtVOMqH4wPPBDmMRy9wQkZTb5O174/\njalOvYB7pHHQi5iFGodCor5IYqeKZW60j39uETG/98NBXrpwFPiK/JOKpDD6\nYmsAmtrqQqnFNVXYD18jjqqZw+VsXvFvHg+enyRMYXmOg4DxPJcaBE/VxkOX\nbz+cZ0okje4kbW3vCPIJhWdzwyX3LFO6C4VUtY8qEO/tLzbPnUoxmioOBSDQ\nNXZhxnDLjEKzae5n02jM7lbgI4259yLqV0f88JrPmI9mV30c/uFeSiId0eJJ\nMeYW+9Zp+/RmpPqFK7uAWiSB6byuYKBiKefE7hHFecG3vIvh8vallFDadxxl\nUTemYc1R9SqPL+BlaompUixQwVbZMPrAYT70GW5N1Dl/YHRdQVQY58J+x9yk\nhKreVjALdL5yga+VzXB80YVVhA7/P3BArk5Ws5zguYsAYAa1XqDcbScVcvar\nsFyqnnOlTbfa6LKzGvGp++Gt4XGO1fvlpPBCDL+r1RizCMuUi6PoxYYhqOZB\n5cyapbaWLrO5EYmLPqdq3YRXMiZQZbUOcw017+9D6AW5bCthydnjN6+KHIma\n/70K\r\n=Q27w\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCV8uTLxWinZZpZwqGFQlums/6/DZuRo4xR1RR1mFmj3QIhAIsflWzhaRPjqEq3L1EARmfMmvqOGCdIhoRlVbyBkf5+"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200120213952_1579556523875_0.10663608646978484"},"_hasShrinkwrap":false},"2.0.0-remove-index.20200120225927":{"name":"@atomist/sdm","version":"2.0.0-remove-index.20200120225927","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-remove-index.20191230145915","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.3","typescript":"^3.7.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2e6381458aeec56398b40f4f837771b253fe0bfc","_id":"@atomist/sdm@2.0.0-remove-index.20200120225927","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-DG4b4MRvyVdSw6pcKyTCoHE5reUAPOipQoNsUN7fMuMlm5OAoTgQ9RQ3E1iuTKkLJIdShzEYft7lT59FE3EVxg==","shasum":"fbd5c37223493a1e439aa19a48155644930dc7bb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-index.20200120225927.tgz","fileCount":1159,"unpackedSize":5696435,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeJjFUCRA9TVsSAnZWagAAltsQAIfTi5w0Qn27Out27M48\nrjJdQxGH0XCkrMCyTuNUN8oSOm+TZGJbPJndP8F0zC/DDHiz7nwYvtI00Vxj\nOH24P7lUAucm5Az6pqpooFhpUW4h8s5DGjCSCZ9/U+TMDRsudZLwVe6hgwju\nm5DN0DSrWVQMIsF0X+jZOsHzydf2uNRlIKSnMYWPzNEOEsTd7IS7U/TzcULt\nzrvgI1VufqlwkSIkKnFM1qHYgS7lHGJBJFae0foWJW2NwtByO6TPDqxsPrx3\ncFX1js9qyIaqQfcJ48YELACATgT4uhKKo4NM6NDGwyagczm2OQybcOXKAR3w\nLhJFTeLDaQG4rQcRRjESFH073tiOoHuctRZNWPKvc1f/g0EdEiskiSS/T4jC\naGBxlPIKOgLGhjf7lES6rlytdw+Sih7H3jcaeh2o/oDG6s4MXOZaDs2vJdlb\nMyVkQcqWccv93q+AlxVi47YcpLgL/KNSyIa47hMkYwgLBS0fp0w4MCKFGkXE\n8txh7uCyfZcaUBC2cfsQOdIs7DIahP49T5DFZHM6a/PfuxDVg5gzlr2A9vSn\nBtEq7gIK7iqex/bSfpY6kP0wqzLc6tPcrMzL3UHWz6U5m8d6IacSzIHnjC9T\nS/ENwZO6/2zfTYSLq5TSBu+4F0pUNFz3BxnugRmQPyfwM5PVQVGM+onDRns0\nq79W\r\n=x33H\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHtglbxcdbuUN+V6VJitVDSIjps94BlbqT0XPvfpG6O1AiEAuaDvHKYkR+czviJ2YIKIuLpLV8fg4ANzq/4fIUpsiRs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-index.20200120225927_1579561299843_0.7826544641058271"},"_hasShrinkwrap":false},"2.0.0-master.20200131202557":{"name":"@atomist/sdm","version":"2.0.0-master.20200131202557","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131200849","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"af6a19afa4e1e9cea7e80119a79106894ad9c6f1","_id":"@atomist/sdm@2.0.0-master.20200131202557","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-P6evMQKIF3iyzy38IcKwA3H6Szz7l2dZELnpqnZH4Cjh+8T87zUJgox2uphYiMGDT3usW17y0yksWePTXo9Fgg==","shasum":"e231a60f5aeb9c4dc3da89e04a941c8d55f13e43","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200131202557.tgz","fileCount":1159,"unpackedSize":5696268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNI3XCRA9TVsSAnZWagAA/p0P/RHdkW1QuKxXwvvclwUN\nsMY/G1hSGPgrZYEHKeiTCswB4/oZqYJDbkJk6YwGOKywNkBuSFfMcGmumXgQ\nCV6pd7EnGgoMj+c82OiiuLGmy20zhIYh63eQxWKP+xHsiutT+ZDD1V+IFVJs\n5co0Lkxnhtc1sWr77r86cVXGnJkRVl6mQnKWHNsQw2xlgf0BSVnAf6hwzbli\nUrslU3LzB3FN4n1KRgleNHQrCSA/xdlJZWX4KXdCSdO6AeX+/tblEOFCw0xk\nX8AOIc4VbwyKf1o/hKLDC0PZcy74HxOC2meiJzjMQXv5nrGgXe8+G/Re0jT7\n5QxQlPibFehOZZ3AoJHMyKZrHxX7oc/pyU97a0BrYfbXXekbYkW1AOg4ZdcZ\n1C1vPZOBUXlE5+gnxtnQDj5uFYB9VOqQd2LJrthvbOEortdJXRM9g+Px82ab\nBV5Qx2B/98PbHxB8BqrldM5y+3YZ1hlxHqXXH2TxB/tFBywbgKfCR1AEKhqM\niOtJBGZ5kTGNrixeKoavBzLtRzRZnDUfrKmeg+3P8roVDQriqNLZbX/+gRdx\nVW8d0HyjZouyj3Yqui+ngEa9HMLQf/8n0VjBX7EvGpg48KQJEZIkFi4PfiUN\n7LJGFQzWHwXQ6ZUwmGXvhPGUWd1JoOr98pbddwDmG5PehwZOnudVchRROu0M\nXpiI\r\n=c4wp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDejwCHBo9bk5m0FGFeZM4GNhhw3Rz/a2wjR/bk9pFscAIgYPJ7DNfLSNQL9CExdtcDDtkXXLcDSBLJ4SJoz/LggQY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200131202557_1580502486724_0.7693020658713046"},"_hasShrinkwrap":false},"2.0.0-master.20200131203114":{"name":"@atomist/sdm","version":"2.0.0-master.20200131203114","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b924455623c9c8e50f5d77af1caaccda193d47d8","_id":"@atomist/sdm@2.0.0-master.20200131203114","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-sfSN/FamGqBksgGnrIwPPbaUncrRZynXdjlbrtunYHDILgSv3fjefpqde1mKK2WQyFwCejbtdFt45jIjxSkE0g==","shasum":"5b0421d6cc7d8dbacd4a6883a8c4f85507467eed","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200131203114.tgz","fileCount":1159,"unpackedSize":5696268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNI8RCRA9TVsSAnZWagAAeZsP/0W6tV3ESA11DKOc7WXp\n41SksxjP2qrZipygabZheUEh7g2YkBVZ9GtmSJDZVH2Xjiu+06tA4V5NpZ3O\ntrYjGJHyJBxOACwHeeJOOo8OUrjDMnCgIcUEK3sjPkA3b+dfj5U/vmTaBWng\najuZm4dJiaFpupC/BWJ92p1PEPgGF03UuvaAztZ0rsBXLjSTPtpVJWPKcPcd\nEreZkc4zydfQDjeG6TrriraIYqVLlOoa5Hnb8D/RIQIciAGrVM8dROaCSB+A\npNF2KgFRM50apLhZl5pD/70SY8HL4HgjBcd52PX2WI7tjzCpdz1NKyrRUADr\nPJ0+Km3RtoRv6oUcDRolxdSJ2IuFl1O+Ug44NsvGBdBCOlTyAHsbAHbAc2DF\nsA/DRngiYk7Qn22hucc1WXVnNz/XeO99VJUiT7nRKvK6oTUHB1Sz9dUuLm6/\n4+lH2e31wj14fX15s6nu+OvwB9wMYzLfBKKRC7t3gXQCnChEiYo7s4PoXJpQ\n+8WI4Sg2gUWeyf156Vu05srGi5bvy2jTHK43dS7MXqRojuMDeF14pJx5uZmJ\nO6KSl3drTk4OgAyxhJ+qi4T8M0IC+3Yr6YqIs0gaRVwbz3EaLFME5DB0Ix5c\nxnKdfzMsU5k6RYCjMnxU9NgMJKYZw09cB/CDveDM5mC1Y7CINiw2/iNnJ3Vl\nX5Q4\r\n=EAs+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCArc5mER9IhezhGa+MCGpNFRAmvTg7/06wEAecZhr/pgIhAMj6EC2SzfO3j8pH92QWWOzzTzz/HpxokUm8kVYm683L"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200131203114_1580502800861_0.6985247656567635"},"_hasShrinkwrap":false},"2.0.0-master.20200201115609":{"name":"@atomist/sdm","version":"2.0.0-master.20200201115609","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2545913036f00a5a10003be7d89949003aa7ed89","_id":"@atomist/sdm@2.0.0-master.20200201115609","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-YOMUWMy6JTS8oi7uDG1WfeEMp1oYZ4lWH6FQpnykUvdGzVONokWuqskL+dz+p91FUqIhF7hTUJWR55l/KTEHFA==","shasum":"4c4a00f0b238d72a0798859bebfd68838b3e88d2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200201115609.tgz","fileCount":1159,"unpackedSize":5696880,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNWflCRA9TVsSAnZWagAAmEAP/38Fy1fIrfQjCHKDZc2v\n+XWNA8pqg87NxbF9ZbUEJThlMCGu0gCxscChqsWg2TIKviA/NlYd77mTSlLX\nAzLWgCJ2cQ6F2t4hChHVemGG7iJeEADxEJWa+hr+/EYBMSC1oPgJvUnqn9kQ\nChN+4o6894cG5v25PYLmqR7nPDquE4dbZ/hA4eXbiM7nnZ5qzSNtwilpCamL\n9ly3O26yUewOhRZ2NlS66b8Ry7b+WLItiyRSn718uyGbRVVfpf3gCk7OgECY\nGxv9lJ8mUlEFZ9azHpE5LqVMlX1xDBQdFsThsXCFXo1nIZ8XmBiWbs6Mzdg2\n3nIBfnO9OxvJRwnBIn2rd+dBSkmndrGuxbzriyqcq/ISHBibeLpkPlTxWlGo\nfCnlCir4ic1dPSRj43zcFOzH5OaiyM0vjimrm84waQNjkYr/AXcUF0Ti7TRw\ngUeHp8fh+iJFHZfEyB09/C204XsCkwErfuR2YYwDKfnYHQZFy7jQKCIV1JTP\nfX5H8QGZD86m4BTdo0DWrjjnP52vMftISHKCqKD74+oL6dzwWdbNjl0o+bg4\nScSjFvBDh+pYlObNE5I2/C/D+/d9Py2uzEbbTnscuxmOXBAycw1S51bqOHXv\nOpr1BCXd+QsnxCWfPGyZMqTXWMP6Wvt/PmxPX7ZvkyAhU3XNRBgSRL0ekeCF\nK3d+\r\n=PuRp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCsnT1nOA0mVv16A7YUlitcMMZfX3V6dODO2VRbG4cSSgIhAI8vANR2Lr78wKJ5mJTILjMwMSqZ3cjo6YXOzRB5vBou"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200201115609_1580558309152_0.9635599053145656"},"_hasShrinkwrap":false},"2.0.0-master.20200201162131":{"name":"@atomist/sdm","version":"2.0.0-master.20200201162131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a638e65a9a3be34e0928feffdf1e92836f6cd62c","_id":"@atomist/sdm@2.0.0-master.20200201162131","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-PA2J6Fdb1un8mBES41mwRuIE4zm+is8QXSCFU7kR0G0amw4W1ki4yNnmuRPxod4gBp4pX2b1E/qHqazHmX0W/g==","shasum":"9d10e426f3178771a30f730b07f3df73e4b4ce8d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200201162131.tgz","fileCount":1159,"unpackedSize":5698459,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeNaYFCRA9TVsSAnZWagAAXMsQAJIFsJ/5p4UTAsnGXH8P\nCgOK73B3BXL0Eja7GM+Plow2TPCpYUNlpT/Bch4Hm7KbjP547BoG/m8XVyXv\nXkqolNoSCI18gGef/AsIrSWgLjdJBYOBRh+oi19TMUTiCeT7WLOvfHEol3F4\nkRqtP+X8djAkl2PGQpQnFZRN4jmgrSOZBI/HXo3PYCnGyavRPq4Cpa8Dh1Fc\nXQ+xMhfWo4OF0DcOwb9xP6TJokMQjiWrIYrTS+YmnzyuIX2votaFzp1H97MS\nF0GSQTrUa5pbQPnBflWBUr60dMVq0HNXaCJwitr3K7bFXaFoN0e+sPObZZiA\nhH6F46HkWK1JlMr+nzCqCCDzpM5ZnMVwsqct05fo3YBGSO8OYGSScfx7ONVa\nenAA5E9/x4TmrFcEPh3CyGS/tj0i0U0xEK0SbL9LSFeMb3BMqy+kcJCSBGC4\niActcACgKNp5sYInjkuPuwoNYdypm2orpU8Lq2FFxuZwKjlJICIqKRBD7od0\ny4FCiiKcxUd/zEU7lnUaXeDlB7+Hj9ai13jL+oPME53Yf4bm1tpWoXTN8inb\nqPbHU/DIYNPOj+rBjv3vFd/9jldO57ZxoZ3r31hDQBBWIP0zei0/+at6oaHt\nW+sDSI3obYzKzXVXKs4Z/NxiE7t9z9x0yiySfpd8HJIbGpuUZtUtLJjiTzIP\nyDnZ\r\n=bNFU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG4pTlgCjCxP3XS50mUaz58IMZ4wE+a2CXKNx3BNd8dqAiBJuh8Zm8tur8CSdeqo/fiOqkoQY4ogo2emv5wL+7tjXA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200201162131_1580574212961_0.09845274485087052"},"_hasShrinkwrap":false},"2.0.0-master.20200204153359":{"name":"@atomist/sdm","version":"2.0.0-master.20200204153359","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3a0087db2a7e1849b0ece33651b5717b1a0616b7","_id":"@atomist/sdm@2.0.0-master.20200204153359","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-aFvGsITc/C6TXPED35b2hIxrp6WGU/g8mp08y+ZcMR6nw3hwCfKaF3zbtRHQsrE8uWpdr+oKqaUL0XZ7a5lKpw==","shasum":"29e946ac5393c9663bbf0d8b89307d8855004177","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200204153359.tgz","fileCount":1159,"unpackedSize":5697562,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeOY95CRA9TVsSAnZWagAAwyoQAKRkjB+ArK4v+0PcmyIt\nXtjm72Rxzc0ndl4t6b/fWsfZ9D/nZJxU07eFfll4KYx8PhW7eK5n5WCcjkb7\nkJ9hjLTu1ZjTsSBMI2iDTutymJBAuchWhs7qGUgjOFX0Z/mVJL0AncKUTyfb\nDzTMRSA141fO+iK2lP3Aypl80AKsfSh1teGjrvD/lrO+V+tza9+8olTFalXK\n5jMBuH6qteIMufsotvDSf4LRjK6YhSKdb9AD50K2CDv3f4BF7Q41T4gHy59o\nWc/vICF0Jn0aMv/HJ21RI4nrmyE0/60THuj4Y9lmyEOmSwMLMKyVmgU+jgDB\n++VIbhCUQBySW9uH5wGg/2TTO0dvy+6vGbkzV6mrqlSEfZR/Jsp7AL6r9PgZ\n/uW8FXEtCprYCrwNuvbQOzM5rcDQJtruDKL75i91Q4PcNoBK475Y5ilAr2oV\neCDVKWya6Xm4j6bZJXF/d2jdDbqAwr1He6geZ0O/8qYmTNEYmBoW+gogepCS\nZAk2wm9MZ/9rbcQIfNn5GhpVzia9KvhaxZKHom2r3qnt7u6Hw9xGsNVqxHBl\n8wHV6HfbPcpyP7VEt8+1ENf+eIAgzqIOVfAV6+sCstjM0AYa7/GG29Ebh7Kr\nBCaWgIII5fxf64lcRKPD1KQ3o2FwVFxWgujcW31gCsJc1xRF0tgk6bHlcJC5\nYw8a\r\n=t7Vt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH78dIGOFqYKazkZ3JRPijD1B0ScYkstECkrX67HAFxMAiBcfwHfAv+38cNdHLsdfqdZD5fGXiUT8Ewnxe/6ehaXFQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200204153359_1580830585127_0.3511624724819513"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200206145221":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200206145221","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2ac3f7a242c640628a9b6cf4bac9e717f4168570","_id":"@atomist/sdm@2.0.0-skill-output.20200206145221","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-WQ2xejKdQeKAsCiycIJuAuwFSrvmn7VBSVyJK4H0aDzTAGoEUjnDzrol7zjG6YQE0epAvmQazXgLG6eITka6VQ==","shasum":"bf8121772b6ab675679a8ff2049f0995b73210f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200206145221.tgz","fileCount":1165,"unpackedSize":5820079,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJePCi5CRA9TVsSAnZWagAA2JsP/iI7j90DfU87BHeSIgP/\npRskU+pMUC4euPTBa9Cikzr928WObuF+VZvWgWj5wVnpUlF85nwZywGUT+Kq\nQb/F1oRF0A0JuSCpFs4GEHpxyc1NYILbTCiD6hfscxbEvAAfwdjNlBeOJBHM\nWEv3OEcjZjrQS4z6C/FwpfqEn/GyOcJLSoiQQ82u1Ym65w4iT07II6tKDspx\niUlu/1n67SE+2G0ffrPyg9nZA3c3c2WCfD/LLsn0QWQdD4sxnfNi0z6rfSIV\nixmJxcnKcYpKsgO60Zn1yl8Eow4t8NZlAyCXrx4QBUmXQFvBa25kYvntAXql\n8h6mp9M/7ErUVKVAZ9O/uOeKCOPhQbAwe2rXLOzQrQy9YYwBeB+SJ1lMCxvg\nBUUWVKPfnvTWM5VQO55RdQ7Wey/jpg3p4DEBGmy63f1mhoExSeZguVslCOBn\n9sNvYEQNkMT01I8Ws4X3Nw5rIPzHb+FMicrE2jW8NVL9Us0jdJRoC9T+sV4j\nzd9dSBNj2odXYD3KA4UrbnftAQgu9HnapXNp/pnk0j18NvckxX8d7AFRAvrI\nfVI3GTnUPx+40jVIVDSORu1kj0TwQLHxNfMnif2oAWwPHerbtLGdZDVRmBfb\nBSSiTTAA8iL7b16/hLMMHWqOSbXZxashjKcdReMU7BJ6lzdMiZBbWknsSvpn\naCKD\r\n=pdxe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCmWu5vuX02eFqKV2djV/mHaYOc05QqQD9NT8/956UU2gIhAOGjqNHpWTXG0tN2B0vFRaA8IqbIYRaKlL7bVOGCIDMV"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200206145221_1581000888701_0.21623815761833076"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200206153703":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200206153703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e057ed945f6e0fc19f74fbab8cbbbd6cd13073bd","_id":"@atomist/sdm@2.0.0-skill-output.20200206153703","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oatVm7ENW1N9GM7XYE+18aeSngcr5Mq1lPbzKbz8Zc8dEXOSjGrt+KKlbC/SvKYMtA+3MjnBLhbgR6NAE7nJoA==","shasum":"32f9dae384740b3dbb865c03f62c1cedc964fcc6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200206153703.tgz","fileCount":1165,"unpackedSize":5820253,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJePDMlCRA9TVsSAnZWagAACQAQAJu9aHaTeK7nfR9I+ZeT\njkfWhYEJc0i8IRSH1LqL4LRYVAIwe6uwX+bAUijFy5PPuKrZB27AieTXP6Id\nmrnAHUVGRgfYERQD3gw/7nbhrz45g0+ImllVpMOytgQpYgrbFdcVAkDB9WGz\n8OsaX9F/yxUwi3kFEjV+cjt9Eif5v1LetJLKopeY4Y1aYPMM2Sf7oQyghEF5\n0KKqQuV0dLm4lyFw5ab/ambOROsIf0PMTcMy6BYpbcWRloGiW/Yukwu405+X\nkFYNX84M7CNYeX6WJeoPgoJahi9BhKrejeb5E7h1vSSFON2Quq7xm+/n17Ce\ngQHZiVXsIanfnxBBcsXXzYx1irCsocNEo4RejxjRu4SaajtbKc9+iFoJQuah\ng1NQjqlykH3wAaGqn1E05IEmjwIKwkjW8XNSZh1inbbrRidJEhtMxzjb7aNr\neZnf2BHw3qM4eysW6JL4lKJVkLEA2yrThpYBnfR2Q3U9is/Q7I9ZOK6RLMO5\n5E+y0AqFI/0pjRWF4taL5e4rd4p7TeMcZw3RJgXXistbpyzc0jpCtgS5BQZ2\ndi1k0r6lbgLqS8ToW7jXxPrrB56/3wv91s1cOSr3FZ2iHsIuy1qXyts8GtLc\n3JpDJKWXibfSOT0S6tV6ERLtwDfDlohYww3IGGXwAJnoRnexWemQNS9BEevX\ny2Ob\r\n=Jhwu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIF7CgTtivWD89ZUcfGM0+KMmAbiydzSBrvvPu5CRWUd2AiEAvY3AgMX9JJHSDGdF43dZnibcoq1JvarJnxffKyIG4rw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200206153703_1581003556435_0.26341563276642255"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200208122908":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200208122908","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1946bed0c22d2e7e906506924c0c8f849d6e745f","_id":"@atomist/sdm@2.0.0-skill-output.20200208122908","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oRl/XyqBZhrL861rcANJG0Y+koXDiIEG22zt4ykOZGWU+oksMNQrJuxkhKuYpx52eJVD3/hO+PpTMATaMIWrNw==","shasum":"3456b880c3e510f0d2800cb0e277e923078c8de8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200208122908.tgz","fileCount":1170,"unpackedSize":5827661,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJePqoeCRA9TVsSAnZWagAABJwP/j3EydWEQ76gv4jHTa2r\nJw+0DVjkhhIIRSnWAUebE5Lf9EfTdHF4fzunpzeX0Iz6IKOsCajkjli+Ahab\nwCudgAVYY0YrgMlgK73oWmLe8gHz1ntcFrOYKOpqq54UVgzkwXzXGzxRTCAZ\nrWLpgpBE8y9SDHHlHoYgog++7ooBUZrwVHz185Q3SoZAgFM0gK3jk9a5bUIh\nOanmplQIp+kPReKpkBForLhJF7rtd90yj4Kr3nqhAJGgNrVjYf6x/WaV+8zQ\nCaPsMiqEHp6KUJYoaJWKa5naqJdGLh6BheRPn1DaKniU2Q9wvOzfjOaG19dA\naK9EUUKWMnL9gwVujFYbtn7sJ3+iBQY9WBsEc/swP+Eo29ubC9XAJeBc2sXF\nbOGHi7WujCC5IaxY0dTsEC6nHSfsc+9W4J8mV7S1sCLDcco4k7UUKnqIbAG0\n0LPVUegJnpamId8h2sNXqFLXsp94PH/SB2x1o4FzxxesGjNXf6w+RRqWRrag\nEvFvFM+Upu21pamizvdQjh96eExf7yQKCxeZ+Hfj8himVJspiQct7LQyjoow\nWwLCNIPFsAjZOuzJ2MZCr9tEN2yOQGTL7/FzMRHgpJS+mOcf9yMhnAtWML4z\n4yvJiwEI0EA/R39BmZDufnvve2XIR0OcoT2SFs6+uqnj3ghYZI96NeiNbh7n\ngJFg\r\n=4od+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIErvyyzSwBMr7Ze8O5kc/Th3n8AsdmdU31Oc9OtXiQhRAiAvY84HJO1dHUFGUKILb5c6yaR2g8mMnL4GcBxDBgIiZw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200208122908_1581165086152_0.23876034440533522"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200208174656":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200208174656","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7200eebd8fd0711e492fa22f8ab1e5b00a2315ae","_id":"@atomist/sdm@2.0.0-skill-output.20200208174656","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Tvae8s7Lo2ulnEkCaCHzEx31mdOdJNK59Ih2L9Rg6u/y9N+VC08en5WAhvTr653JJu68hTWUa2Bae+M5RWJGFw==","shasum":"e826b6ff55d6d87fc806be2b91c583e26b06f710","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200208174656.tgz","fileCount":1170,"unpackedSize":5827610,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJePvSwCRA9TVsSAnZWagAAzwQP/A3nXc97TBYIyCQQfkA4\nxOhTt8CWKj5JhZbLmMpQugPnjAu/1pg0bPz1FIlPo/TJZM9o1oj1yIMa4DWK\njL9Dcnd1CJKWF/H6FRh3lCtutPX5r5MEXNTUoW1B0ff0Ey2TSHyB6xkLnIyp\nPP68VwHWTx+H362e0L4J4QLPum3lgqhydq8v1A7GNzUdmYpf9MgqhlFgfJnz\no5MI2VNIElbz22w2YVhx2+NkfpfWnCDpyaKYkoIMov5AqimDaFTyvGDiwuNl\nW+6D3T3Y+493dmYRRRRvpa9nEs9wmVUDtRsx4Qy63qJHIoR8AJLv+3C/5tqc\nmb8CMBvkGCMqfr2kH1K4KutEhMTzUutgNhI/3gMuJEsrrORRtMe1CAIm1z49\nkb1VpJU8FQpg/1sq/RsMemv/qrCkPfKk+XzhcdMlA9W9WsKaRH7Xr9GWzWB1\nnJdpS11DCga+Dysp/ZH1QKvrfAgBp/Rrp34ID17VeQ1ho7ybksKgIFsSvPj3\noAXI1INmKizMhx9aiKZElFt1sP9BYd7/zbkONvQS+N92RboIeYi5hi+xbtet\nUGi3jJ/pyk9Sd/Vi1N2HRtVhvtSexT9muCkf5C9oh1eYtLwI/CF1ajEIrAPF\nPqy2aS3Tlf/zLWyT1bTcoAdvlf+B9LNnjDt+K/1AIP2xqwqpx8USuaR5RFNh\nReU/\r\n=VRjM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAwCLVpoug1eTGhTRmLFQYo5/ywTr9ifz+nnqFW1nFpPAiBVFeQtnPSpGTzhVCJURBG1Nyx5hgbk4oaYCUEGCm0xYg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200208174656_1581184176317_0.8730312180840092"},"_hasShrinkwrap":false},"2.0.0-master.20200213184450":{"name":"@atomist/sdm","version":"2.0.0-master.20200213184450","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a52bbdeccfd3bfaa1f6aeb95ae318017517afdb7","_id":"@atomist/sdm@2.0.0-master.20200213184450","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-ggT4S3A0X76ViKqQUrsC0TEKYQOHnnE8NLNstkSqOhQAO3xYLeBkwW8qvPTwo7f3LelAX/BOH4Tb1hVJW9eBhQ==","shasum":"c512cf3fa5742c4e74f424c90877d0b742a94483","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200213184450.tgz","fileCount":1159,"unpackedSize":5699358,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRZnLCRA9TVsSAnZWagAAEPcP/iTPbI1+3eWRGDMo+/7l\nK22hAW/AEV7x6dyhr6oNTexYHEWqnQAxE3MwCq9tfq986pdFAYoTpMtFck54\npyXpFixYFx3M5kgMRKtzGiGfxpmr20gDNyO/4oyRQKjflGw5zftr6ff+OvTV\nxvBo7Wkyv2L/yPv13xLRazlag+n9cHKJUS0VtJD3vLtm/JwCGATdSaH64Tfg\nj4UWWKMVchpNYcFK/ud5lk0lM48jrtaR3mDELAm+nBoqiOlIngFqyWpUos67\nUgkfJG/mljkxq1W2YJBw6297rFvGm+cc454op36Zfc66e8TfDv33Ktuxiq6j\nXK/66WNmnjwfOomX2pWgSsAUw67S/MX8nHUM1g3ZXkhIOrncKPYLbZAOnEY5\nwD2JUrY1Mr8xbWQALi8VPTx1nJqDQOS9rZAnpgDKDyS2gEFwMoSKfx5HYrMm\np+aHeXtVmwVGi7BpEovkRsKkwgD1/xP0AW2hNSFfkhtChRVQiBZ3zsbyYxRi\nJR1HPTTWk1b+1wos9kKk8kp9JEDVtTKuqe82NdcnRqgOcoJ4Gu44tmmEMg8H\nmke/X/+1C9BpyYWBW6YD50TQZvifGJ1vrJmeEMfi/i8Bwo7rpyPTd7cDRyZ4\nCyYOtrPao1YjAVLcWh+36iO7i7F2KmympqzvR9y9k71hE/OxNGT9w+jHqgJ1\nd/wY\r\n=LbmL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSknAgyyIbgPARq723oD6I+ViZwi97ymWPId1WqBxSLQIhALRZwuIwZQblfFa1LpLEng7p31ky5ACo8PAWHwfvEX3J"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200213184450_1581619658495_0.09091248284488729"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200214121905":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200214121905","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cd17d6f6fa1c94d1163afb100f14f44521a0bf6b","_id":"@atomist/sdm@2.0.0-skill-output.20200214121905","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-geJnhM11AVkhmZB86zx80k78RjwoqypeihStEDqqISd5CpyUuwMOA3sgGJwsOpi8d2hpjwKx1luZ/n6zMVX1mg==","shasum":"a10f1cffe4475eef337493359d34f1c6d3ce014c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200214121905.tgz","fileCount":1170,"unpackedSize":5829406,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRpC/CRA9TVsSAnZWagAA0vkP/30F5xRZVRd4mPL4LvU3\nnox1HK3blC4SWgJHVT1skMj6LwDPVl+UNTLfJjabijveiTKVLNUF4v5U9dSC\nCu9edGITVTuvA1U7fXeSrr9r1ob0EWOxjvckGAusAv5FPcjvun9k4ym+/FtR\n1iC2lqC0zoRlYHo482uKlr2HOUNZ78wXX9XW7APVWQJDEsl9WQdIjwEJP23A\nkH8flZ42G/D1DYYZxElrP5Rzgiv/ijUJtjYUIrfXzcPZi0j+ydnhokm4YdMJ\nZD7QftEh9CMUXjbeNo2VT7aL6jyj2YFeGzZg9zNf2xag332qy3D+e+vi7sRR\nnV9nmMDXzSh73Ic8ddU+NgXT+KRF66BdSBlSaAZQHMq7RZ2d5ox8yk9ILGQ5\n50n2/vdJwbHWiVtjA6Pjk1C1qdc9cJ7zrcWQUJpfUK8Sizt8D+k6iqZO2mYF\nKVptmd+38GsDuzn+kMNOJrpw7LSkK5TFwMgH5mVU2qQGHbfQ+lM90XBX/1de\nMdCy+zSImJPZQupYHMrn/NnSfLhbOj/tVNVl7+qegSZDfiIKXT2Osc9mqNIl\n34okKebxgxbNgNFKREuNBVxHhVol49USWiWzMmeV6mAs0tspiqxYhV8wai3U\npdPmaYDJ/px//b22a71Q7Ft11PlALoIVZDcBzlLM8WBogfxJ7VoIdhx3muLZ\nHyoJ\r\n=Tdtw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCt2v7+QDLu+7HAtCafDrha5wtvf2SwhCCIhuxNAfjr7QIgDNtsj6OeeFPfmKFCAbGAm0y1TlYN7KU2BhllwczqnSM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200214121905_1581682878525_0.6787685777091419"},"_hasShrinkwrap":false},"2.0.0-skill-output.20200214123548":{"name":"@atomist/sdm","version":"2.0.0-skill-output.20200214123548","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1c1cc02b53ef4d9abeab2c76e9302ff4e82cfc46","_id":"@atomist/sdm@2.0.0-skill-output.20200214123548","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-vvzUG0V6jko4WiegFhVAPQnznwfTTP2IC4snTk0CBzlEnzY+cbdLqIxLuuQDkuvrniX+t7IGhJ4+bZU+z690gg==","shasum":"f5795ec8cfc48ad13b90903ac50f8e0685377366","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-skill-output.20200214123548.tgz","fileCount":1170,"unpackedSize":5830389,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRpS2CRA9TVsSAnZWagAAoKkQAJNKMlm5wMMfwdR07liQ\n2SP2NzhgeN849yTx7tXAYWAEx1/lquFB1T2ZEFMzVwrWEMWheAQLI4RIZH7Q\nNWW7b8iH4ej6HAlVODMpsGx1M177ZXcZ3mADKvTLq0XUx2KIkRUoC8auU256\nUIWp3VXrUivFm+laXhFSfVKvbsw1JDvXIOiSuKRHoatKcsB29GjEEiFJebr6\nXpKmzmyTdbuyJDxi/PB+nQwHTyihYZhZCoxz2wK3Ypm/HXGNGR0sTLX8vs7K\nCJsMZPaFKI6rpL3vmWG7iV0LYo7XgFiu7rQxcYKJDfDKhUen+KGI+qP8dxWI\nFv6IMUt4rzo+bVvSutL4RMBYSDIkyRPxr7AEL29edFlhsHOD9a2PnFCl13+d\nSJiYZFfA+2w+4khypyNcsJ/fbG6t4mA/HfU1UznMVRh5gct4wwUH1MGR3opB\nO8P+GJjFtfatngOXI8u+LxHgUP5bdBULObgqtKCONJY+QPJcJOz6jxKUHxp7\nGvuoBzWVWkd+SkQg4aJbQqit09XajI/l9TY2iCXfye3OjVuSTWfN0cNEhNT4\nEajchPwaWIbGXiw92/VVf9BPtQMdXmrly3mj3zMk9ZgYYL1WJfiZuPPmFT1I\nvbMhM0akgd6KEx2LnJb5iYY+4coramXSzIhKqefIgMBhr43NI2S5tZaPffrh\nxRvX\r\n=FhiL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQClr+Yt/ddr2i82ybPW6tD3f762W26ZWRACtlrKOD6JKgIgU7oJfO+XilNllVBMh4O6RITlr02kgWE378v36kSp6/U="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-skill-output.20200214123548_1581683893974_0.3848444781352869"},"_hasShrinkwrap":false},"2.0.0-master.20200214123953":{"name":"@atomist/sdm","version":"2.0.0-master.20200214123953","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5bdfda40373a1f2c44a419d2cb9841d3e0c51086","_id":"@atomist/sdm@2.0.0-master.20200214123953","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-4c0eG3JidPP8L1R4vwc2wr74x8rRbEbkDJsIjlGE3I5GSXyx5pzlf0g6ym3XzyVtea9XsLUAz6Amup+L6w93cw==","shasum":"60fe010de551bf3782c786ebd497389d1df07ce3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200214123953.tgz","fileCount":1170,"unpackedSize":5830467,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRpWqCRA9TVsSAnZWagAAQaIP/0PYx9vworVMuQmIaZv0\nF7MTS6b2V/zAsotE3MRsHzCCRsAM1pTcvTwASRFRIt/VpTSWMd4/xsZ029j4\nvArCOFJL05BXbxf3RA9QN1Img0+k7xn6MoiV5HVo8TnHLGks1i3TvOT2P5VY\n1PYtmwWF+evXl4FxZ457NURAaGE+x3CSOHqruZX8oA3FTyWnuhVXJtx+2O83\nuYbwD+2s+AQWgb2gYtVt1Vmrv47xfWtbn6S0d7tMQBJwncEDZV4M4Yc7hR3D\nnR/GvSd4GhMV80V/V3dvb5tZWLLTZs3fdjvC1viLO3+NJuf2kHytNGemWOEx\nMw6rsRIKZvdUW3D/snHQ9gthSF1qE2WmvvqrNrLrLZO5cVZ/qnXN1FZ4jXHA\nNTj/rlLXas6xxO0Yxe/6Dsq5LDEn8YOq4trdZpzGUsbZHP8vGJHkxLm6LYci\nxLyYC7HXHnIlX2M3oGOhPbsQCiXHCSONddGP40VeU3nivlCWv1eQrcHEy1lA\nfWr92nZNdMQ2VIANWYuOfChnlvYZ1QV7GKgimPUWQckUdaTvi+DhGi8zazRV\nkTr9qMYxn3JKa8aBjk/nTC0P+PoqMZwDkplqXDJWgKdym6S9Vcs0AbNmkT3M\n7eMzCzvJMObMWjO2wZlMYvQsFz6lk03QujVUCPlr6b3z1XTo4Clnu35GVW0w\nnhXk\r\n=WsXH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwpfLQsqB8nnGgGqth7pxh6ybJOYElgHVllY8C57YeAgIgTSyX3OGz4rfhG/USEb3YXkRA9DT3tSnUnKgFEXflGtE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200214123953_1581684137552_0.7884193321559878"},"_hasShrinkwrap":false},"2.0.0-master.20200222012253":{"name":"@atomist/sdm","version":"2.0.0-master.20200222012253","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"061420061f327efc4147a9a531de200bd44107fb","_id":"@atomist/sdm@2.0.0-master.20200222012253","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Fp+BkNwdy9xXTN2FhMOjeCVTA6T48UrBg/EQM1JUK/1M8USFCuUu4IgGcymPYCsbDGaGoruOduAygmNlJJmYCg==","shasum":"d99dd0f0046528f30f86d40e14689fda58c32be2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200222012253.tgz","fileCount":1170,"unpackedSize":5830715,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeUIMNCRA9TVsSAnZWagAA7GsP/iWaTylqFQQFNsoYbnw9\nE0799C1jZoUsy7XHsO/2qvpLjDCzRd3hccoDEcPKcBTxXgYQ5RWT3a2I+VVH\nURTirTgHAh/53nBet+p0RgpvT0PnltJkEb17lhBaQEIuKxe2ka3vg7apu4g8\nTp6FUyraslBSmUYmHQhB2y2j8KSjfOjWP5rLemESBxYSyGlA5u0g5eNJVY8r\n+yZpivV4xDAM8jOFTFMPk0zHK1NV31DD0oTKS/c/RwvkLL/BZo/Qlpv84fr5\noS3CD4KNLy1IT97QAb0xxtFpvUhxTVpZdYkNunrveXWpsqeBCZ/beowjyCGc\nKEJCs+VnkIItGFDuzF9tdpf5zT5XqwZgGAleEUDQQRBoQzjuGUnLcsdxolEL\nieHtNqDjgVU4HlbhPOrX+5TClq2GnKvcWOI/t4p2+vGjXnG3hRevtfZ+rb4w\n3xw2u5n9gdn5FlOQhabRQp97GD+X0o40rfkekte1Tm12Lx1jiMPyXypNbgKu\nvrz/LGv+DX9Dmyq1m8WPaL5WHh8ytNFbZHES/sNo7KAAbo84PL/wnwHOg99v\nHiFWX4dgx3+2ZnDyt6S6g/IvQI8VIykWPrLV1Qxikc4SPJo0++CkiQJku6sU\nvQfnE1ZIwzSvebAQIbeUhIJF9p8IqE4ePuLNmjcwAs8qSXYvOVJ/2WLBAUTE\nXQWt\r\n=B4Xl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEx/cj5YcbWuFOJLYaMewB5NMKOHiZmJRBE03/LlxrDDAiEAwnLxa6XJeW9OZHlxGbgfTcCNuBsYuEACn3ngbpahkTo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200222012253_1582334732527_0.3815106244645008"},"_hasShrinkwrap":false},"2.0.0-add-core.20200224203654":{"name":"@atomist/sdm","version":"2.0.0-add-core.20200224203654","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"084ea8feee0adba5ffa0a4c09fad22415f8b944a","_id":"@atomist/sdm@2.0.0-add-core.20200224203654","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-vsSBQrVudKeRzPKTKdobxtb9qiM3DNOW1prsMtzsH+qVAKyXZF9BKAZf8HgHjCnm/tSI8XgOJAli1ZsNtGRWPA==","shasum":"fa40c9a1d02d7f3318a191a8868d6f869c88fa87","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-core.20200224203654.tgz","fileCount":2064,"unpackedSize":8737523,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVDUVCRA9TVsSAnZWagAAapYP/R4G+Eox+8r3pXB3kSGH\naJFiL6VBs0chrsG3shI4o2GC4SUdeQefFHonkDBedM7n4L4QIEK2Wd1LIxOM\nTYKJmYY++PQwUogfecG+BpzzcelLwZuYw/WxR3CGDzWhtfCprOisE38YwwOn\nakPpM9UikMLz2gAtHGkf1GUzJY4v1zaOtAz8ANd3N+uerQay/8+e0nqDOZLr\nCWTKJMJIO1XcELh+DW5VaAei6VO/ECtPR2NTUcywOkEeLMVDOe+K4ygEOgad\n8iIpXMrtYLgn1fsAFWU2GlXLi7TtjL3+1MfaNhPO9a4wP+PeipBVjv0yVglT\nB/kQQu+VUxlWC+VazjXhyW/N9k416wTtunx+11E9w/2jY46JA4PowTD8k4Qc\nbgVeM+XfwubPxdlr/KBwWdVzjgIhbK5lEb8spOioh153wip9sRFxhkXFeaOh\n27v+ms0DIT4U2VRbKxvPygVUyZjbNYgG6y9fKT8HlVsZkCSlgWBt+bE8+IxC\n08c+ysDn8dHrfVQvgYkfNPLofJ6jg47QQZmJmirftIT+qOVdS/Ie4DiLwoGd\nWDZTWCDiuSRDqH4Smg35jU1WDU0S/CQAMM1ShO8WYARMifQ1nGPGRivbf0IO\nWqbTMifW5Ikma60LW3CRgk4cwAeYDHo45VTmz1jL1xlthXFXyB7pC4J92o9e\nzs4A\r\n=NliV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCozXioVAcHsDmqZmaloXLTqX2SWbHUE6AoU8/vkV/3pQIgL29mS1bmO6K4tBkDHiuZHP3TivOKiVYrrf0F6xrRwgU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-core.20200224203654_1582576916710_0.7138067077662087"},"_hasShrinkwrap":false},"2.0.0-add-core.20200224205437":{"name":"@atomist/sdm","version":"2.0.0-add-core.20200224205437","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9e2c13cbeed17592d96e604d109eda11b36e8293","_id":"@atomist/sdm@2.0.0-add-core.20200224205437","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-piXUGF1KHKePjPnWl8MgiZbrMsytnupNcnNyDZBrDiSn/gDOZT+24mWymjX+sgR7eurre7IRgnRI0Kj14e5o8w==","shasum":"5884bbde6be56777d781db0a38a233e366987dcc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-core.20200224205437.tgz","fileCount":2064,"unpackedSize":8737523,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVDk2CRA9TVsSAnZWagAAo0wP/iTWJPZjTjWXLD0TNiVd\njpMIPVBhNIpwmk+2iem41rjKL3YH1nONvuDzdEKTy2smvZKI5WTba83HaeaJ\n3QKe45hBH7eoGxM3ASP9walGDw2t2pXlNOf9NyewQrpIKldAyOpivPKcmOP6\nauf8tz4ChNuKl1Wcbtc/zXx/MV9ACFJn30XqtnvQwSAWuwqnE63T5+2usLON\nzzAhg7t66xqkcLQs58RSedtnnixely7lwIz3i6KEQMhFtQniSzONPLd80CZb\nxEvWW5OrrxZHroxK7NMmFsHur7+yYlda9c11mXFAukhqDCLpfO5UwAYeRgzN\nrmShV0vRHdpWkbOPBOZzRYYRBJO7lcGy0GVMbspwThJN5WLclVqJfp55qpUF\nvcOE1cbvFazIyn4B6bcuvPKN13V2qw6RouBkhSUnw9OKblJVFjztDC8Qc/yD\n5/p5qTQ+GOi/S1pke6Nu/W+4K269BQizQ9SM5U8epaPXt3CrMr4skUysWNzp\nPkFgbCfcjpfp1sJ8lBPFUsaceslTWnZ3EccZvCsOZOKHC9avIynZtdG6wZ7S\no6wUNhvURqqdAYyeTKuLiIxrPB3kLC8LjUf3Ky65wYhejb2ZrfzsLVVNDY9y\npy/KWdGM6QurRF3LXd309Aj5MHZ8AwqFZsgDrNrr0ThZwQMG8dZly8nQgTEE\n5tLA\r\n=mcRJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHYP0ApC9ucBwBphAV050g7Fo/mulO7rLYcgeRMyz6rYAiBj6AB73GNNCRRMFPnJx9SbBTpmj8x2STu4yGDPwBFjrQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-core.20200224205437_1582577973483_0.14703402108325392"},"_hasShrinkwrap":false},"2.0.0-master.20200225134700":{"name":"@atomist/sdm","version":"2.0.0-master.20200225134700","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6a329e249f57352e2726a3d00cbdfc98aafafe8f","_id":"@atomist/sdm@2.0.0-master.20200225134700","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-+rt15YIa+usLV3M63wvP8sr7UJ86/ivT5+Q3zrTqAEff4KCxj5aQmpFuqP7f8GC2eDz8iPtnkGOnfXcd3+qj6A==","shasum":"406818019a64ed28375423003b5ff410c081c415","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200225134700.tgz","fileCount":2064,"unpackedSize":8772486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVSaACRA9TVsSAnZWagAA/kwP/303rQ4WKEDyKHPkdBDl\nBaj7GLoIktkIM/RSNZAyEJf9kBEvXwbsSAp4HODkm7Tn4MGKf8B05RBWHDks\nSqdwfynQGSU6uKuhdTMICU5YuuOW0GrWocohpCn7Vcvjs26HVnaKKh3goMgP\nPd/1tD+6rejag4iSVK9J9ghC9xKBTXln0EsHsVmlKYUTb+7m6clsXULGIdVk\nJpnrG8DslLj0sEphWWk1AiJMK7vOFCuwWOTVHd3vAFhC+jZgmnrr5FdLbHmw\nbrEF/t08aMEPhBG3LFExbBCZcvFdmxYLmMj2l31zKyVu/ZIgkQZzkZwdVCoA\nOpNyDFNNKsb2KgKqnv/gH/VJtINFWieh92mVPQgL7Y4v6flhitvUKOAQ8uaB\niAGjfz0OgzwB3uSn2nZlBgaD3Tz9Sr3/il2i7/gUZ3+f7JNaAhAwW0p50aXk\nV6ywBqcn2IpE8OirBahBxZqgn7NhCiRJ6QfTP5HxsZxtmdabxhrfZWYnMqLz\n58LQkxLl/79kdLjoYegA+PbeW83En0MZmI0l3LJV9v32qZUhNJbc6Jeuysem\n84aRucxXZKxpBqcOLc9oUnsNoWvYAfaiUr2TsKC72SQqzasBTcl7R+Sfz3bX\nCX/BI579HYGkJ/ttO1gTBDBWGU/A9zigDDInu3u1tx719SWpZuHRjjCXy3yJ\nGeNn\r\n=4IWJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZxcnxj5bKOS+rk0O+FB0DFqj/gxSI9DXdk7cg6CbQFwIhAMqJAn/uEQcDV5ISky/c8WqSxachrCENnePW8fd6l+mY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200225134700_1582638719421_0.9090290179388008"},"_hasShrinkwrap":false},"2.0.0-master.20200225134833":{"name":"@atomist/sdm","version":"2.0.0-master.20200225134833","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6a329e249f57352e2726a3d00cbdfc98aafafe8f","_id":"@atomist/sdm@2.0.0-master.20200225134833","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-T7SS4HEkibnPlt2mIk8Ed6vEo+dGd1RrxHt8SFeu7XESnifmrK0ToJ4KSRnCDev2cktvEx0/hGOcCWEeCkNtGA==","shasum":"408079648d4289b46237e88b89fa1f8ba19ae5bf","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200225134833.tgz","fileCount":2064,"unpackedSize":8772486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVSbOCRA9TVsSAnZWagAAhhYQAJu6wzxkLqwb94YgEMPa\nwhGwizMkWZyLBE3hyLuFtPRkJ2lVvjwxQxQfqlvsCFK66v2P3BdL5jgRIqk6\nqjypDLT4B7qCWmwO6ZEvEpmmK3SxU8CV/RmjWf4bWo4cKKZcUWsTSW/tr3+v\nP06y5pw8KwKC/jA+whb8uHrgCBaAf2vZzSWid9Lcm8qG+fEefurbMgQ2x/dL\nv7EQ9kaYUzgoqw46R2jzs8ACB/JcP2C5eEUj7Kf2tuYDS8/cIwaJN5KJL58l\nBdhH84MK0Q0MG8Y3p1lDIlTJGZdtPkQzdVq3IyqzMYFV6yZAO3gWAY/4ltkN\nigFzBg6ravtIk73B1VcnA023z9+EOFi5UoXDM3r8+vtKwApDIMOxjGjYk142\nk9ndcjAxP8RS8746QcRU0PRTaiueSR83nR1vy4uI9F67PZ9EQvIK6cmOdGDy\nJLEkaLAAIcqzO0YqqxmHmJ/CVvCSOWw4QJ+HEzJcAGNiZ3K2bB1mG/oKneaF\nrDuV/0ytAkfX3iAyzZtK9PDQxTFdF8gkCsEXcpjeF7wjsBN9opGmNWdQfc58\nN+t9vJQ0wNz8pSxvsyYtXQG5bZjyWNMrchce+V4mt4gg027Xu3yoru9gjsM2\nLW3ts7dBUbc0fD0tN+oW6prWsuwZocbJxBVeiNbgknhrzgsiOIfcqeYK1x8e\nGxsN\r\n=94CJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkdRyNt6vBZAYQSJWy1urjQqsPk6l8bi/wn9z7pstMpQIgZW7pOdxiZSOJdEh81/W46U9mteEXo4rLduwTDx9ZcgE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200225134833_1582638797388_0.11125688013484991"},"_hasShrinkwrap":false},"2.0.0-master.20200225142134":{"name":"@atomist/sdm","version":"2.0.0-master.20200225142134","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-s compile test:js lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3b7f85297dd5b4b93f448af82715b3b6a55177e6","_id":"@atomist/sdm@2.0.0-master.20200225142134","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-f1Msans4Z03HubLlbqeh71U2XOVvdmvfHE5gcZFXf7pGE+DEK9rYa9CIalTNX5uOzc7mVytHhR8GVFZRUGWd4g==","shasum":"52d4f3162f87ffab4dd3dacbeba0054a937ad543","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200225142134.tgz","fileCount":2064,"unpackedSize":8772584,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVS6VCRA9TVsSAnZWagAA+wQP/jWWnLAubesRXtrkO2df\nTy5aTcEEdD1MmtNbnvjG/r1MUMkOa7oQWocZ1BIxYk0+QzVM7l4BB2w93imv\nWm4fD2990gg6hcsZ2SRba1Fd4rHj96ORu8y/wNvHY4+TD5RugQPDEOIKa8Da\nHKSLkwiyfP6Pooyv9uJAice3O1GuePwCpl17NvNrUDDVvmGawzoEtIs8+UNA\nyFUKxcxUkwOwAM8mOxSJgqviwfVWsYyKjrcXG/6v/Y7Mt82jvvANvSRft2zR\nmHrTS1cLLMXxXOiIgWh9vMp63V2yhHmfrbmfyEhdxIj4HvZ98Yxe3RXX52bG\nB9Jd/EsAXui4yWGKxIfBzrAVNkL+CiFe4UGY+t5X6Lz6bYdFrNqnAtUN6ERY\n6tLJXA3JdXbh1oetcELVU3R+0GV6R7J0uQAzJBmPRBnFi1suffpHVNxLSoBd\nseN6hvFj87viwzeWQO8DwYs3PsY7jxxEEKlp982jeOF4Nkgx7rjfgpiduaWB\nJU86PWISVyh2QxcG844CQEnCubaHhG5KSxD7ax53M5vG8S6B+l9ssGNJw4ar\nHMObLKtu7ygKkfXC6BDbeu7Bpu7JcmgSZCESM32aPyW8ZCySzW4AsVH6Q0Ez\nl3bfAK+vIvViJ5LB1MZMhq9LExyRdUAwUxUD2MSD4sfwFCHBmgm46QiVWuEE\nuaTZ\r\n=3Cul\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZosdpZcG5+NzQfXNK8TDPvs0gzXjYeh4NCyBLq9b+7QIgUw09vDs6rcFqJoxP5lV01HXkuvc4kgnBjPLTJFOokmo="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200225142134_1582640789139_0.1346100583261436"},"_hasShrinkwrap":false},"2.0.0-master.20200225144423":{"name":"@atomist/sdm","version":"2.0.0-master.20200225144423","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"953f81e0c3d314d045c7eae4f06a4f02dd3c719a","_id":"@atomist/sdm@2.0.0-master.20200225144423","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-EVqzl3iMVUaOq6vSldyZ/gm6pV/hHpECY+ViqsVv18N7bK5ETPkQrkrb3oTlNLTmZ7J2p7TOvYgD7d/LAId44w==","shasum":"5d668c82810638d3a0a3184cf4a8be296c6196e0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200225144423.tgz","fileCount":2064,"unpackedSize":8772626,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVTPYCRA9TVsSAnZWagAA2YIQAIZvbEbTD2SMh/Ctp+Vj\nRHeo2RaUnRY503I74w3RdwwrPjHf1LkzS7mmFtiTETRiezqbxEFo6tHSU86E\nEcjur8YkBJtFKQ1UhwBk3lVV795ZARmdZBVD6wdo97K7j4KqnWysQGgBFf0b\npB050XRu7Qse+YY+lsGdqqONhutYr/zt+tCNUzpdHqnc5YVSoy4TgoQXtpCG\n0lmNPVETIxiYx1Q1uGtlHChij8PAhmBH0FHlYtKavn6SFFUqSR9dwWL9f1eL\nEVxz+KtVt4CvnNAr3h5pos8xR/6VCtX0Y6JA42j2xJ9iP+dYQEIRaF0hw6im\nHbrhMxgG6wdVZjaDig5WSf5jsW3/YMAIbq503ZXy2ONBTHOIZJ3jICekddZA\ngJbQRl1hz1/zUGFD3cubdtDALr/kJj6+q0cIg0+EYVNyLD1woEhJDJ0IP8Bi\nQZpEsU74RvBM4ZopO8DZMMll+BXpkd4dn2oVattl8dcjQKfmIMh2ICwey5ds\nJB0A/dPW3J/SSBP7LIDDy1vFbN+sEfpvh4cKZkaCVzQoAM9MZSAHoz+4dHoG\nVZ295t78Jcy3IOW7JeEKWmPTgU79wVh07ygwdq5rPJG3K3qj6Rpc3WiswiW6\n1N1u1pmdtYdJFbBQeVKESEy8cvzJditxRpnd+B1C7SDKOcJ8bbHSiBmmYoOg\ngbiI\r\n=mUuV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOXT0ZQxYE6HBs9s/JVI53cOPDMWAk261WRUPzT8ZoEQIga7YX5Pdmz1/jIMiu9ItLw16+pdgCBmFz8cmhUZaOeNA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200225144423_1582642135705_0.14931904388895267"},"_hasShrinkwrap":false},"2.0.0-k8s-cleanup.20200226094648":{"name":"@atomist/sdm","version":"2.0.0-k8s-cleanup.20200226094648","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"8ef4b9943d595d388cb22dd0d8ffd9b42e4b2baa","_id":"@atomist/sdm@2.0.0-k8s-cleanup.20200226094648","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Ocuim33ZzqMSw9ilEFCKeCW+ywwrLWD+XPgvNdkZC1T1htIEDecnjXzLVkkpbRNHF2MfMqVv8hvMd+cJJiQ28Q==","shasum":"1ca06d4823f1e5db4f77579a763cb240744da374","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-cleanup.20200226094648.tgz","fileCount":2064,"unpackedSize":8768598,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVj+RCRA9TVsSAnZWagAAy3MP/i4/6M4YAMGrX6PZiUVl\nojcHwt0r24dqGyJPLJBlF44irf1yc7U7xOpmCnZXO3x2w7wJCypuQYJ5T8hn\nxSKNpK9QSq+m8QWXCbbXxtjd5udxrFt9SWn7wuuUd281mD/SVdUodqa2vekk\ny1IPAd+xF/f4tjHF1l5S7/69CkgnG2pktwSc5mLrqBliVLFyGmzLbm0u0c1v\nNNWbji5wx9DtNVReQ56bVpKbl+t5PsMWMELTa4jnimgx/v1TQluch2K3zLse\nBqROqG9+N/4xzsam/j+LWST9H3vUwfQmAMbRGys8M83yy2/uKu0mkjFWmQnP\nmSX49hK+NUtOwijgaBohhi/6/udZWMXCX0FTQX8xYV6iC4ubPYYX90yaYq4w\nGWBqmKCCX93qXNrh3iWduJX2nrBK/WVzGw4KwGsx+VkDGoxGcqciNmNsmk2q\nAPiWQA4xAnjcGItUEhfcEEdw+8eU4GoDRl4o70m0d59mL75dqIFYl+dZr+bu\npI7cpKBDb4gXE3oJfdi/Od/szssCyxLpUu248IMAnlr6Jpvsu0Icy+rb8ucR\nqWoG6IsmFIubHavBEnNkZraHUGj7l3JLglUdVVR63ul9YR+JnuzoahCa6yV2\nAj/edVrdeV8jM6y9I+AHI6OAZUcb5fU367q2AITI69ZR5+TEtYjHFMlINA0P\nKzf8\r\n=G6gI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFQ0E5lRaBibutprmWwr1flGpW7AOJf5Vi6WigBxFmGEAiEAg2iVg/Hxxrgwg+LcvWbvU8SbwXLtnXT8emcPNAEartw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-cleanup.20200226094648_1582710672637_0.1945901602884399"},"_hasShrinkwrap":false},"2.0.0-k8s-cleanup.20200226133342":{"name":"@atomist/sdm","version":"2.0.0-k8s-cleanup.20200226133342","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"21d06e8bfb4ef10efbd8cfcea47989408a18cf69","_id":"@atomist/sdm@2.0.0-k8s-cleanup.20200226133342","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-FrZSSDlB+NbvPQmvUDKDfNzGkVPhRiIoR4xJ+DfVe35XUoz23Ddc9R4w8bKfG501wjXAQNLMnHELdcM6jCUTpg==","shasum":"1d41d10f94f1dc04e75b3587afe8797a310597aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-cleanup.20200226133342.tgz","fileCount":2064,"unpackedSize":8768598,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVnS9CRA9TVsSAnZWagAADPQP/ip+WgGBznR8KBI3qY2R\ndnVGYoqm9JpTfBdAvydJ9yyVH99Su1+6fKathLHIW7waSpZsVNzRNtr2JDHm\nIzWWa21vUPLltcHSq7xuqy6nqGdsqHl7X7zN9c91g0aZrIjt79ks4oCHlm9V\nCuARdLinRZ4yUK6Hm9lHNA+2SXT4GNpi/Y2jKB7ZhYOoHGxz3+q9kUNdJkim\nMBMu21HDYvnTk8U3JNkiR0n6F7XJfRLQ9tGa7YZS6Lot/F9ty7aXX94nvX3I\nQE94dfaKXQMRwB9bTIgYLuSrMURXIEMOoGE0s6IC2R+kgElZY9mqUXXMHPGu\nhKIDOsBJgOhsw3Y7rvrcZGw9hbw6/EbJnr6ycuBH0ZnDrL1FUSBGItCfk9OM\nvVKeGsN70qWm65xe8SyLw1spXEqoXgmQa5SQFVQiCumkznk8B9TdkGaNrdb+\nwGHNGth5Sc4+koR30mfiMfDAGkB1LLVXZv2bY/j8IaBFDQiFrO9vRBpsqURT\n9v+xWHAb+UXM4vpfKFMNCx8IsH4lXW/9RojFpaQ3Mpbfx2PYzzXDP+kf/E+/\nvCRbHvkjRSOUtvtvZZCZISfHnalYUuznSO0Uhszz9KeapK853k034eS4cRPp\nN0v/a01s3hhvpW7fSSiy8psOe3ucHEthJ3rbq3Y/k0S7VdjcoL/t4PwDT9x3\nvT5R\r\n=4KYr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKedjgd2i/UN0h2IWs5tfjX4mN4freMdTf/IhlX1B7xgIgKt8Lu+FtINbPq0pHdwr10/qDkIgZ3bpUJB7LA6enpu8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-cleanup.20200226133342_1582724284759_0.5241008100545894"},"_hasShrinkwrap":false},"2.0.0-k8s-cleanup.20200226192147":{"name":"@atomist/sdm","version":"2.0.0-k8s-cleanup.20200226192147","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2eb516b28efb51dae3a8ae94fe59d9852e88541d","_id":"@atomist/sdm@2.0.0-k8s-cleanup.20200226192147","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-eukPFbFtTDps6b8LxO5L90PcXSuSlySlVhoAQzf53u82+3S4/C97edvGbi2jE9/EYnq6/oTDN6bv8XRUrTbLQQ==","shasum":"4f474c36f7aa96bea651b5617f1c68fe4dc401b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-cleanup.20200226192147.tgz","fileCount":2064,"unpackedSize":8768394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVsZtCRA9TVsSAnZWagAAqhUQAIlibmjyrvOs/ix3F4Nz\nyEoSm+B4xE0PLQh1RH3Z7eJkgxvNdFZ04jOmN501eB1zG+e4bCcln3dC+VCp\nQ9H7tFS2xgNLh7VldJzaCOdichkqfmKu50iEGdkae1gRkkbsTegjWcyDhE8u\n7z2+lU/Nr0Q7mH+x/kfrIuQIf4pQvdnv49WdU5RhqmaJsf07Owh4s1vjLka+\n+F1O7bOnBtyfNXiCn4rbvHNiTaSFGQk/e+VrAKMHhrJcO2Fgg1Kjo8S+ZaN6\nuvu3aUyme5NHHkmw8hHLOOQZPbwpYZ0AFB2iy3e1NyjxTJ8nlS9AS8wugC9g\nhdjZIbgh59L3MYgXz8zJS3ZYo+sYML4MvteogOjhbHxpDEKlniROAybPkERj\n0GXosL1J6LxB/PbF3Ei20LO+O2YCvuBmmE1MDC9NUr6sMXsGvrcrT8NjUEM5\nYJNYFsmDsaGtMkGOeR7LswVZ2xYYRwMUi7qPWVi/IOQ5xOV1nLNEmA+hylTt\nBcwrv3iR9Xge6ufOWcworZ+VzaetNHpgTmTOsDnJw/HE8a8fzpkal8osAhUx\nqriV5fHjMlUUoCcTjXzpW1ur6+E+7kqaTOoaQHGeVm45imB/GLwZyoyxpwA7\njfgDcsWkuNYSTvClDpUd6PwOVUOXm2el7X8I6kfc0L/sf/66M238ZOQdvCGD\nBCOk\r\n=rmbe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDNPojmv/HyFTuTshJhuU0E5fKWstprCT8hRnF7qYGAUwIhANpT5nXB+tNQ3/C9O/reINfqct3Mfok6S/WYw23oBE6K"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-cleanup.20200226192147_1582745196146_0.17328004556511867"},"_hasShrinkwrap":false},"2.0.0-k8s-cleanup.20200226201257":{"name":"@atomist/sdm","version":"2.0.0-k8s-cleanup.20200226201257","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"00386740182ecea081f72bd5442a666349a73064","_id":"@atomist/sdm@2.0.0-k8s-cleanup.20200226201257","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-uyOJOZGtrPVg5jn4QlGVYwuqp/MC2m1E2XOXhAwyf4s+4U75CNGYyPsQECAanxlJSr6/vxjGRZr8ZVmJ+z9J1A==","shasum":"d414186fc3af01f573ae2f37f3a70466d21a8230","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-cleanup.20200226201257.tgz","fileCount":2064,"unpackedSize":8768394,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVtJaCRA9TVsSAnZWagAAdgUP/32aOj+BJPRjjtX3DIi3\nuZ5kgBxR05hosEUXCydZi0yNLwnlMN/3BLLnZhDYcs9GHWYsO8N0mmzsxwVk\nBprEUhPIAPp6berVP+yF9VfDW7a1w+GwSrDbZwJMgUj/9gQJgiEzvQGBatm8\nQmtPRWMhiEdFOmGtgsjDRDe5461Npl2ecsF+Ovaoiy/8tcc1p5jcDeEdQ4jh\nnIs+fMvwTStL022SwLqXLYspyP6BEiQ8J7CzhmXhOxGVosN1G96yTH+yAkE4\n4qMyBhwaFeOwDOpUbkQjb/yaX/4FV1+vlfE9XOKg5OI2CFe+izz59u7M/zn5\nZXYCgtT7t+VozmKNVIskBNCEUOFE7lVuQO5gl5mNKuEJMs6QG29YV97pOYd+\nshQeZf/xSW/sI7KL9BmHNCJNFYn6u7hc01xdNzLGOlVEt4fX5t9wi8gfAPB9\n1+90Qemasz05BVIdpbmfPL6AznghcnaZ/hXwq25GXxy9y+gop3UQJngHufnm\njhR8gdzyREZYiorrsPi5yGSlRFwZdM3F2a2AmCgncu2q/+pBHke/0mFbB4jv\nFvlGtxUDCf2Zw2xxJLBKIyJu2vsmdIx0yauWguDnEXWzsgZGnZZ3ex5zkU/H\nlLQItag2TWBL2GQwTO1OYR23L4nQ2yIBRJC2pZ8IGE70Rx6t4lnpRoeMfamq\n+22u\r\n=0ssI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDf/0FaDgcivLsVMvoTAYcrl50w9HI3vMylyyZ2anyAuwIhAN+DhFbpr98vnMDMR0+MsLqZJIr21KA1zllT7ClegElX"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-cleanup.20200226201257_1582748249939_0.39406957319645297"},"_hasShrinkwrap":false},"2.0.0-master.20200226210530":{"name":"@atomist/sdm","version":"2.0.0-master.20200226210530","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d2d7f0d8b0f7b0afd579d693797c7c3ccf2d78a3","_id":"@atomist/sdm@2.0.0-master.20200226210530","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-CPFpJebVOVMCwj9eI3BOBh4Huc8o5cdJ35KbUnEBEVsaPb5RTbsk2fmJlE+saDBXwV5QbusoDLJX9dP89Dx5qg==","shasum":"e24a6d128232387fdb3a86432c4e3052b8ca65c8","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200226210530.tgz","fileCount":2064,"unpackedSize":8766959,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeVt68CRA9TVsSAnZWagAApAkP/iow/0raiV5AirwCnGGY\nPP5nK7OFBQgcqCkvc9/ZvdT9gypXBHNZ94B0v4iRxqUEqrjA6ix86V26WMoN\n7+NqcDDF6E2TPx0LDPcn6XcEyTHiaUj1JmARpmtbQ4HVnVUJkLM84MUAFmA/\nsEZAlnwo6NSvCHsTzQkeksTWakPalWcA75orGUO/dnRX48GFq1dg1ShYK/f0\n5BzY39VWMV80y3tv5Y8PgFmGvcWpahFKsji4FzSqA87Rjkh9af3eAdRRYkqW\nM8n/G58Y04Hw/lwwdRmzh20uKDMleVXOegm9bRYWu+CXl9DB/mAjo5ZlQDQ5\nKTNgFGjnLz1z1QRUK0ovJj0LxPhX0qL7tkoM1yOMwSNtRP2y4/Jaadb2dJFL\nJAVAOMQQ0/4F1v8fGVQlT9PenRXXS0F7OrALd5gyizRvjmjnZGAxkagGS/i6\n/UVM5OtCc1cYJfGZXd8gVeCWtsyWrm12iom3A/liw55TA/lX6foIWfjiX8ZM\nORKhPHsUO3d+5qOxAbKsfPXCFTVbLDm1ldmzzn/mCygUkSVOE1eMIM/asx4u\nk3CESGfRr3Gh5xtHd+U50HveU1y/JTzvUu83f95f7LR/irwYwB47IxBbHv/U\nGqFRK9azbK8my4A4OXURgcCsXWlnOdkBFuZodH5EIWiosJPHwCVRARC3ZgXO\ny1Tg\r\n=5fAR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCMy9kYH7lh11DRQKvdGi74bxZcIyj+7WXoBkgSWsldxQIgUWxsad2Y1o4xOkLGMylG07nbJ2Z/gooxGjjYR8+dYxk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200226210530_1582751420137_0.4457027636619362"},"_hasShrinkwrap":false},"2.0.0-master.20200227091110":{"name":"@atomist/sdm","version":"2.0.0-master.20200227091110","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b17f4e78de4f26a3cc3405795ca8ddbb7fa33d5d","_id":"@atomist/sdm@2.0.0-master.20200227091110","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-DrJLH1V8BpdhYrkBDZ0iVOp0PpULSGE7mlNGzexTq+8VYME6czHrNJ9JEKrqOszepYHAxcrJXQgTfW+d0zX6RA==","shasum":"ea92bf523e6d4fddbc594b1bf9c96ca42ecb0174","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200227091110.tgz","fileCount":2064,"unpackedSize":8767052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeV4jtCRA9TVsSAnZWagAAXyYP/3yJwq7V1RklwZJmzcd4\nVun3rF1/BPVvp4aZ7/MQ8EpZgfAB8XCJrefGUa6kOAk9TzaLeEaR9quHGy8F\nZPpR9LQahwc5IALnQfVW8LCdaKVJFHNZbHJd44tVNakH/+okCM6CmP5ZLvq6\nVGtmKJ/9K9APi0Vv1q3UO1q70Fj8kQDAjH/oE2ikfldqMP7lysX+Hf3DHMFR\nW21CTi22pK2j9CPUTeJ2Qe0Ci5H7NhrD6TlSYpLXaKk8Ibks+I1GWa7t8+9x\nnWi5xhDYhtwUfO05bkRA9zQQUW8BTYZFhyeurjDbTUSacQPhS0qzrJC+FBjb\niymqs6FqAJMMg7lB9sjmKO0+uzv3EfVlsw3iuCAX3x9+xb3lFBmFe9jysv30\nXL7C5PdiKQ6XJf8oDMh0jc3/p3RM1yXRjD4bm1teC0ncctJDJZaHRVAnfI+J\nzR9jBjphbDVQklA8pkKS2cxFksNF3B/NY5pc5ABbUYZPJr+qQVSkZwtm1Wkj\nz0GVR9mR+WIFKLvVOODp7OkDGv0n5qXHZVyZi2VJh7rzRzlIgk+C0tY3PR3f\nrkb2rVzVzNI8PxR9um3ciOue8QdC/+83pjHh9tdqwjFHXemwFkkLq4/m1GHZ\nyVXN7oyxaRuMjmoWVzAxGpXmDQjQBddMESoBRdGkXKDG77R21sCWwbq4+rmd\nDWDk\r\n=oyOF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEKqcHxngtk/W0zVPg3AclE6+rDASKw9EzvcWcUvfFqYAiAptdr7VDGEbZrdeb3bawUAVZ01X+SAtvA3HIFyX14tBg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200227091110_1582794989048_0.2912963855808861"},"_hasShrinkwrap":false},"2.0.0-master.20200228152343":{"name":"@atomist/sdm","version":"2.0.0-master.20200228152343","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ef1178b65ee556fb2777bf8a4d14d5027ac81821","_id":"@atomist/sdm@2.0.0-master.20200228152343","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-BtvJnbirbTsMhaNZkcvd4h6JUETrht0h6dvDU69sqNd6zxXEYuRTcY3dmHDjia+iDkHxRPIvMmtMV/MT5kQRkg==","shasum":"cb7d95cfca4713054384c57a5983421774395921","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200228152343.tgz","fileCount":2064,"unpackedSize":8767052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeWTGdCRA9TVsSAnZWagAAw7cP/0pccdu12bSpxQ8XVaM9\n8ZJUOqDpOETkE67aNaQScWOZ8gHNqeB61nnbboNWQrkd+kxQ2h2mvk3XDJPu\ndEefhBxmlO5Vt3Nqu7HpYal2GIgOK/RfYMvNzIoRXQx9GnN9RmS6p7TcIGUa\nx/XSm8mJN5K04RbIoJ2aQwd58m+Wxr5f0W2mhYoTyxLg8dOy70xdycM2tmiU\nJUXwuVGtZn+Yl6EYbNHS47og9DBD9SbTnhqsyLf/Y9LOL3hF0HE6IWscnXYF\n/dxuzq/W1MldYYQoppaSvBEpmeGddD/y0vUyTHFuuelqp/7fbrgNGQ5xqrho\n29K1nu9lkTT/jL9/MJidyUGhMBxUgtYp4lahxshvvdfL4owZW2t0PaEfhkes\nDhr48Ja6CSB5FVJ+MEyrk3V4uHCwRgjsD/+SrJO0NZLdUzdPYC9rDlwIbVo6\nzgXPgYSe+mzbmfDhUf9SpYg4r9Z2XnqY0yTLF7r3DjLOQLzHLKvSxK/9BLzy\nPg5S6/SRADL6sl3ofiPq1It/9FIJVfBv+KLC9KX35K0+uvRmEeHaTXJPDgj1\nQpWXMTCOYkLYmy+1wfW/7QqE5hr9dThtk4/KIoeIj9a7V8ak/fKtxC5fOIXS\n9ePdOE+ImyWzYDU5d38Ox5TCezCdwGW8FKLFjGT9UIy8xb4L8XacZhArayEj\n0A1e\r\n=bZfn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID2LxIbM5aYHp9/EbPtmvhZe1LpLwFmKJIHBskkd3ziqAiBfg9hPTUPNxnkPkbCxUBq2+VnQk1fSVrtpAjmenihqNA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200228152343_1582903708839_0.585995137392529"},"_hasShrinkwrap":false},"2.0.0-k8s-container-job-816.20200228212446":{"name":"@atomist/sdm","version":"2.0.0-k8s-container-job-816.20200228212446","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ac9912b5ec989369df7caee11d33476343634cce","_id":"@atomist/sdm@2.0.0-k8s-container-job-816.20200228212446","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-p6PGaQO1Yjux9ZLrZDqAfPJDCMaobjYlIbJmothL6mLHVdxbS3iq5f56e0HqBvHCjyOYQll5e7SVYfpILzNcAA==","shasum":"d7fd9401bc1649719bbc68853c0aa3b82b9cca24","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-container-job-816.20200228212446.tgz","fileCount":2064,"unpackedSize":8781622,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeWYZECRA9TVsSAnZWagAAUb8P/3PXCpXlfVJt6gLgeOvj\n3jWtZuu2A1SPLEuZoPvPLVe3sfrIJQd7wlNhlZKYKsL5h86yPjh2cOoi9pfh\nlxoleTfYuzTHSagqSDwQ37eRRqFZI+32JdpC905aWSthe4BzwF4WeimzLnBx\nT+BgrbMI6caki86RZsDqMTdwKYuqk5rLgcHt24NQKjp/oSQaA1ATRQGCaOv5\n8Fm3n2pg2qHbhkUDcwWTH6cXp05ISJuOuYBomJhpx8O5VFGgix3NAsNPZDoL\nGdKPEikmHKs6WBeAoImEseW5QjuLeYrlKPKeoCZEN0WACz73WhjPmGa9OnK4\nz87V6zBXoAOrSSUQav9YyfQCwj3UFEXnY9OndnmWYjr7LkN66745FRWWb4bg\nD0t8ZxN3OF7r/wNLR8OUSpn4cUpyJoUpJBEvNBYFaXEoqp74x3WngKDcHIOh\nSG2+k4i8s2FK0vLTPxOhjg/kh2sYsRdz37MjDjKXlTZ2YrS9RNPbzW7XTX73\nhkgBj1x/liHSLFKFTVDG4AhKcWDNghlYOHULbvc1EP+vvKJQboflzJ7oHduv\nyz1pn8PBSDfYlzEk1TiqhMzXkbhZOOwErTMizyu30STV4ribcZn34Mpebyzm\nnyaszmjX1aY0ogcerO8UJnDrCwP4oYQ6bWUCYQJJtNsVCPZkNHk2Zr2b/QEj\nnI8W\r\n=XBSu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEW80s2yDg/d3/Db7Gig0PeXZKeyub/rdhuJ/Dzwb/AxAiEA8SerjA0TimOTH11TrSw2L/WTpRcSIwoJUF1UWAzzBoY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-container-job-816.20200228212446_1582925379605_0.788555748792257"},"_hasShrinkwrap":false},"1.9.0-1.9-fixes.20200303005416":{"name":"@atomist/sdm","version":"1.9.0-1.9-fixes.20200303005416","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^2.0.0-master.20200103175554","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f064e7b015563fe51d7df6b3de3aef7aaa145317","_id":"@atomist/sdm@1.9.0-1.9-fixes.20200303005416","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-5To4LS9I8BeDIf8CUsvfuKJJ2tMiV24/sCLVrtA6nQsJ8JNwkVZHS8EVO+/ClBLaDbBMqZNLRN/Eodsvpmgovw==","shasum":"22998423fd1d7e74194a5e6f7866d80d8ef302ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.0-1.9-fixes.20200303005416.tgz","fileCount":1238,"unpackedSize":5794265,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXatFCRA9TVsSAnZWagAABU4P/RQxTxPFXMWwRikIQG93\n3W/kNutgW4QvCRzSxqbCL2C7jXdhog/Z9Wxe9Nltd2OiMlMYdHuZNUeGp3O0\nCCved30WOo91RB4JS/hgu79UM/qZ7qUzsigQn0x/+4F++Qbi7p2UBd5hjY3D\nnRNBZolNfBrKs6/jj21oCwjLMmR+KYO+mxDn2w8epUXc7WmW0H4Ts103xvx3\nwuvn2Y4vNrZbzQmHGzkAtXwlk0uuTf5+/DHa1sgcAjNaV8kkyFwYU2QkEkUg\nMlkY0yj0RM/2LiCz5TDKZhuQ0Ut6KEmysE35J4Q42j7Ju4IACitDwNCcKK+M\najxkDRg804QZkr79pXIPtt3Luav2nfdCOMlHq++6Q2o0DbUi2/m272t8/4uH\nVjHBvBKf1VWyN01d+6ASYLyfXuYs7o/HXj769kG3Qkc5OyFa2o4Wmispz0nm\nBL3PrdCxkyWwYjihEZ+j79EzEHxhene3ODBuLd6ahlOmrXGTWob0CGYzoEli\n7aB0cxeSPXhM6cWiXIxFZldICpszvBKFvzpTBqMqvM3VB3HQoEyYNYV4asW7\nAghrMk0Bs8zZvZmWR3rlDAdAQZfWgnYqBugEyM5UDXOfWfc8SsrrrdeCEyAx\nonpp9TWrh2wgduGC5LwFb7rbOYYBgQZbHN9HUFY20SCiiXgbmp7rZJR6M2Ax\nRjuI\r\n=zNYc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCY8XjEFxjNNgrjHBUd6AciAfsr5X40kcs9OhQwHvYO6wIhAJob6MVSzNPEbvFOkza1XDGyfiPdDZxOL0pgW7DNB5in"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.0-1.9-fixes.20200303005416_1583196996556_0.5334716608060315"},"_hasShrinkwrap":false},"2.0.0-master.20200303103640":{"name":"@atomist/sdm","version":"2.0.0-master.20200303103640","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"1f2606e45ee162583b63d1498e0f97390140888d","_id":"@atomist/sdm@2.0.0-master.20200303103640","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-xxBky5H5fdc2mb7GcSrPIN47ITL0aj1HqvBRHhv2Dy2tdBOc9egWs4D25EFwEdM+G9M+0oGu2pwqrpt6IXlezQ==","shasum":"62fb039412519f1b4018638512ec6895ccebcc6d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200303103640.tgz","fileCount":2064,"unpackedSize":8781693,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXjR3CRA9TVsSAnZWagAAlYIP/RaUK6Oi/aYx8zoelOsH\nAHxxhwZxfj6ujjsSVEHhpy6cPjG+V7kuT+SeQNN9d5AOIXfxFf11JNoct4DG\nfgxqTq/K2rIBVxcNFMFhsu1i8OD2IWa1kj9rvvEg5FED7dBilz6w0ukXcMQo\nHdDbXO4bRffmbyW6CBbU9BTClWPB+apq+aTtvH+EasAuqObr81ooyYskRMT5\nTherXz9aWTw5l0pn0zRmiigTLJPJnkEvbzAyPNLt6sxrphlklG+f4YKZIvJ+\nMCFzCePfAxcYJRJbhiOyz5j2UQZl5jvgxOUOgrT7QJzCRa9zc5CW36winTo1\nDYxnSmZ6W/wSqjWh2V3xTF36cPRsqU12Dxtbb5gkXKeGwx2Kh0Hxj83MFXxC\npv6b6J10IeltyM2eBLblsKJOgi0Gd0sj51Su/qw+PiboFhbhK3V80Dv6JZSh\n/d+DyjLhPabAYVtBrbFzPQNqQ00shO13591Yq3dskU6L+8GipWxkza+8HEA2\nM6h3kxwuy/kRznIkObfhBuqfnuWr7wIxmgmoqwyKtjRht6TrMak0Bv2gNvgE\nPxTBit6rhKd4SlKgYlahD9LNCSJo46Wy4RBOdId9Pb4JMlELmpqoUSR1Z/sS\n7PLpqbAUSM4uGtcGtVwsq6BA37OsxilExMVwZ0axfiEfSTiFcpuU0L5qDHxI\nq0EJ\r\n=ZYRP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCYQPyOrjknOmDKsgWZNH2VUhd+TZkFKUpaTYn4t1W8OAIgJMmOeSmDSXKDd4f6N4AgPMmFEKtNzUDdrCsGSQwS4mU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200303103640_1583232118352_0.6836219798007943"},"_hasShrinkwrap":false},"1.9.0-1.9-fixes.20200303141649":{"name":"@atomist/sdm","version":"1.9.0-1.9-fixes.20200303141649","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fc62ac76c4eea1a42a642a5d7e9404a23df38459","_id":"@atomist/sdm@1.9.0-1.9-fixes.20200303141649","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-+yVLTLzqfKa4eauEx/k2l0P0RfVtDixl7qvuwX8x7bz1GN22KcSGkTxtR7qeomBYcLHdB8i1nQBfa/CxxQzluQ==","shasum":"1a41809150b2d87e4f91f0352af97885972226a3","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.0-1.9-fixes.20200303141649.tgz","fileCount":1238,"unpackedSize":5794243,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXmdwCRA9TVsSAnZWagAA80wP/igbjAxGHFxpdTI8SIyu\nRWUI+oqVZGCR4tBmpnr3ZEyZHX/j/tJn7YqT/6SPEw+7tDLMLanoe8muiXum\nNQnyIIxPW/t7OjJHWwQRqfhUebJJC1b5Ue8CB0Iiec7hIWvnZd1ueBEb1lfG\nMteBdYULjS0/ovp2gAARO2jNeusDWsyPpG4w5BH1GroE00NCzg4+mndN+onw\n09+7puZ7sYjblvMsAqh5XuJxHTylzLrz+rv8vXtmaG3SZdqahq3sV4QQjpIv\nf3sz+1gW1CZWF9OvHXmf6/8v6jj4u5fytfgOLwfDPc/NOTUk8ZZDLOIFX02Z\nNzIu5pLaa6PuTh41TWb0V1jyvBiCoHrpRbLuLKMASDJC4aSqAIkFQvmTnedx\nEbIxPs1ELFU+zL3HidJXIwt4Y6vsnK5Jco/oD3a06YRdYoN1DrMmfHOPQj2l\n6FXgqHS9r8cMjwLf4QG4d/rJOdVQHXk4MLalUuZh4aj1uw9S5tYOPcP2bbvN\nxXeP9af3jN+1JcijyAIZBgccd0VFO8GeVWkj36bF0cf/CQovRLHiXo1UXm3Y\nIdhI+hITZIkVJHZbf31YKmBr70Ks5scr0+SBePEIJt2QpJsJWV1mLxLW1/XD\nCwRK7nWmP7LSAHnV8JmR52hGcfKkN+sX1uTaFOhQ6AiazdQtxnTwfe80RuCR\nf1JL\r\n=1BR4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCVOVpHl12PJvJ5jlufokUPYc1AZImW8Cum4UEec7DaMwIgB5YYF89YSW1ZmVxEn3goMaCCxrS/Se8tufBO9rpbZAI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.0-1.9-fixes.20200303141649_1583245167947_0.9039015718829073"},"_hasShrinkwrap":false},"1.9.0":{"name":"@atomist/sdm","version":"1.9.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"_id":"@atomist/sdm@1.9.0","_nodeVersion":"13.2.0","_npmVersion":"6.13.1","dist":{"integrity":"sha512-lqy+NlStZp6SjrjU/t1wI8DfWLv5ZAcaYzoFLedZcGFsYVelduQjciP+RuYQKu3UvBgWFenM6loB5KtX0QmysQ==","shasum":"2376c0af3752047c04dd67b464ab1d5828421826","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.0.tgz","fileCount":1238,"unpackedSize":5794218,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXmqmCRA9TVsSAnZWagAAot4P/2hy0R6DrGNUKM6W7yra\nLgX4YYuaaIq1GY1vEvAYzHLZgi35JsLBmM6ec2tzOpQVN1sfLuk0zSULf9PL\nDPIDQ17KLhn/NYLFeVDs6MATjB8Bb+NagBGy9wX1JDaMMuh1B+hP6o7us4uC\na5rD5dgQKPehxqsLahrcLzkseSD0pqsLcJ2yzmAXPWhBQRgJoAemYvWxA9jn\niInvSHRx+WL3//6K/txUAS2+NzBbrIzyzH9KpM5VmnjOvqnOPz9YV5jdgtWp\nxAuLBAK8wa8PemGBOvXtiXCr22+YL4cP+SCVC0eQvhgc2D7rqxfP0Rq+5H7L\nGMkeAYNpUZzW6XmPkFqtV8a3aEp8eybaT09BBCmcW8SiqBMupesHfb2M3t4c\nq2Ad49W/klFoziQhgIyjcTf4ij0HrHrdpS0RcelUTEZ+PWCnxSMxbsAaCWBl\n3Toy3dUQvcYbeTBbk2PZU+MQDjJKLQmCwcwIYTsUxcxDxmcJhbg9huNEFD0h\n7InFSzBQmHcAwz+bM2ycxuAEP/utsW1NBrPnPvte3s1GUua3X1bXmgZ+gXSS\n6b+0wRERfaAE3gGLZ3v6HEAe1Cq+4Xwe4Az535Y6NsGXIZCryVTFX1N3B5UW\nGoPk/9ZWfpwDUwQ8Mj+Y4oQKw8bahWysVMNGhDFfJf0vicMG4qwU0EwIge0r\nVrbJ\r\n=U8WQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGE9DfMX6WqOyjT4VE33fPNO4+8y/Y+BGJFEp5aUsPkyAiEAledvShlTSymKy65lH0+ZtXAbCMyG2W18tu0N2+0izlI="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.0_1583245990231_0.02864711944122278"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20200303143711":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20200303143711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f81aec5307adda628b1e6b536c2bac437fdd09d6","_id":"@atomist/sdm@1.9.1-1.9-fixes.20200303143711","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-2S+BgWTmTzS9dnyvJ90qC4Xa7r443Ry0CwwPtwhqq8yUSJQHZBcn5df1z35G7kvhP+UcyU+TNgiGyIxCUIIMHA==","shasum":"ee880b98d4556f05ca42d341ffd81ae8c68e1728","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20200303143711.tgz","fileCount":1238,"unpackedSize":5794322,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeXmwyCRA9TVsSAnZWagAAiL0QAIXQN+KVG2So5JWFRSgm\nDcnGbqSQaQnvE9XjIgLhZancTSJYVLN+fSh+E3OW0nbmayW43Dr5eUZXjJ/7\nY3rfVq1ZboLNLzgLTFVar6fDxvIHi1NzAFovU1bafmgHTCmJg3CqRe983rPj\naetQ/LnQQddjTLnoIVw5rAhz8gErh4zBixqjUthtHutqxaBassmx3gmLnu73\nkmxouHPiCEY8a5AUJ65ntUfWVdkLvhc6CGOLV0xlJd12zLD4a1vioVWQhYHA\nCHkRYA0/Du/2Bf6AoEeqaIPOnYJJdfXK7+bnZklNYJavwB5s0+8GwP8z3FCq\nEj5kvFYPeUiu0XdFBxp/PNqla6DEgTuObLvDFRcMgnwZyJR39AfLDyhvezhP\nBMCF79i1yA7pIEvpE3OfjTHvZ2ttYROPHEjs0cFAsvICQuJEaFz9JWwSALDa\nTy2aSx+X93D6OLURVTEon2nsoE0Gz/tDnbQ4JzD6m2bqZi/YBWGUj27ikGUM\nRH8WWbyxCmQuZsU0TvFLdD3HQkR2AzCm2/BAw4Rr7qZx+xuaoNzjW6uKb20y\nM0xcJZZZARGpppqSzxGY2hJvxNq2fER9siFPF4+BgdQLa3mpTJDYgpyIvSPQ\nRr8x/OJiF7ZxGfHqjRnH69svtzLnatjcSio818DfJduWykv3UJMXV+In1TEK\nTIG/\r\n=AyR7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDC5IgGWBggQah4j2Oo/3QvhUavdNkoRIOe/CkFBnoxhAIgKqHrkKIzMDRdKmUemjI7eYE74k6tMbxNigxbrKBmaqs="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20200303143711_1583246385466_0.6905761342745684"},"_hasShrinkwrap":false},"2.0.0-k8s-patch-strategy-819.20200304133717":{"name":"@atomist/sdm","version":"2.0.0-k8s-patch-strategy-819.20200304133717","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5280706973c9878fb40c494a15bf8aa941cbdff8","_id":"@atomist/sdm@2.0.0-k8s-patch-strategy-819.20200304133717","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-9OlLlbt3fhAqhhH/NAgZjf4Uy4hJBY5YVctiwvBdyURH/mXYLOvGbVSVwm5jUs8w2xzzTSKKZsk07aNFWjlDrw==","shasum":"665313aa71f1d731b7fe3abe8f9f5e38eb10f4ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-patch-strategy-819.20200304133717.tgz","fileCount":2064,"unpackedSize":8778013,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeX7AGCRA9TVsSAnZWagAAR7EP/32YKYH+keoZVWfvuemv\nWPHIIGwa/SyUq/xJxrYJabNNG1nNglE+KhfIupuExRxmNC/AnLtBkX6WTBYT\n+N3p0U3fLqJp/3tlfVDqztZxQRHPOAPyDkFiMrHHKZHEWf/iMcva4V4g4IL2\n77E0o59OOmclyFd5PJYd7x2NVzq5EYe85ywHJ4ey1yAMo2Vkkis/8I/Kw8bT\nNCZU4MvADS99GRuz4a7R2RQ/UParYtpdgIpid7RxDeLL32wRezVEzZInqXsB\nfi0fz3MNy7fegoGF4cs0X4kdezE/qL9CO5orn+Ref+B8TpGtK8uLgkTMLWUw\nRGSTrA0+M7T6Zd3wuVG9SiFfbdo5VKDTNnQgyaQiuanHk/WuqQGAPan1Gmyn\nWccYte/YMZXb0ygmQWtUuKO+HKZ1zPtFu3oYgd6q4J58e/NylJcoRN4fr6/z\nQXbDw79+WAongKl1dDRzVKlKqIfwfrpeKsA5JVkHnYEHA9XiZUIbe2bi8qyM\nbrJKsSwK2fNtM9S65dRUvB8Zy/sM6J4iLjNmDNJsM+yO+HNQ5DLwCtZhbKWa\nG+BN9WwaR6C2pSsccKZHtSR2wjKK+7A3sXgF/0PdfUfdHPalmi//9OZX5oy/\nzxXqtHalp7pMwZ33srkMVOpYRFvbceZXgd8YGcKt/+eeWpXRfew/0lesGtib\nyUoi\r\n=Q7fG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICMvzoyr28PN2G18+nOHmkA8f9lo01N4ib8ANCE1bMR+AiBLewGjwwNPW0Gmr9fmwgctoyJnClLdHIDZEazzK5b3Dg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-patch-strategy-819.20200304133717_1583329285741_0.6062448021549789"},"_hasShrinkwrap":false},"2.0.0-master.20200306162936":{"name":"@atomist/sdm","version":"2.0.0-master.20200306162936","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"973b0e091134ee9f85075c1982d8da0a9d48eb9c","_id":"@atomist/sdm@2.0.0-master.20200306162936","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tKLMm4qLzzoRmCv4XXk0rtqYXpFt7N/YvZefKi4zsza8GOjxWzKDtIUURDahC0FV/ihZGhRrO8KI+VftgUXv2Q==","shasum":"844b23d32b1745f3370989a3a22411a23b90bf21","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200306162936.tgz","fileCount":2064,"unpackedSize":8778106,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYnvPCRA9TVsSAnZWagAAIG4P/iH44RtDDBfZb5b/5t2t\nJXEnlthev7ayCQPi9PE8K6+XoQHhQo533qHxbYg1CG3iVRer0pSN9ZW8oiMI\nn+O1JlH9gysQ2EpJyQAjVSDHH4jGQQpvjMD8kUGNV2Re9rxROtix8WJYw5S1\n8GVoLOMGvO/y/oZ+liPlYI3NtvpFtjh+46t3tIitAmyE1rL4oa8aaEJHoj4H\nwcbOLnvBErL1GzczaIoco3/ViKJsxSriu7iZAODQ3NoSHgLLR1tWKmtjJfaE\nd7iXDwdyPITCUaVyIVlACvgJKJNq3f153P0Kn7JWJTK+aWA18GJIgTdbNPYN\ny/k7lSibWD/rkc2wtuA5FBvnIL0uXcRAy2HzkzjuyqEOFEDLGP6RgqwC0Qlm\nELcffTIc0GXKF2F+qB0WPWJ3aCo5ADqDVOzwYgAGQaictgCiZt8V02JLVyf/\n19QxG1liJ9NjqiN4aC8sbctcpdsJweJorpIbT1Z8ne22xIunSP4K5/a28Ac8\nIe4u4w3WaayHF9bZWrkaNjRj/kS7gYMgBYAiB7DvReS3g8oFd1uZUXRDdOg8\nddkhWokUynajhOt6s2tDObKFuLE3+5uIqkU8pgMeksczPXklFCXi3Yw3nQbh\nsT87ZPsWCBFKtuKseg314WYqz/+Ii1Sx6Lprgl9LnWHFnyb9B4+Jpw5COzzD\nuzvL\r\n=iI0I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/dL59c79W6VtvlU3HqFV6ax+XSvKzw2PJDMTkLrcUMgIgEPIWLM40zHLlYthKAkTfJCEUuVHgJ5NM4/WHhrmMVas="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200306162936_1583512526964_0.7293317129465926"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20200310183143":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20200310183143","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"cc39f51057adf5101ca4816395d397bd3cf61c63","_id":"@atomist/sdm@1.9.1-1.9-fixes.20200310183143","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-7bCRHbxNdrYeIAMDCVMEXztT7+/whtqU+q9DKAD93l5c+dID8dOnNz5Jnnw9E9J3K0lbx2xEJdM6SWN8j1zWpQ==","shasum":"0ec4592913fa44e04cc2a3076ead63a9968e3d4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20200310183143.tgz","fileCount":1238,"unpackedSize":5795453,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZ92eCRA9TVsSAnZWagAApi8P/jLZFzPAhRdK6e0tEedq\nVjRK08+pLOJ5MGTz3QZy9pqrobSMDHvRlEn6H3KwcsFS60Ro7Jr+h5QTE396\nDh8fVouzQY6ysAMtdW+QYN7nnamQ5dlFjtE+7e907RmtCKrui/3FIJD3cSWV\nDoCWtdbxR2Tq5IGAvqQpP+H/jJWvvinT9IzkfpZwOVxfNYMf6D6ZFmgEZLXt\nfXfPcYKrw7hhzEZu0EqFqz39hvP10D75J/2C30vfnhPnLp52Fh9xpnDQFRlo\nvHX3BhNaTN5wO11uaNWYxNqfd3MZ+PpUh8uc0BWAGI8tq6zUtXJj77bW7QYf\nXsiQNtAGdiOxIZuh5HWXzHU/OwhuvvSprj18yEhFiHyNZ9NzWVJdhUEpx4Bp\nm3VD1GR7veCw76zaILzoQxxHVsY1dFMKbkrEl1KtR4Oy0geV+dWDnOauwFlQ\nTsO7pJOArmx2yjEmza7BoXTGyitjDH7/WMndnOp35jG6/u5FWZ3pPcHJXHKt\nPcCcTqyyxV0yjgY8Ds2jc96a0mZG/Z30OnNJfTakZu4/k81TMcp/T8CnWJ4n\nz5mNcbGbWXBoaINlMGfMcO5JyjRCH5/sPpmw/fGyHlHqv8MrG/V3bFelQKd/\nfSq2M+X7aSi3qvCRHVHjNYFe/MO3VxsB5Mm4ExjvICOklE54/Mdc8qGp97sz\njuOY\r\n=x9eJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD48X9q03OOwr21rfX9TuTMuqU2zerKZnvGtisqtqAGZgIgVl+JUKVl16JocnL245lhkgF3/HdquwLCMeqJ3S+hARY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20200310183143_1583865245902_0.5191731794810348"},"_hasShrinkwrap":false},"2.0.0-side-effect-defaults.20200310184749":{"name":"@atomist/sdm","version":"2.0.0-side-effect-defaults.20200310184749","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"5496c224b24ad36ded9b04e77c31079815d05cec","_id":"@atomist/sdm@2.0.0-side-effect-defaults.20200310184749","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-3flCqx15ILeDkpBJ8AaxCW1q15SPDlh81KlZNNcLepBqcHCNne9cJYGTHTRPmgn46afWirgRLSw7i5X1MT4WIw==","shasum":"c50dc5898fda1f82b15957e617c53f8e2a53ebeb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-side-effect-defaults.20200310184749.tgz","fileCount":2064,"unpackedSize":8779198,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZ+HOCRA9TVsSAnZWagAAJhYP/1ipKJ4F8koy106rimGR\nKhzf04jdZlJzDKrsbgRu8uWBHnbfm54H7Dz0GUtSjJklU4YYZVnpsBPmTaFD\nlrvV9IiltgVt/15PW19Srlg2+NiLBvFK4rLOgUKsvkpXRP4dQmMk2KvyX0RT\ndZapA6Ner7nBKN6WIAz0wpncuFS9d8Bwt1lrGOol4MgD7ZDoXmOyqgW6cvPN\nbkmLm0eC0idUCMgDqP2F6ZYhRtzie0FMAvYL3LGGM+OcqDnWd735dssmrtdE\nhlZOR4tJOt4XrtHB4bchT8sAg9YmT5MyCKtzql7zOjTUkqYzmBIWZQP2+SZS\nVBsFhDO8jwEFad+QRQKb2BTcETqu+1WgIr4mOCUczVN/6yYNjfKGWnnDR3v0\n3YjAVavWqDW9yTMXUb9XpC/jDYUGEjdxU4vQCsgWuILwcpcxpzav7TbwfLaQ\nF52NGhVt/LviGZeCSg+F9LbSKslznKjGRIX9/P9zdoNmsj+3iC3gWIZsH7h1\nEYeMrXmiZP6iKz9XpBZ22nUBpqkrWYoQmWLTEgDJyOj7+wMdSlBVoEAYN2vY\ntEfiRnKV9HE2MNaMy/EIt1sopKfeZwkGCB3wA3TEEhKaWVC4HHymCkdE4Oto\ne2HvLFonWd/c8ZqKoK182nkH4Chs4IKj1+YXPzrMJAIYNzUsVCFKMtnd3Doe\n3SHZ\r\n=McTB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAob7nj9fZW2NWneoL74Bf491CAKoUPsn4sM1ccHWux5AiEAvHEkfHZgLPBfts19saQH2laYyXoFXD+tK/yYORMeI+s="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-side-effect-defaults.20200310184749_1583866317943_0.0055131652310898005"},"_hasShrinkwrap":false},"2.0.0-master.20200310192307":{"name":"@atomist/sdm","version":"2.0.0-master.20200310192307","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200131202342","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"abcbe82e492cdbb264866a21ad3b7f35afab92a9","_id":"@atomist/sdm@2.0.0-master.20200310192307","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-iZ/GBNUZXMfVPwWhwUoxtwa1BjeTjEsLKSgeHt1lrsQQjWvBaHm5HoVyTCbNzQ758/vg6jS/soKhym5+gnFOfw==","shasum":"1ce53b3288f74409ac5703ba48d9e8e67908243e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200310192307.tgz","fileCount":2064,"unpackedSize":8779291,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZ+pDCRA9TVsSAnZWagAAI0gQAIkv06Z5ECTi1ZOrj9V4\n3nSueMflqQXzTJ9fzjuAoVzsSAcED9ksfrYADziibQROx3PVgLGuv9rYP31/\ngYn2xHgp+1FPGkuivtRRrQGt7xNTSYSJizmfcxsMGVHiBtl1eK1nJ7cQBfRs\nBFvvUKVmbkO4t40cKqpIex1WTDs4KfXEzTWuvLW8cDFwEGoCyRvJQFDy6QbC\n5PAun6DbwGBFq3dVksN4IeyFgCLUFi03HKX30pA7u4KiwMFBgmhl62HjZ5Qd\nemQKq0R7oJxhjd/E+FQXfsRBcqaAYodM1xAr0qG/Q806Tw4hayJZq9K5k8jO\nG06BHLV/XLnL0DsE2t7NavQXuYKsoL3yk3ZyhccJZmA2/tXh52I7rCWwQpVU\nv6LV6MWH/WTjO8JiC1m2GB6QpDtGFyqJYkE31vjDCZC800tqZVwdae4HjBi3\nhsmSgOsWYHohJvKZCnkyALcp0At+Hv/xAmzexjdu/PNUpSBRXPnBxzM/eraZ\nCjUQ3/55WbQ8gJ6SNUwrHC35LB/OXcVQ55EEi20zskz+NZeyD6CKwv96yiix\nY1TuhtKEd6ipxxEvx8nuz5k30zbnsbp8L3ewOoN2eN++sMzhQ+/ZMvZa0tUp\nP0W4M2p9g0enMvV8NWHuIIUr8wVQ6Vp7L0sykm/efW19AioRoFskSJoQnOk1\nWc3c\r\n=Q5CZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC7b4dMzprICOeueja4SXt/zdH4UZyHGR91Q4+Bs0O9eAIgAZnmfip+CP8teLJ9+yDr1ZcRX9iJpnsMYQ+dN+NbLTk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200310192307_1583868482898_0.8878822972736509"},"_hasShrinkwrap":false},"2.0.0-master.20200311001438":{"name":"@atomist/sdm","version":"2.0.0-master.20200311001438","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200310235858","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6ce8ef5923667780d759e2dc254c22eb9e0f069a","_id":"@atomist/sdm@2.0.0-master.20200311001438","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-tF+iAUtfQvthF7i7VD+SCCcpiboWW8Ct8JnzknNX4Pt5YE7MtV9Kin7BNb4ThYXyIYhxUodC2xV8f8RBqzP/Bg==","shasum":"b711f8b28608f6baaff77ddc76b80be4a188311d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200311001438.tgz","fileCount":2064,"unpackedSize":8779291,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaC5rCRA9TVsSAnZWagAA2k0P/j3ToS4roJwlfQSacjft\nsSy8XHqrFPKvjB9zx2wD00MOBsqnLjAYxmBtw1VVrujXyLB5oktTF/aV+xcq\nzH1YRcQ+C+4bxQpKznRSs9k9MLbVinuM1NxVo+q0cd4NxBl7bF/UZgCPztPl\nYO7ImG4vkHQy18Dzv+jczxoZx0fzIWcs732GC7ZQ4wxHxwhII/B70N6qvDlX\nKCaq1C/+7yJbhQLiShDiCTaJLPhDMAdI15zXZ9Zr0E0SV9w82mrz3tg5/S7n\nZ+e4PPNOQ3kVSpCZGRhyzgmTV2p+pInTfVrsnOinW0z/wKPSFHRHssf2cvC0\nfBbcucF0mww7fXOV0BJM/PNjgh01odj/juR1r7kOPxw/gZKykA8Mj948hnGX\n9my8HEBpDEh+DLj0+RUgZQV+WAxJXK1H+wGHmEVRpN6CDaVXT3Lzcn1BmZc3\n1Dopvrs9I4tRKZ/SehfTdVihLv26+uHGpA7pQP0pOIOd0F3ej3jF9KccLOLO\nul/GnRYPhgPafMlj0SZaluKho4PAMtdf3kXeLtG3Gtz3ZshEAJx4abn+Dooq\nCXxk0pu2jURUrTUbfrZkm4RpB4pwrCuGlzUa72J+DLneuHoJnRSTju9sPh0r\nhCZixAZazHHEjqskGybIXJThuq69Byh6w+LKDPowm1/3G9watXWYoOzuN3qB\nJSoh\r\n=rxYC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCR/aUnH4rhiPKiCQWg5pa4ZSsW16UQ6vy27s3rvPhYMAIgSlGYY40P6zDcKWJvB2ih2gxXHjZ/Qh1J8I+/z0SjN4Y="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200311001438_1583885930939_0.8835268081968086"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20200311125136":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20200311125136","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"^1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7f18ef866b03cb18336d23dfb54b0e297cdbb99e","_id":"@atomist/sdm@1.9.1-1.9-fixes.20200311125136","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-zw1ftQIG7fU42b6x+LhENxRsQuOId63rh5Hi8Po3Sb7Is0LFszOp2cbpiJz6iZ/O7K6KQOq8mxI8WCIy3jpLcg==","shasum":"c35f90fbace1f4760a501569dd1d7f1fad764935","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20200311125136.tgz","fileCount":1238,"unpackedSize":5795478,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaN9qCRA9TVsSAnZWagAAfTIP/00dRFKpTb06gMls9ReP\nY+zkWl3Lv48iPWF+u2SsOfxSY394C1TZTwLgfD70T0gtlyJe47rPPDpDHD2c\nzdlnMyaNTUQKRCK5IRTsJyGIhVFqffkEi+jaQDGbT8LA7ZmyfTSReh3yT5qr\n4hE6MKrI06qVfsHmQ7ZSQGu3c0tP8jLL5EUaSQgHQO2rdAdgOHYmchl9pQn5\n3H2yqhc1YvPFn3EPSYcIVLgiB2iiurkQ1JCmR9mZLgAkRPwTlVJLM+6XhFBn\nRuB/n3D63kMm6q6MP0u28z7bTSUjyFQI2McUsXd60BDXC6GuIO77gCPyuFBL\nC6a+UXEd9idYScYZWAsnnOWTupQubuH2pEEOLB62kTjHXIE3trLwTXsG8Cec\nwVL9uKf4JCKMOpxn/gacMWQ/l/kQBRpVWSkzN92aDKVZGoCTiQHGIBopM0Pi\nbQzxmDXLEuP93PM6y6TdXMH3hDmL80vZKHBopdtjOgY0JsZx8TZ6vNOpJwKh\nvedk++7GG6dc18AA3m/po87AYA73mfyzm+1NLW59YDS37a0dGkopBW3TOu55\nz6VokV4JyYBIf4Ciy4PsTbBbsahzgUgYoBzrfpijU4qosmnHNn8BGNsznZan\n9YbRd+Pp01/mMOidd7U0IqR/sqgEyIokkaiYIEhzLNTlJJXcUzr4VGC+mJYJ\nVoHC\r\n=6Cwf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTPPrY6i4loCR3Wt3WBF9K8R0/vmnWnR04tSFtL1RMWwIhAN9iz7AgwxM5p1vEiPh8RSSmax1+Oe7vG3f0osDhznkQ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20200311125136_1583931241893_0.676116745969696"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20200311134607":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20200311134607","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c90ea6375787238fff74794a937f7214a3b59135","_id":"@atomist/sdm@1.9.1-1.9-fixes.20200311134607","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-hc1n8EAeJBVO1P9cna2AMOwCCn9GFHElR7bibUGJyHFteWixVCabquNFrP7LGEm0+3RpOGwuQd+KOHBk4me5sw==","shasum":"3db01924ef5f1a0850391536c08c2fb81cb05af7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20200311134607.tgz","fileCount":1238,"unpackedSize":5795477,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaOwwCRA9TVsSAnZWagAAggQP/ROTsMuRSYg12q4kZcM2\ngzL3f9aP6B/Y5fhhkaZlQDwsL0GSB9V6CLvQhYxHIY7yj4ZzUPdGXh5W7ygV\n2MKQpQyVE/oB70aElf15GcGuJbWbch8m3pSk1zlNuFZB4hPJ1MI4V49qhj8B\nZardc+F0CseMjsT+bFD8u6TZr9I2ZtXIoFvxp9s2jX/G62rPZGERfGLNT0HU\n5nxrG2GMNF58kTs02SVoce6EcME0KXnldkIUFfOdJRGf4bMm6EgxfR6p8Iiq\ngKFXpwxs3n+QO8XM1QAkAFPU4CMmmEwv6oqKhvlt2FTEhV3ykxrImKGnHb9D\nGYr/LCVgTw6caGxSr6uY29zvj4NKpUDJccsF6e0Ywk144MBKNCdFKOn+ze1e\nt12fEw7+F7/MOMLY9Q96j1J9vfxFddiDfUhxZ+0+Lt3zhm65x/e1IY/tzeyV\nwzWJxEQVbeGFBkvQFvsx2nJRKSEvXKmDhacbmk5fY1PPt+h7coH2m2H98gp9\nQgCcIvFvUmWAwZGAgAssfAExTxTR3jwUKhM/dp604a06QQcA4CApSoQsnhQD\nqprP8wBUf6n6kLvryS1ipcy4jntlBWhsRjsiNWpcG9SnOzg5b0wWqByiX0e/\nOBdrs9VDvW8Cm2aEh2lP5QUafVzMLBLQQkX5kTVpU9GqtgAWMrZBVXAcn6ls\nYAKp\r\n=uZ4u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDea4MsTObfTRhnhLAZfbJuvx7d5AfjHND6PogKMmUzEQIhAJiwTKkxGOP6lYZuCjTwNZgJYcxbR7QtWFhro0R3dRR1"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20200311134607_1583934512179_0.07800387173933454"},"_hasShrinkwrap":false},"2.0.0-master.20200311172920":{"name":"@atomist/sdm","version":"2.0.0-master.20200311172920","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"efa6f2e9e8f6b146c0a037e475471d21c43d1dcd","_id":"@atomist/sdm@2.0.0-master.20200311172920","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-mG70d5upYkNEiaHlVLVxlA5D9NzMuIc76V7ugivti8vkP49QUpTn3H4TqlZLPCke5xQlymLRbW5/6/F/ExGQgw==","shasum":"a119327e7e0c5948ac825eaa78a1c62a45bb05fb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200311172920.tgz","fileCount":2064,"unpackedSize":8779874,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeaSCZCRA9TVsSAnZWagAApc8P/1ZI3UQDCNOoGgIn7nVb\n+0ouN8NLJapG9kVJLG0iicJ9rOTMo14rvOOPDrtBWcbz1MvlZGeatCyBjvG6\nJ4n08zFsnhe+549FYni8x/g83NW2t01qdvabKipyijLhjhzqnsLEZb0Hyg2S\noDjYFC4XGTQQL1jSFpK5QcYqr5fClXPzn/VBVBZoQlglQwU7uhx8LgRsFbzK\nImlbXe7fB/dCmDGiHNHEWe8xGlaHUSGPlWioQ93a7LJ/ECrPQ54YtkP5mmUu\noi7o0PIP5SlZTHbK/UhOFKJ1s7mIEuVZm/j3onEn9JK69k61OQ3xDSS/gzFo\n6nfOpuptDrtXxVFG7QCl8xvwj8zHWHQ612NVv300us8y9kUM4OkF1JBEXkeL\n6aBExAsjKX1GLrtq5J76ZzSPriXj5dMJqO3IGmjIlbzpWyFq54jFS8lC9sQu\nFmES+GToC2xMxLVxQjfMZt13TOucT1dQLEwFRBvmI1ZCGn59Sm3gnBx1BG6P\nJrLl08nwGMCqbmsit4g+cTQ0PeAj9aihcFborXYC0jKgF9jCXq0I4ybfKJRo\n28wDlQiJsMeyP/xYXnwN63K1Qtm/Shr5gYIGgJq6ZQfx1H2bViJfMme1B09D\n9UK2xtOEp99MEWS4yIA6vk3bgFjyJyX33RBMddhNpfRMb7M2FH+3pl3TsL0o\nfaaS\r\n=cmXi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIH7rBOw6oeBpaq+/o2Ui++KM4jjZA2tz5TDQdvjjL37qAiAPrfaNMqfUd+1ivrSYztCI4HkeBib1kk8D/To+Cx1+Iw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200311172920_1583947928981_0.14680255177888846"},"_hasShrinkwrap":false},"2.0.0-sync-repo-github-app.20200312133402":{"name":"@atomist/sdm","version":"2.0.0-sync-repo-github-app.20200312133402","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7f207c0dd62aff8bbafdccb607bd68282979312e","_id":"@atomist/sdm@2.0.0-sync-repo-github-app.20200312133402","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-4cyq+nE3ju8Rr0Gz4iLR7KzdHCmToJelVsQeTvKPa46vUeSkcPgOEtnEeYF0wfZJfs422hRMKrRSjiGkEO2Z4Q==","shasum":"cf802d8654ab28c28586c0048a98a6e6117e888e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sync-repo-github-app.20200312133402.tgz","fileCount":2064,"unpackedSize":8782290,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeajrVCRA9TVsSAnZWagAAlswP/ja84Id/nj9LRtYwEYiE\nqNR+TPbMUm91Jtn9Tf1VVl41JxF4iOy7LrhGXrJu44JHIg8vtP05/U0YidbL\n37lZ1e7havoJOPYWbmFu95cr5/aTImg48w2Mv0vKu7f5z+1XRraU0Tz/Wc2n\no/7EZnx1S9FGXibsVcnDipOsKP/ut0h8XtD1y2svGxYfLyCyXE5bwAbQXEc5\nyX7UZh37VCabE5ecz4R+cuh6ApbcZt9C0zXaNjcUciWTkcBaRaWKCiWY+loz\nrZoBShrAAx1LvR+QIdc0sz4LgFxmsJmBHCZC8pFjjypikcw+RV+6JH7m7dxt\nEjxeK1z+49vOCjdYu0DNkEEltzMAGZDbCLKDmuTi5+g/oQSaWHvqzABpU437\nCoRJuhG6/fDc82QqcWVbdbOhtnQ4+qADb+vy2c0ZUPvJwmYdX6uGVupIQjcb\nLkXUgCjhjZqEFvjjPHLtdZTlbtHH6ls5OLPbaaUeZjI3+/rXvKAeZCWwnLMR\nGJMyKqVkPyg7Py7fNuZTY+ICoy11CXSltMACGBjIRuVrBiCC9a1eUelYlUCU\nX0xaITINcwl5L3vq7v5rRwgMDN5ixRTE2yWb7/SxZVvYW025YjnjinQyWdKW\n8QrTBZOB/aXySKes3pTt2J0CS+xb5D0D0hEpl9qHLF0xjxB2NGOslbkuo6kS\nPRtm\r\n=iHIB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICnWuf0FILGXtynZEQVJab0sHMVwJkEZp9UGAz/uy+F3AiBoyWs7icdMuD828AttCanbLpizhuytgwhUnXoBR+kZXA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sync-repo-github-app.20200312133402_1584020180794_0.1031599627745805"},"_hasShrinkwrap":false},"2.0.0-sync-repo-github-app.20200312140329":{"name":"@atomist/sdm","version":"2.0.0-sync-repo-github-app.20200312140329","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"9ffdb39e1d96059bb66d41e6b4f802bbd666fdce","_id":"@atomist/sdm@2.0.0-sync-repo-github-app.20200312140329","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-6zlRDSeCAv3/vz41nVBlnbs/xMsXwmMJKn4RJSNnnY6G9lGIgr4p74ezO4iupkXdzpSsEuDhpzvB3FwplY5+Fg==","shasum":"bd56a278f7415cb189bb10df1327147b856e287c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-sync-repo-github-app.20200312140329.tgz","fileCount":2064,"unpackedSize":8782290,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeakG7CRA9TVsSAnZWagAAqLkP/0fIL1LMKxWTpxOwLak8\nf9T7ceAlOKmB8j9xMh+kUKrRNb46yMPNhfVJpiOjo+R5ysVQ3fmir1EX6XDQ\nr31+aooeqSi26fA8W2Sfn4V9UWnWH2E0PwBuMbTDK0+Q53NzfGHfRY8rxjqy\nwLnMYu5SC2MudjUcTiPkQMW/Rhrq1uAzmoDLd7tqu2KRKDqwMm0WmhZZyS3k\nOYmWtqhJmib/IopWrAjP3Lr4UC9WwQCA28zmizvX0LxUt1eH7KxhqOe+zlJm\njiQSfHdIy8dcvsZbDz3KXFFhXXH752g2286Ft05I9CL5vPdsOuMCTAr2x0Gu\nVNpXiGjRro6qkFBhB2txa8eCvlgVqW8hS05neyO7hOwv/GhXXlTSgnBpArx1\nuTUye4nbmsTv3RhYci9sJc+bEC9DKYwjJZ0Xpx4GpwB6rvDGTagdDlN8RyFT\n/HKU1O4AExHwhWzqZ06o8nnncFbdjHkhA+LY0V7byCW8CpRHmGecnvYX40Dl\nDxLo1FUn6wquC8eovquQKyUeDS8SYBHccI5cjHnCbOKUht4fAIM0tERfnVrO\nnWJP4XxVafS6LJ/iRtkfTbzbvqsK+9wo3mH3Nzqfey4ID59SjoU3BN5Ez4SC\nassOMVecCZgKGbnIkG5eFQECFkh0zjE8F4vOr6Y/Hz4362P7OVrQ2ClGKe7h\neCog\r\n=MisZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDxNeT/cucZmuNC8lu1vnQ0xde6iqaxka1NIJRAyw3mGAiEAqRin+ADZgf4f6xRcDIlEUt9/XJkjsi9thx5o3P47unY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-sync-repo-github-app.20200312140329_1584021947168_0.03131295216325558"},"_hasShrinkwrap":false},"2.0.0-master.20200312140727":{"name":"@atomist/sdm","version":"2.0.0-master.20200312140727","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7141208724e6f90232377ff368574fb46abcd3fc","_id":"@atomist/sdm@2.0.0-master.20200312140727","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Yr3wWyCzcUeB2T3yqpFjEKblr8BIPkqjB66xtlG6UP5l/Pi96j5mjQA1zFdESvA9e0cCzJLO2wfQg6pAQ3HFgw==","shasum":"68918760aeecf0c4d86fb02e10133f0f6702cb63","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200312140727.tgz","fileCount":2064,"unpackedSize":8782373,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeakKzCRA9TVsSAnZWagAAqkwQAIKc212sylTWN9kSj8Ct\nCaYY7g/wTm/fPIpxvw53xX4Uc9ux4ZWwrVe397IdLWOtroVMF/XWESseWkAx\nC8/USd9KJ+NdVfvBBsF111JbrKcW7mybMl6H31fTXKSixQdLPpt69LkBgF9v\npurYFbe/8fOuPh8qQ5Mddcs/4UVzQmVtGyZOoykD8M6ATsIk/c4kxliUg/YA\nrGyUzLCKy7A/8pMv36oaDPrPKPeNqUPE900/geaNZxxTOs6MNtBqzXYA4lTq\nqKazPhcsDFxRnouTQJhCO7ShNZCrhBNHF9od9LXmsA29LQqqxdIKPQeaBw36\nJ2bnBleZd422cqPNLKb+iARIKNFP8oqU/BvXKIDXlC865r9B2i0i+UmcnFZz\nzarMi0cz6CGEG6gYX1OeOuImSAStd+XSuuIvHZOpuCQPPjWt63vWSukDEISj\num/t27s4jmCRe+hyA2d13jlGuPn6tHbgPfkJg9/u6S9R8TyEo1BpmiCMle+F\ns8lgqVu4/T+ZNk2qTz9V0EFhZPLeexmbwyN1cJnPhGYezymaKjTtd64f11de\nuCZ7lBA06TJZtampdXSluqmkkubmvIEdmVHEZQ5Fz9SoItqyE5Z3XNw3H/Rf\nIWocED4rsK01eYvlB0Sd9VXiqGwG2fW8c2gwDMBTwCCtfyXJrizprgY/NQXQ\nuK6r\r\n=eItA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDpcWYOXWpNXg+06Oa90GLKduP5kihU5zP1/68DjT+kvAiEAs7ZMeMfLVr2p3sMtLcIpotz96QNwTYT1rNYWd5KR79o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200312140727_1584022194512_0.629795260239469"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20200318141711":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20200318141711","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"54dd9f98a0163789193236b39799f9a79c4da511","_id":"@atomist/sdm@1.9.1-1.9-fixes.20200318141711","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-5BjUjcqZtPg5wTv1WOi2qVvxYOSN8xiyeNvLOCk8mf11ZiZj6c0MTAWVMt0WuT55vDd3Z0+9KH8S7so8sg67Qg==","shasum":"005144c6d8116c57811f1d5ec7ca0bc0bfe0c9db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20200318141711.tgz","fileCount":1238,"unpackedSize":5795158,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeci31CRA9TVsSAnZWagAAKZgP/iEgQgrgjq4V2Su2jvwR\n4wecSKMWJQLp/d6E7wOsimmH9wWVNh+drUZs4ZaisUFFu6P+ppKYIcw0QZ2O\nJBTzc83THiBYONVhMDUZRDJXxuTtOsRT/9sS9EVPuiCqTSRxqh4cCortF6Sf\nSnd/yydy9SsPGDEWtI+IJ0VesGq/6tYqWtAYF2uefgxKMm4SOREpSE75AhM7\nbQXGXvN4uZa8WOjoWoI9Jt1M9k70elrxlA+9bd/84IiQ8oyR7TO9vxtZsu63\nEbX9WoMWGqdjEoWiNMj5U9VjHJnPJqlv1c+GECoD+5WdR6GsCqGr2UgDo/g8\ns358Em8xiGTnZeeBVdC5i6OfdmfE87VJRKh+gP9oGXXW22QW0zgwWnm1pxYo\n6fRCc02pIiDqMvZ7Xz9RcMrWSrjTzHQi164Y2idUtDL0l9RYlvAbryao939i\noUkuXuaB6L3uv0mF0o2XfNdpG3plvIIWx0hLaFU0ft/fWqNABIhTQQ7ocwg6\nmqwM+gfYlUcY6Dxurd9pc6deFAoC0mI2/YOgyQ8iCj4S+sjvFLDTlBteMa+Q\nf3qky5yJVrnZVfZkcYC3ShwhX4bewKMY5hE6DfRhMPNQ5ToMTFQvkjJuU5ts\nosW7E+FowgAbEbq788OJcejRKM5BfevVs+jOmyF1+LSpD27UpJ/FSfmhG/ET\n9OCJ\r\n=POHC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHmdYDJ34aAtceajNDFkUK1b8ngu39Zk6Q7oPNKqwiFkAiAoMBrhjLlKzfUnc43H36nF+dl9jHVR5IP3ZL08XZ2Vtg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20200318141711_1584541173190_0.7231112063645204"},"_hasShrinkwrap":false},"2.0.0-master.20200323171527":{"name":"@atomist/sdm","version":"2.0.0-master.20200323171527","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"79482432ed45371f43d8b79e7a2e8fef54dd4d60","_id":"@atomist/sdm@2.0.0-master.20200323171527","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-DCqMlSUhqtgzrnGtVs4hN4h/D5BgV4dvEPAyF19k9qXAxYUxSfDHFFAzKdmRdNImv+agua0zHpDGofVrg8KdTg==","shasum":"117a273bc5db07dbcf468dbca4f5c51462e76e59","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200323171527.tgz","fileCount":2064,"unpackedSize":8782625,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeeO8/CRA9TVsSAnZWagAABHUP/RM1/EBpniMY93Oyu8JQ\nGvhI8JEs3oKVLr+2TOaJaGVuTWOqM3/QiEVQuspjxD5WLYbeOxYXLM1Zz1Fk\naA1XEQA6uwfDU90GwGT7Wws2LNOlC9DeCZMOVx5DCKAvuHTujUF8UWpwwlpi\ncUxU0EqVORdzgqpxhv0NCEL2uchkgYLw0ZxHYINERivrqQlok2T58BRbym58\nHpc5SyBU1N08cs+JDCz9kijLJIK41rxYd8CxyJMGbpejAcGsjjKXrdsiXXFq\ncKYK2ShHAHNezb7gBu8Y5ZCvrccywZ3hMEFd0llpJmrTRnjPZvM93lHIPgS9\nIDhYFrTUcRu6rWQ57VeUbOVxYhql/oFYOEPj+MtrC9fT3s1x1sXMzfqnkOjm\naOed+ofdzszBDn6c1SvC+kmckIiZqX26Nz3F88MJVpt9q4k+AOhR0xP5hFEO\n3SIhVjijSRcXvA74n7RBHZb1Y1Eub6+XThy3JQ0lLQUftoAqpdRBcvnGvL8w\nUqAH6S39fAjMsq/et8wtt3Z/3Pcc4hdCvIyrWjjtop9lNCNNsS4hD1JfC+GA\ne7Cq+2BVuldwea9ZENdIxMqZ8zXi4UiT88R99ZgnHW0Lo0JfOzVbmkbqznlW\niDLminbylRl7VZC0oeECShn9eRNN6SULDDlub9kuQ3hUDJzk9Fv1CCNRfjT9\nMCN+\r\n=/3Mw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDHy2eiLVg7N7xoYPjrlJNwAN50qRgClB55+3WYeE8mewIgDK3ha5EhGdzh7qg9h1j0Z9B9VIRJIVskCwSa3J5AO8E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200323171527_1584983870858_0.7027013082768923"},"_hasShrinkwrap":false},"2.0.0-master.20200324201747":{"name":"@atomist/sdm","version":"2.0.0-master.20200324201747","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a74ed3febdfc6b800d5d36c78f2a1150f994206b","_id":"@atomist/sdm@2.0.0-master.20200324201747","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-QRFiuMzEhCs3UZ2TgiihSfyJR1Vd1VePGT0C5Jj7abPXNixy/C5yFLOdnkuwhCgnyyTLwnqJU4KvAkqO+2Bw9g==","shasum":"033408f794de6edafc8806f2e9a5b7351ee4804d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200324201747.tgz","fileCount":2064,"unpackedSize":8782625,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeemwKCRA9TVsSAnZWagAAs5wP/2dGinxcgDMVc9UOfUBO\njVutiUV215j07mYs5nK1uQcScmLkePiBgtXR2Uoofs8rtyJTYgWkiHAMen+H\nNA2pZ7NwADvJM+L4kIFtoKQ2JP0/piHaW6aAKHU/O8lex9zGNH1SIldeSd4o\nyNIPpL9kKOndU8P4tS/SrUpx3QtyT2xl3VpIUqEkZ1gz2IWdMTjM/WdOgDg7\njqjNTl2M8Qd1DB9t2iZHiyfs0EH8d5FvDo8ToGFodS/BRDZ8VCW0YfBOtIUR\nUt1F73kXtTm2548e18yjkH/EXMx9h23mKXozUr9ipjPRHQmoZS7rsIfFr2Xo\nXBBi4mVg/BU0BzK7VReUj7HP+PF+d5SxrAgTEbsVTr9ygpF7thZF8Ys5hPLc\nUmK1PnLGPXsqUwsk72eevyQz/jYSzIWb0gDAElQaW+8qaox3JMRBpJ4ve4KD\nPa1NbVEbDii2Ehya/xH6UTCOaJdl7luIPREFqxMdfLMprXsJG2Iy8ItFGsNf\niSbmrTVAm8JNvZwMipGfR+GDF83Uvq1SMe4DHERNdRD+GRbmsrIRgNxCQgd6\nr+ja13gBuGjdzFoNgV4BZClGlZA+KU4zGnkq+EOuZKz59ONpeP2xA5V0YuvA\nE/ZwRJJJbCIYF6f2S6JaOBz+X+yIOG4A8GZyP7bzASzRTr0Y163iTXGYj4h9\nqtK7\r\n=CLQP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC/0PBuinNj9SknDiJ8M0EyUPzlsPqoS/XMx/mOZ+aJagIgD2jSElPE/swsUjBqS0jxEt8/Yj/pDWl/uwXyUyeg8j8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200324201747_1585081354086_0.09577669514592846"},"_hasShrinkwrap":false},"2.0.0-k8s-sync-repo-creds-828.20200324204549":{"name":"@atomist/sdm","version":"2.0.0-k8s-sync-repo-creds-828.20200324204549","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0dc9ea066b6ba2253adfd094a36bf364422ea491","_id":"@atomist/sdm@2.0.0-k8s-sync-repo-creds-828.20200324204549","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-bx0mHwb9UEAErgi8SEfnZ1cw3wM6SeZp25gEHjBsuFl9WjYIzXcBg+i8r56IcOEvA6aLQVrVmn/oNfgsdKClTQ==","shasum":"01a27cebd732b84515d81505509858bb79be87db","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-sync-repo-creds-828.20200324204549.tgz","fileCount":2064,"unpackedSize":8785345,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeenKoCRA9TVsSAnZWagAAa0EP/R4EQcElLj9Tt5UcLUJm\nLCLFRIo381yhmV0rvL5J/S+Hb08YtlhC6X9T3KAGW9btOg9wuLCPsJkiZxGX\n1rJOLWNGsErZd6zEQ2bUWDM0lSD/tOed5RLt0qydolmwTYyn2o+othunoofj\ntHgq7+lZrqD6CGvXXwBEza+rI+a4BBYXUmNKx+/oXs6je4qCVMGG8OTFAGBZ\nhr3B2sfR/xX6DGBI6l/AH99aVG5/wICPY45pG59D1m+QLsvYE0mLdJx3wRwI\nlPxSQO5JOtnulKcid5eWtny7QW6+fxAXRmdT99Z7IX1zsVJSlAmNTZcvoh85\nu3bb1IkKfMmLoETYOiWp2YeOfhPsA0vUCvLr3Y4/bDgwGc67I4qvfu0vGACx\nr9EKPx8xiEy1O9Nx5oT6pVDgpdPuz9A91zStb3RXaSfEjWLW3qSyrukXVhrs\neOqFd3oZIOh6NPPA01fjhwZZjcNHoEXN0f2P0AXR1nz+9kmYK+p2JMs2fqdM\n1nzs5KYf7oxp7Qc07RqCha4BT7EzRE0kTvr1FLnapx/2XQWy1n1l0Y33sYe7\nacDwBXp7XWX774u92URTbYHokRpj0NN6x8EMiYoExecJOY5rXHcmsgD42ciQ\nBynMy4uhXvuUZMbHK6KLWI/anPC/uk7pPRu/J7b8ymReCV3Ily5U+8ohLMHL\npxlp\r\n=1pdX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDTZlDDRW0p0+tVcR/TmcFTZFks0pG4JvfDnmo1kYSKuwIgTadSSOugeGUOFeXEZDb/k0Of2TF0IMbl6oR/6BfeD8I="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-sync-repo-creds-828.20200324204549_1585083048143_0.539323291722694"},"_hasShrinkwrap":false},"2.0.0-master.20200324211658":{"name":"@atomist/sdm","version":"2.0.0-master.20200324211658","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"514345806e1b258147df05f6d72abf5182faf1f0","_id":"@atomist/sdm@2.0.0-master.20200324211658","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-sr8ar05Y5pmrDE8CKBtbTCU80Ab2qIsZjZ8PvL5Olm2AsMJE1eYPV6OTvzSYiSV5zWgY4GRHVqGsHFav94lwtg==","shasum":"78c382cc9b3deea4eddcc40ea8fcd6c403778180","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200324211658.tgz","fileCount":2064,"unpackedSize":8785328,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeennPCRA9TVsSAnZWagAAIgYP/3BID2Yo6B/+y9Q/NZOQ\njZZD7TjiZ2TeIKtPjCzPhuPSuyRj3D9xy2/CgVAKYvBm/OjFT9laLFKBeLsu\nw6bUxZOvvHLNrru1Y5JBC+LwsCHil/EknGopn4ebUBV0M23ymSVUSArifCvj\nH/lme9Xe8BtzlSNjNe7DhKvLtZTlHkeeNg5nuNf4rEjH6tjfDypPyMG80smF\n37XhM6OBsAeArtxTPGgJU0e+g8SQpaCCNVwkwfiupnJbCnhgofnaQlxzQG7O\ncF65RY8c7JGw8ApWN4+u4XkMRXm6YJc+wpBH+rOHC2aCuzoHRf7kvAd4TUVo\njMawVOrYXlw2HovfdEKKyZEYxolignKGU4uJjXKZbbP5/U6JPwEuWFLf0gOC\n70bLFEZAtbtGF4TkHSEfUm7MPYkl96Hw3Nb/tNxmq/+spwNDe4g/nbRolMMD\nS9UNV/bcTy4XeNV/L1hwgVPVPNVHg/TzmGrPL9Ljk+ffGWyQLl9GSioh5wNy\n2i+5YATQoQexnOfyfdwVL+vM0dXctpVOuTgrQVdI0O9ln0svVfMj2vQCaVgN\nlxlhlglkItl6V1BAaK05+Sltesfa4oK/LgLJuqNOsRWqZb4OJ8m8bVJnLzWm\nWvyB++Mv0q+NNNA0kQTHhICE/KscZVVKJ8JNwFQPvhNXNgAVenevEe3cWzoT\nTzxB\r\n=fTeQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID/wOtQN3W8M5EoncCHXVV9pWK6QEQ8U0t3X4QXUX8HOAiAeqEEPcP1UsibYVjt9WPZwlX3DJDXB+Qpl0vsVIW9Hyg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200324211658_1585084878810_0.3296799720663992"},"_hasShrinkwrap":false},"2.0.0-k8s-sync-repo-creds-worker-828.20200325010940":{"name":"@atomist/sdm","version":"2.0.0-k8s-sync-repo-creds-worker-828.20200325010940","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"fc0b188b8f066865a9dbcc4be0b9640ff48dabd8","_id":"@atomist/sdm@2.0.0-k8s-sync-repo-creds-worker-828.20200325010940","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-IL6pNCrwUjkb7mN3Z8a9Jjmuu4hvFMpe1pUrTPvq2P1NqutavpSqA6J3oPbGSK2yuLtXevEg23IIbwycSahBAw==","shasum":"4407e769a6e8803c5c485295ec9c27cfd78234e7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-k8s-sync-repo-creds-worker-828.20200325010940.tgz","fileCount":2064,"unpackedSize":8785880,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeerBmCRA9TVsSAnZWagAAPwIQAJ48Zu5n3tQxyBcNmHlu\nggSlQiaYLG1oY+luze4BervuxYtv5a2Z5lueS3tMg5VZs/J1YcxyiG/EP+N/\nuTB/N7d+z0XAz8SX7f+TQAh1rUZnBYoPLRj6GJ2F+T39TYIa6uUochXEP+oS\neDb/XatmedWLJU1DBig5ps06wg2f9huZM/z5XNm/6bkCMR6W2IseVWW20VNt\nXQGKAjqOUPoEm1g7lFpAF/xY55ziQIr+1XuZWybVTKiBeYFySrfvBB8p9B9h\naNl8+yOCPCIEZTrin2llm5e5aaj+9JjS9zIERpwiDMYRNFBx7N9Fkz6qOTQl\nHytP/t4gCPARVbOjpVn5gU4DGPHS69xMGbGCsfxzizf+ov5FNbouhBnjBdqs\nEqh+RxDgeTNhRlxQazQjs4ivvIPU8XgDhyEqX69ly0dsiJAbpaaxGAt3hrPi\nonRgk6xEkCGLwAshR3MobrcFfAF9L9N4UZAlh4Fnnd7rlbr5QlojUo7EBHym\na+QwZTL1CrWEu9bwK/whRR2aAA7YQAWfLcCSS0TmjtzBDV/tOw8wZW7+qoob\nawyYcQGK4OtoOyw9HHA2E+enTvPqKB+nV68fjIe3bpHw+GWCYZES2xJA9gP6\nsa2oJAYAtu7pb6Hp3j2j6rcmPPn4dGD0Jd2O6+Pi+Y5oe0feXN6/zQYjkaQW\nt1ax\r\n=ozWG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA4WXhD1TK8uK/V67AKXflGWRGWiP/fGy6s0XTYEuL0uAiEA0Abpqz1W+eDXWqHQ4ymyWodBtDdKnNWk0dui/ZxWizg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-k8s-sync-repo-creds-worker-828.20200325010940_1585098854291_0.8939048539505898"},"_hasShrinkwrap":false},"2.0.0-master.20200325060840":{"name":"@atomist/sdm","version":"2.0.0-master.20200325060840","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2cbb4345264d5216afafdc1518cc584f561925a9","_id":"@atomist/sdm@2.0.0-master.20200325060840","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Pw8hsASrfN9sBhHbOBiBd5rbIRY/ZuxcGAj3o+jKyuPFiyCGcfc2kSt53qZfII1ED+HP7+PpMWQS5/mjIE/K6g==","shasum":"0928f6db75d7b3e92bdbf396b303f5b836f09d71","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200325060840.tgz","fileCount":2064,"unpackedSize":8785947,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeevZ4CRA9TVsSAnZWagAAl68QAKMn+ivdLwadZ1MqfD1O\nrOXTdl+nDyyUZzZtOFk99FVpG/gKwVjMxbRwZwLeduZc7dVcLUbGR+cGxxHY\nW5z5QOZ+fpyRePO/4cPQH10b3IolzZPQWA/MzsVOkPAKd9Ths3bUwPk/pXcL\nZiHBlkjPQvKB8ZLA7VQW/nZK5vMqdFsj87b9zIPNdxOG+04ePNRXRappl2WG\ngSdSTkcK6honMZYjp0YKQOTVZvNx2Nf5Rq3u0LiaTaWQQD+wzmwl8WccXMwP\n3kwfLcbx6k+Zm5UURN9Xj4/ijM8yN+b2m0the7CSuVxuKN8F2h2MSYOJMzrr\nZrkL98D1kuWYQVjZ38amouwxEsCZ1ilCrXHXJvv2VmEl9IT9gqHx7lS0vG0p\ndfHed1Gn3o1z2L64+S1ACmACKwkwsA7hHIRzxb7qqPzoVBRxtmyhVRnR67z8\nX49dIq6FFqJcjb16CxzY38y4ppKaCmMHLFXRIJ0to1Bc4q4RdQyLiikBMwha\ni0FZlAVMEx5PUByQ7lmUZ4nIv3tubCt+Im8HZJeyBUmS1NrRsRmb/6E2wSh1\nWaelzS8qfx5ToThamXb8p7j2WQ5R0TaEo+ydX8I5P8kMh+OXcLP/NibsmiPf\nmcJlpg/QjAtKqs7AAA3DAPind7LpWQPrktEXqtTQCi+xlO+kJOR00KG3OYwE\nLGND\r\n=ODij\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDIrJag1BM4bxqZoUbMOYSgWk71XupscqUi6Azw1hEM4AiBCZdaFUJWOgJyh2KjPJJLeAAbP70CoeHtawAKm0VZpGQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200325060840_1585116791539_0.2918422813770918"},"_hasShrinkwrap":false},"2.0.0-master.20200325124454":{"name":"@atomist/sdm","version":"2.0.0-master.20200325124454","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a6c9e3e9e28874a9ac8be017d5f334f22d18f02b","_id":"@atomist/sdm@2.0.0-master.20200325124454","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oZFxPLlfzsdIulVBDapFeLEuqZ9BXoB4uX8RNpBUvF8/H9HH+NPgiPW7wuv6B6RSgx0Sa4aTj1IUDmr4ztjtZw==","shasum":"6a9b46adf0f62edd9ebb486e0642fd0859dfee34","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200325124454.tgz","fileCount":2064,"unpackedSize":8785785,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJee1NVCRA9TVsSAnZWagAAUpkQAIEOayJMiLuLjYpNrtFn\nFYbfN2tIsHf2ESC7fkCc1ohtX7uPzxjogZu1g9fZuIzgG7y6HlHG3kzfd3Rg\nmAjrRvUG25pQN9n4Yu6Evq33D3+iPyH0gCGdNQEFcd+tjsvHA8UZYF2HFQPS\nNl1PE1zmRlnm//AsqUsvVrrCLTmKhj0GTH9vpuSd44BRLbyKruDCyJtWhrXg\ntax4dDZpjp6r1KUucA5n3n7ZHW4xX4OqJJB+x/leEAeVkfnw3Emmwtm04sta\nsYXNRzsm/KT7jJmoM8beNu7+5++r1PPpEM31nQZj/WTJMybGmtPoUuZRyJ+B\njWbMBl7gf+zRjQwVeMCNKVZESsNiupj8DkVbGr/zK325KFkwbgWq+RN/5yvB\nrO+oUNP7PH3l+3+UwP5lNYe2CJHTzdw/qrw5kDyv3KZpSfK4EyShvdg/ehRW\n9b+GMRPriueGB/j6zXIEBgFEqD5HmBSG1VoqUHs5CmmjrDnQENkz2k14B0fv\ny4HE3NIhMdjLNySj95inGXQKZW4C1P99j+8KJ8p0APOZ2x99r6eqzoCNkUiI\nix8bR+7XrCRrza0eVpMWJr0rZtkJd6TPUgANcWNweaNvRkklVKiRNIIfV/ZK\ndoiwnhOGspX6cHSZNoCdu9MAMrd3CpPhAFCCQIU1Ga6QuA+NOAHGTjRN5LzM\nE0Rr\r\n=5ShD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDqyONSCv7BjRklT+43qoRa4UxZ3LBKrpFMt0wWWdhGIQIhANRmtd5Hson/+nxx5K9tO1OqtmtSQqDX0tEP95fjy7ol"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200325124454_1585140565223_0.14763976873340567"},"_hasShrinkwrap":false},"2.0.0-master.20200325133634":{"name":"@atomist/sdm","version":"2.0.0-master.20200325133634","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"85cbe5ed21a69520eeb1173849ac810716cfd807","_id":"@atomist/sdm@2.0.0-master.20200325133634","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-4/XgmXI3BS8eSD8t6KxUWcvc/8pkUTJhaR9WUzwGCPQlMzm925xsg6Ixokhkx8oLTQo/ZT4ozwkYV/bwTl2ZgQ==","shasum":"bbe5062ac6045deb901f3633fff20f80b15b0647","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200325133634.tgz","fileCount":2064,"unpackedSize":8785823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJee1+UCRA9TVsSAnZWagAAoa0P/0TBSENaEBTKla+qKLGv\nqx4LT87svAgQl8eiMQQI//KvTxEtX8l0EXNRvu0LYf0qcIbXPsG5e30h0PFW\n8mLhm9NtnUl20wVROJg/AeGFbM46nHow1oFZbFNv7/qWS4RKvS2uqOnhjvG5\n6lvFdAHD1E3E+LI7VPckAg84mvG64QZJlfr/2civKJclznzIzkRjPKzGO4rx\nxDubupYsL3c03wUk6YYRmJBCEJ7W7vLrBiuzzQrqxXOTKZiSuatfMkbjw9M+\nnzFAFIGRS3tDuTj7G2PVBU9o3qVN8o5lue25QMNvjTje5tavSX93cuqlVn+u\nrIefKmbDz5WvQRNBQjudKnoZaWtMeZ00ffESOBfx2hHHYAPGSsVvdDtvUPBq\nHEcA4dSnH4f+OYFW00ape8BnJSA9Dg1UkuchX+u/Nd40YgHBlnC97Jkr4f/e\nbjWMNeXoU7Slcn12Ba7ACqEctRj+RYzfHC0GNYjcWVRdA8g6v/y1KyYFEhoU\n7RTnq3+a/5CfbDoOOhTT6c+2omdEfkHzyHhypCqv0EE2c6mSkvsgOIDTt8ud\n5R/djnE6m7LGa4WU97vB7JNzcadWLpmpd3mRM6nkI11Uyhw/fBqFMjw40uwM\nvuysf/tN9996DiflulTjxl/ugwcjvtoK4A7SajlPOJ0wcR+3bDc6cPELtVF8\nbeBj\r\n=OMZz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIADkpYlRoOHUqCwAOHMtiFa8St7OHap2ZISKRBTw9dM/AiEAyvUBtRDQXQybcKav2FXF9PJf8T6jRonwozV9SN2a+oU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200325133634_1585143699659_0.6826574996908836"},"_hasShrinkwrap":false},"2.0.0-master.20200325193634":{"name":"@atomist/sdm","version":"2.0.0-master.20200325193634","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"a35a39203823777e863eb9c7c3c93b30a93024cc","_id":"@atomist/sdm@2.0.0-master.20200325193634","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Hq9Ej6inus0SjTlxkUvmkdmEJUFXA8FOqKYOF7d8VLKMKm7R3jUr/0HWV8zbl3uhAmLkS7VPFx/AKFn6aSXdrA==","shasum":"aa4fa0e0b3748be11d552c1e8f4021146b73e354","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200325193634.tgz","fileCount":2064,"unpackedSize":8785755,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJee7PqCRA9TVsSAnZWagAAZ64QAJH0ZXM2GLHComBKQFiH\n7LnxcJYAaPUvXGjCRDYr0PUhKL4Sf09stGMtyhE1I9B0zOjL0I1HsVaAnqUz\nDOYNRYH/cyYwbVtox58yHxsNnzb9F9s+vc20FDu5DgvAKWKEWUK7uOqgPoA/\nXRQgTuco8fXpruprAb0z7E6urD8kd+tX40btuE8/obXarWxaI2sTO5GyxkTP\nzc1kBJc4/KTz/hBAbb29Vl0iDmb9y59pYfjLIU/ffIe4v1kU4Rb7XdApbhuk\nSLAuLu9MZQQITuw3WFv/0oQC4XNZWCkaP8IQYFqZpsTJrwTpLqF1ZKBqugOM\n0u5WNPMIC3Gu6tX3vtRKWoNiE7aYomXM2fgRrP2+wihjuPwDuX2FTCPQuxrd\nWNUOvN0b8qPRMoebC3ZPu6qtTGOVCHihylI7wys8d1ZkwLdSERdIG+7dylXJ\nC0oChvbXOViYisDeXt55vnmswQhluyS6tUV34047sMqCwgVkEwgcCLG/j61F\n1v670Erh/G1U+/kkASfZ2KBefmCS6hJlbuUgAk/dYi57W1NzfHcEO2Wx5V3B\nCrhtBUJ/bLZ5fPUQtJ+w6ECG10yGYroB1MKBb7gJ7BgswyiaSf8NPHXVPGJY\nH1jhs8N8JzEnKDETCENsu2Klzv1TYdH3Iwqld47xIgIWxbzriEp1wXALh19S\nEYWY\r\n=zKiw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICScm7ci2cRQF2Pzxal+nfj8RUYVzC+X46gK9V4zzjreAiBqdHI8Rr8cViO8doWrAiCL892Rgza633IUV4Seuwql/g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200325193634_1585165289738_0.8688341862729969"},"_hasShrinkwrap":false},"2.0.0-ipcrm-config-change.20200327211110":{"name":"@atomist/sdm","version":"2.0.0-ipcrm-config-change.20200327211110","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d8a181f884ede9fc5783d8f76dfb07e24a8454b5","_id":"@atomist/sdm@2.0.0-ipcrm-config-change.20200327211110","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-Ii3A00omAy7dSK7RigPVbyt0u/HVGDQxAp22AjahkrJVXNy6bo9r2oQ7y6NvHEWTzWc8YrhpUcQ9BcP5ZJ1Bkw==","shasum":"78ad2abe8de2dc06ae08076aaba44acacf781696","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-ipcrm-config-change.20200327211110.tgz","fileCount":2064,"unpackedSize":8785656,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJefm0hCRA9TVsSAnZWagAAnn8P/2RwZujbG9svIu/CdCp8\nsOcHtFwjRK4ZsQKSQVgE2Y1X1z6Qvfg56NGPV6HToWsZcmTMHVuHEP8oQay7\nmGroUIx8OjT9dYerx4knHeBeXT4e3UEkvtelhBS6t40IwtqxkBQrJVCl5OkJ\nK1QaFbT89kx+NxwfLybA9r33KOoVroJr9UxPQM5B5VZMRXym5Kp9LLiYtvTi\n8o8tR/E1WVjpgmlwB2guc+5UBuUd0fHeUkrWNt6L56JB/tF/Y9g9K+jFjAkU\nCezmbtet62K80jY/Hkk8lsbYuYeSsOXSxzfTqhEE13pZCPnjfHzIuaP3RwQq\n2cgsXyocDhLMJBTUq9FlBAP2DU54tbrAT70r1TEtc4Q3BeDqh1hEu8XVeaRr\nf19lY1rwBgXKwZHQldxagqaGBxsDGDL0KR1HaMElx+exdsXYQbQIY3EUXEts\nrJREyetx28u7pnBkftUwdYc4Tl6GKu603cJPl6ou+1rLgHdU6syKEH8Foxnf\ncu3rGoD9hVwoMys1TVDVnTqAxr9d91VI09/UE82xSjzqA4SBZPNzAjRfr4hN\nClDMBVfgA9YQM604Q0EUsOfF4Yx0iHfRJM2Ched9xUClAVWLyUunulw9KQNC\n4xsWz0ime+2k1AGmuEujDONXhj2hO6mWxwx/lQn1HMiPmaacOU8uvrceoXjg\nitha\r\n=L08O\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAjYhoHWS8dV39vczU4KMFqrjysW5XBj/HuCjFexEZUtAiEA2gmzDQJ0x/cEIWhjrljut648oAHsEiLM9Lg8Zw9Wjjg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-ipcrm-config-change.20200327211110_1585343776442_0.4632208712042072"},"_hasShrinkwrap":false},"2.0.0-master.20200330164246":{"name":"@atomist/sdm","version":"2.0.0-master.20200330164246","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"523a4ff5f69aa68e3be550886a66c4c1d7efced5","_id":"@atomist/sdm@2.0.0-master.20200330164246","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-slxNX/yU2ZMeGBEgeBc0Of4mW0UP9/srCWrgRliT+XpiNCmU3icqygipp46A7kZ0j8j//ySE/X2f/GfAkL+E1A==","shasum":"8a5f1f22b65671cd68420a765179290d30e4eed5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200330164246.tgz","fileCount":2064,"unpackedSize":8785754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJegiLCCRA9TVsSAnZWagAAvp0P/2mvMwWM5XKIUt2y90+y\nuWqH8aC4DZEfJtwWf4E4RPabjW2eFZhsn94WWyu2pdhxf56pB5DoZmIoyyNP\nMKWddHKbKWbedjww4Dj5LMYZXdwDeRbl4pZNbVxhQJlsYn48rEzVl/UzRX7+\nrvYP65sL6aaCIziyex2w3OQRpo/Dow+femspfTcbXsn5AwBTZ/93MV2PLCly\n1QRSOG35JXdUiHEhe3BqhTe3zl53Bxjft9McoMfSw3M9wxfvkvV/5tOvvxrj\nzM6rIwnSNydGOFx776KRpBue9VlxwN/h+pojv0rTPGB3KpI6WAuK+qwIPUal\njWilny7mLovUbmDuDtPfSC+45sF+XLEATvFc5c1xvjNR9yYBwI1+Got8k7kA\nvtNm9T2XXLu0sYQAcIf9+NWgi3x9kx9XD5NtC55eA6j7ejBP4e3mhtwE1k5j\nsKknqaFdgFmi5lQNosBLMtzwPRY/wWWUg5zdDLchUCUlEeFUURihXRtec39C\nQodS9Wo5uWI0dwZrknCyjUvpN4DGGxn6wWP0W0FB/B/Qb12uTCUzxvyY45lf\nWV/wBHBH/mRYn0SaE0gkgBG+5uVtMIQ2nbUHtVsKBxIchPwXVvaK0LdtXfa1\nW+GExxdpdp3+sYHa4m4XP7J79FlbiPEc8XGUJBe7MDhp35rm8CEuS6PkOIaN\nKBv6\r\n=GbOy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAxTyjCiItpkQDZmrjvDble1gtsNDtq6zWjldELyyRuNAiEA6L+Q7iGqcz+27MIM/zT/H4PUJWyr1HYNObBELSSSI04="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200330164246_1585586882138_0.3278929246704865"},"_hasShrinkwrap":false},"2.0.0-master.20200416151221":{"name":"@atomist/sdm","version":"2.0.0-master.20200416151221","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"26d4df4f5f24955c9628be51ca7be8f8a61d6afd","_id":"@atomist/sdm@2.0.0-master.20200416151221","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-qACx6vQlsq/lQMiEup6dEmnM72eWPAasst2PaXrG0JLIsenEDGUCz6nLTxUeTY1Zi8RtHgYLjS2J8iaYpVa0FA==","shasum":"4a8a23d3188f0c0e69ebe09229b3fe6975db2252","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200416151221.tgz","fileCount":2064,"unpackedSize":8785760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJemHbkCRA9TVsSAnZWagAAnTUQAJLszIFlqm54YYn1OZcX\nHbGmU/U8KdAeCwLVewcOzwEXYEwv0t3g6vjtA7/H7ZOnArMlvtOrFVLyKLUQ\nb5VDc7C4fpYL1heJFIl+TcRhXR5EiQVastVZWQtAfbVbvANsQ+2SJIQtP+a2\nnmurT1Gr5Q+fWnJ6ZDbhRxW1BEazxoVKidl0pGu2A7QtSu5/V3aD/FuEeY3T\n1miIGXjYKyxraQ9xcw10Qx+bgN4JBKT3mFTbK0nKhF+OiNibQ1Csbluv3j8v\nPDELb2kzL7ySd/5TnfTxvAuRYMbnjSr9chI3EYmf23A6SJmw0QaCY3nTtPm2\nOjF7rlt4s7Y/ZLb1qgGDTwFMxXIYqUYd4uDAJjgrPe7NbmrJy5GZ9QiwfzU8\nx/x7DacydMqIIApSIZuh7Ccbxjt5FA0nwXbShahdT3cDz045rRtSLDjlIcts\nqs4ggINs3P0bVPP7K8Q+g8PNcGyh+LgpJqVMT5lNOzjEyYh/T2Wvbkhii8ad\nQ086w1sycYUUPJn9DEwsOZEYrmh3eO2W2nMawWJi0uSlKHt4Hbuh5WHdAUzn\n043ALTKYQgWNIfq4QkLbcywUKIfT/pOkSa5qz72T8rBN8RToiSmTAsudn+a1\niZ7Pc7cX/vpJZ+Hksyn/TG5h4089B2rCMWPewYN7doOEdqrrmslSVj+1XBTm\npYFM\r\n=XdsE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDJV1Mhh6jhmrkH7+VSSIuTsFvby9PRHIQkkhwGaKsnMQIhAPKnLksEh9Tw2kiOBAGEtofxwB2cYrvLc8syyBUZvO6u"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200416151221_1587050211351_0.9909504782450282"},"_hasShrinkwrap":false},"2.0.0-master.20200416151527":{"name":"@atomist/sdm","version":"2.0.0-master.20200416151527","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"26d4df4f5f24955c9628be51ca7be8f8a61d6afd","_id":"@atomist/sdm@2.0.0-master.20200416151527","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-W4wjAu9uyjevUPaKbOJAJo8lW8R4yXayZGKP0S3KGEfzP3P+oQVTpkoKUkyn096QnMdIZ5lpCsTfm8D6EhbkxQ==","shasum":"b6e7c0a1d1b9efdbb9022e208df934cd979aed80","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200416151527.tgz","fileCount":2064,"unpackedSize":8785760,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJemHedCRA9TVsSAnZWagAAp6gP/RVZkC89WtgxxCKoaDxc\nnXcODWqPxowzogewyV02PyOoA8nnOJtpZLYLOs4IY6okMa8bpTgSfHwZLYcL\nWZZd2CRCvyJELRiSW1I3g7PTy4it277vNKG8ij0aFxXjlAbu5A5C0uPAwvvy\nZ4WCVmXp3THp1MqD/soU0X4iLvFZx/h0yAndWSn3GEYsIVgMu9mGHhdXA5Yi\n7pWzNYeX9lPYDXzfKhZeeejMFFEMsBqDcS7CCWa46rlNPrRr1vDQ1GbT9Ter\ncj5h4UfkEnKOcCQMHXDMXROPNOR1tVWHOh9VKPe+D77TnVCIjCUktfVxAmXN\nyMI8A5EjzqehiFQOgVaGKwx9gYyZn+Z4BglQbj9+Lp9J9yXBeL9yA04l1sdL\nvCEIhoVNI20vEJZb8VHP5KVStSVWDlxuqeP+ue1v2QycHAphJH8Hq2WMlznY\npNjHn04/13dXR+n2XqxmmVnuWP2/gTV5hLGRz38ALiE2WTuk5+PraqdVKT1K\nsd8IG6XjLrOSs9M9pjbkr+zK8Hu1rxW2BR7w0PGhV4E1EmsbNVm4WEPS7dD4\n/RPYChhsgVtBqL51R53AEC9zh2ker9cJI8vbeX/rhZLjGNfAkYqsMTUUyLYu\nAUg6lGdbwAaNfw3zsntgUFbkyiGXoR+di8i0PcBFWEzjwiIFv8/qMcJTYs4T\nmtbl\r\n=Dp1M\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICKRevJ5KYc+2qGd/cFd7C5cLQRlTg5IE9UsqzZMDMuTAiBvil5KpcyCCwU5S6Ka+0jdtQqrOsSTeHReuQyb1+cejw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200416151527_1587050396520_0.6607090226409056"},"_hasShrinkwrap":false},"2.0.0-master.20200513064903":{"name":"@atomist/sdm","version":"2.0.0-master.20200513064903","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"36645236160fed5239c9e254d069f8d6a7b17e5e","_id":"@atomist/sdm@2.0.0-master.20200513064903","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-AyX4+qfc9NZ7CFv8XEqwyxNicSDSiEWnhemh9+MwbxdfnWsXW2aULjAbPlhpfBx/ZJOiD9JDL39ULLuolzudBw==","shasum":"85ba8b825d1e87f317b5ee64b6c64b7e71bf4b68","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200513064903.tgz","fileCount":2064,"unpackedSize":8788711,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeu5lWCRA9TVsSAnZWagAA95wP/A2Bw5/8PqVXPfqWcg+I\nQ0NGc/IamdNzj0Ny6olUBRw6BDZzhoMUdO/wj6gLvbwyxb7GdHlGg6v5CbUi\nxPaPCS0rR1ViieP/mEvCxw862KNJJ3d4CNUVvX2axLxrXNxihtPn7v98vqmq\nt7JvW0Dl9N1rukby655viW2vSBPGv3uAq0HzJPEBnIr/NTkyVUuR+I5CYi1L\n3bKhe6EyvnbdrGqPac7nQJTVTGSmMIafHfDvd+Ljt+xjJyDYXREW4YWHh//Z\nWEhkAixLiDWQR41PF+WXf6g5scOMWPPomkseHlVXrN1PocV5GNQ+xznRv0wr\nc9lwTUnDDfohqi6N6DmY/b6a2S2XCn+tB71ZCy7Ia2FqeDRUJiX8FJfPtI3F\n5ypoq7Pewv6fULis1jAZGO2faihteXAIrhljblv33iszh/tf7nH0qcXWu6c6\n1GCFBP4Uu2Fh13/jrFtD/D677o5/rQQxwvlNgyNGMecLKx5MenrgQq8OA7yb\nOC4+K/e5EoTRxpyibbwo9e/OAsUQQxL/dxgc77t1sIsdwmggnNtT31jiawX4\njZoGQYNTHeneU0k7xKXRVGaDXipZFNpHSoz0TEt+3i707MLdY7lECnm03/VG\n+A/iJ8wnHDOahrXDcv+o4hgD+yW3zElo0RJ7jfoWFdS2oxBx6MLYGGUnrzdu\nA7XE\r\n=YNPL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCDcydK9jfaR0OzHQMo2/8q3+2mE5gJi2G8hClgSSAGYAIhAOOBoNSAM1AyUDBMNyWwYE+fySx5SiDAVUacILPLyZQh"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200513064903_1589352789785_0.9862518921476744"},"_hasShrinkwrap":false},"2.0.0-master.20200513070623":{"name":"@atomist/sdm","version":"2.0.0-master.20200513070623","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"00d4d927c97d87d4bb505d850d5752cc97c9fa26","_id":"@atomist/sdm@2.0.0-master.20200513070623","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-SuKEh5x/NtDOBBQzFCelP3ebZWTRtzwmH8sP/hSBdPQJTd0YF5VlFKaI2RfXORsfbnZ8xfLloTHLOGXpxMrKWw==","shasum":"8a410f5a965e69477f68fff24351e0fb533de383","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200513070623.tgz","fileCount":2064,"unpackedSize":8788627,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeu51oCRA9TVsSAnZWagAALFAP/Rj61gebql64uLL4TpWG\n/YjTgu9IzBZlicQqihxSuuFJsVRV56szHOgcHeCN5bfe1W7bHdsui3idzQyv\nF57Dck0VXyJEVkXOSVkwD17yw7eUFtcyEnCLbc+q8373ifKcD0oJTCzE6560\n/2kFo7uEoP40F6jyiWNtd2sgkLC58GOkf5E3WEv4fHQn46XTmmtR/z3Fugm3\nH1jNvVQrIcRXTgzLN7pMy1gulcMwkJqTsBBgn1LW9jzVV2w3i8zZ+BUqAwV5\nFHyTPGgLcVBvU9TRy7etCC+rrXhCetqNM8m8w4fsSheQDOCSZDvRAX3O7vt4\nuyryRnlt/BDbItg7mvOoWfyowc62FJXThGcctuod2ny9SKI8bUJ4HX+H71YY\n1RSQi9WN/H/Ka2vL6j6FxuX+WfUGggf0Bt267quq+wKW12YhrCnruJZVD6/B\nHylqmt6Z7RcvmRDtzk3S4+cYBkJpphZZVu+Dpgs1xijXiGOvmT26TxQNOaoR\n1hYEtUC7uUW5rYsUsjs1mkoLaB7pUTA5jp0Uin9pknLcArricKHs8RoBchCK\nGV7Wc6ay1QXF1AsQEQKPNUvSXab/ZmuW3c0eJ+X4qbosusNGW6A1sgdNKkrS\niY7G+GeKK3iw6yz99LjBS+5tavG/RD2zavKDOs1taIVMo+1BfIB8jtDfufjG\nSzqy\r\n=+cp3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG06ZZVUoU/clwznNkUwl9cszYZktZNF5bRJEFABXEvWAiBKq3EemjglwDzt1lZmujDxcA5vKTRsATpkPzkzPQmAFQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200513070623_1589353831884_0.527248509884076"},"_hasShrinkwrap":false},"2.0.0-master.20200518142233":{"name":"@atomist/sdm","version":"2.0.0-master.20200518142233","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"817dd3b571c1cb4dabdbb0e29d8e2c4137d5c4ff","_id":"@atomist/sdm@2.0.0-master.20200518142233","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-7Zb1ZgGeO0OwaXQMvlXcsTN+X2yiePPWgdTlfzwTOmkZCjU4Y7nBSa+AviN+iutIXTOvUuhGX4eidU727vTKXQ==","shasum":"5752ce63c2589cc3ae1b54f0431a6f7d5e837d6d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200518142233.tgz","fileCount":2054,"unpackedSize":8765612,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJewps8CRA9TVsSAnZWagAA1PQP/j7Ann1RyQFtZwVgEN4F\nAq6i0KEpOfXWX2kg4VxTcOrgFt/PDUdbMVHuL+SKeWxXAUcECyl28PnLnijs\noB6CL22H8Mhcn5oG40b40/qViUwvXfjc7G+UjpFw+uh/fWjy4wb3AhpXBIdm\ns50xA8MDQCsYgBVef7CtE4iNhQdiyEl/oyuBK089i8UHq7fDwvB23+vNn5iT\nuc7mCF0FO76IhAkjAYv8sXpMOiJewq2FTl8D/u5xAhxx1QE86BS6OaDyDB19\nJh01WLC72JsoubF5gcQdMMi6XLFPSTHqiofODIgLLam0IboJ+eF2qdphk/dy\n7ALEuuJxHwJHFqkhaWRTdWKjCFRjH6KpmC4WhZ74IIbWHfnXw5n8ZcMGi9+2\na75y1ECHSBe/BlOPrPSecAfh/fDmEdQxaXdKYu6uN9DObd29lnowqnxHNJ45\nw5gi9gT7viiuNr4ja35Xz9HVGN9x5U6rJg8Rz1mCpydM75xcSl2sLCVFASAp\niEgRJCpRykaZgAfXi7qkc54LtyXBawYUYc05+DNIpiSNaritWhfH6fvj2LBd\nEfqqOYC7z5OO88to0Dk/R62e9qGpw90fjcEKGJ6wF/BAB9IBm/P3yngraa97\nTe/B0Q9HWgRZWxpcCXCa3Sie0jp16VORvWD41RJlKQYhj3zzOjjVL2i3byd0\n6WX7\r\n=Nqj5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAUZjnnSkaG4B0fpx0kcRk46Jy7nM5S0bTcIjv8o9ti4AiEAwHYdjJElPjpCYD3efHgRimwWICjBsbXg1gSyYE3pLkA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200518142233_1589812028113_0.37304198363049457"},"_hasShrinkwrap":false},"2.0.0-master.20200609095842":{"name":"@atomist/sdm","version":"2.0.0-master.20200609095842","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3b9513ca622bc14d4017443fb63e01005ec05f8f","_id":"@atomist/sdm@2.0.0-master.20200609095842","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-oxEg/W5oQi/S3EToYjYHSJsn9jWySzKONzAwwD4ANEDl3juaa5iQ6oevUSScM/dEZDrKq0Ax1xpTvbDTYDgZew==","shasum":"cbddd4feb1f103c1b9ab165ea857745573eaa95b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200609095842.tgz","fileCount":2054,"unpackedSize":8768201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe315SCRA9TVsSAnZWagAAyMcP/RW60FxrhGvrbi1YlSJ+\n9coLJc6JPD/eLjAmi6St1vw1F/s3aCNxYY7pPeIH/N2vqVC+9L50Ylqk/j98\ns2QoOoSX92yXV/T1iQkn2nqzvRCDHKPFyiWyGt7dhC0D0FOtxbjSFh80KgXK\nBimcVDMbidK+5jO59jzsAUGBmkvG4Wm0FSUpO6/SCfAXgvfJLH6tNQ1RClAo\npTXwKyzX9BJ5BHr1lw369MeS8ZmLEaoJZuL2Z95qBPglbaH+NRIyq5EWMEwr\ndy7djXb5HCXUCaBTukXatthDNGhzhd0sss7nocrDCBHdI+i7mlPnatCd0Ngw\n2qzu8/PG7o7OcISdgXAvTNBfx9aoRLZd0fINDqH+G5wOoJNk+T3D2Jjiwahi\nc+mc86pscAxZoJnsQ1xL+n/Z++80nIgf/EA+aVNiDPsrG0mUqi7E5c/vUQJs\n4xc3KmA5tY349y57ZTXs0E5r2wfn1RMC7qSS+tcJvj2R6XkTqYUENUockguR\nU63Kde46TimzWB9kCMLYAqSDvNh2S/YsJVgUkaYlGGu1a7n+o8+uMlFsuzmG\ncprJUrjLZGq5on5I69K7EtfIptYZdSV6M7Dte+repVi7kGUwq7UUgavz22T5\nU8iRSYyY3FEp4xhedl04apRmjZkf9blutrNZ5EDZ83qoVyHF7Qjg/s8Bcw7h\n36ko\r\n=3T2o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZtFkfmXkxilKvB2dSG7WrTwLHD0qkXy6xnRzrvAenBgIhALjRuIDsyTRG0Z3iY/hVItLp8pxbmIjXJjDPQaMddoNe"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200609095842_1591696978099_0.19647437707750326"},"_hasShrinkwrap":false},"2.0.0-master.20200609110411":{"name":"@atomist/sdm","version":"2.0.0-master.20200609110411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"73cf7aedf69e45be5b204d6cd18ee4821fe2dfb3","_id":"@atomist/sdm@2.0.0-master.20200609110411","_nodeVersion":"12.8.0","_npmVersion":"6.10.2","dist":{"integrity":"sha512-syxlrFx6U18n789/ka71spW6m5QKfa3l8WauLB3NAWoXg0++wUxlea+Q9meoML9vKc7k3LmTb4e7OPusH57eow==","shasum":"cdb6f97f1515a9da240fa4123e5995822d1ca282","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200609110411.tgz","fileCount":2054,"unpackedSize":8768555,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe322yCRA9TVsSAnZWagAAcPYP/jGI0E/U9kp5I0BN3+SB\n97WpkdHd1E0hcoqp6fDmrUqVwHKe4jDa7E7a9gO+nGQ0P0QQ3ADO9+XrQMa2\ngCwoEU5EgBeBKA01j1iy/FuobfFQvgYNS0iS3rDhcyikyEdEwy52ONpqYzTs\nTz3OYnV7QIQz0tFmGwz9i53ZTYY0qLsKsgBPYei4HZO44a16TdmbM14fHyAV\nFpUKgXBBxJ611/Wt+N5Mo1Oz8cWsoedGB0Y9eFpIDf+q274T4hltnwRastow\nXmDBI0RsY66kUA+QGQzzYVkWNa8vUkwkuaTl9xL6+Fr7lktxbk+PmPATNN8y\nPTLgNFi+/ZiPKnaTqR23oG1VCN+egpx6OYzq1z4hbDgAsppTTyz9kFYgZ3vV\noGjy/Y298AOKp6Gk02s2v9EIc1Q1WxTTHIfCakUJQYpIJ89rc+NZfsq4GE3S\n1sR8cYW6vHNPPirS9zRkSXRnFGlOg+uk+VxWIjEa6D3YAXGqIDkijXIywCy9\nD+hIeX9LRAawYlmjBd8ZlQnHEwAv44JHF4PQMyq3tv+MgTpGBb+Z6Juu6/6B\nDI0qYpXNsQRXut4bmHkEXwfd3cteyPt+WgIk4/i/9Oza8tmgh3t6X6zJOMzI\ntAdvb7eBN7FPgKE0ok9tjvlaWF3ZgI9SZ9cG7C37KqAF33uPxJIoSPUJUffO\n+K+N\r\n=yc9u\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCfhYFZ8c1uhIzwEzX9DxS3gBZqCwlqa1K2TUVswjmFqQIgdwcdyfxtBNZypdGZT9IgZSJ+yVY8rjBe0/D+VYQdG60="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200609110411_1591700913855_0.9910034622557187"},"_hasShrinkwrap":false},"2.0.0-master.20200618005140":{"name":"@atomist/sdm","version":"2.0.0-master.20200618005140","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f847c4b3a4f38dc32a7307f6a734eba74d874952","_id":"@atomist/sdm@2.0.0-master.20200618005140","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-YzCzdEt/RkFuROAf93tJjjTDWrekrdqCpSJVXZdWSHky85msRz6tHrGfaiQn1nqOE3iJt9exoXL/SibIYCxs2g==","shasum":"c56e23bbb0229138d7aa06781b45e7d8b56493c2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200618005140.tgz","fileCount":2054,"unpackedSize":8769486,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe6rv6CRA9TVsSAnZWagAAL+4P/jMIJXmc4DOWlRUEUxyC\n/TZLYx85KCT12nMEzEO5husAko4sve6zF3abakKX7caQY+JSOln0+Q7GGcFp\noXTFLNquLsNWyzBzqdkZzRy8vXwo7Y9fZsQtiRS2UUfEo4ybKAzQU0ZNl2nA\nWlEAzZJ6MjEEKRiaZBFwCVpopl1CqIsEWOW9XSHlP5K2BcNpnJrR0JIeopkm\n1y4toBk0LV9p+lfMcnm4sovCUa6hkf9fAJZTyEiiHkGSUyd9n8viVcgeMyXT\n8BrFu4xP+Oq6pEeXMQ7ca9SaDQk5XVzbNhBDdFmMxQ5wTypwgYcONCjcz20P\nh/ct5yzeI0kDc02HjKqmBmE39UI+L7AMSHFV865EN3asoznBi6v+To4Wn267\nn2Dyi8gXswBgHiBq58DOHA15K3ehZ0h4j4oT+q56ivnUiERIOfApeLN/gnNR\nytctarDF0ml7IfpYlVpNMhlSnsbUttzgp87NK8nDEc4h0a+OVYYGanE+/mAX\ni+BwbYVwJZRBQnK4HI/F087y0a6MjMJStFQoZZezgakNmUMHrowhC73iTYJY\nZSs/YlG5FnhXwzNBArrvBBF2LiRS7gp3bHbLgQHKauZXhR/1J1Z61MRxjHaz\nIxPmbdYBmGLyTC6mcquov+bDUL6/KfjjrNUAyShCWxEwn0/jaamuRYoi6XAQ\n5cUp\r\n=ZXCd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFEvHVyLobk7FHPsd0//NluF+n7PYKf2lPnU3L3MgHEZAiBIAYKIt8rYQSZ12HtZQFGTxNh5M8QJVjr6cXNRKfh90g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200618005140_1592441849216_0.3258402028171974"},"_hasShrinkwrap":false},"2.0.0-master.20200618005455":{"name":"@atomist/sdm","version":"2.0.0-master.20200618005455","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f847c4b3a4f38dc32a7307f6a734eba74d874952","_id":"@atomist/sdm@2.0.0-master.20200618005455","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-fUkJKQMw4CSMUmMjMqbvQ4WrkUr9zyu8C/auV1KPVxKEkSGf5+ho0iF4GIBGnehT0CAQWnrgshFVRWpLWF/ckw==","shasum":"bd96e23e4ccc01df65745dba5a31b1af45fd3d0f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200618005455.tgz","fileCount":2054,"unpackedSize":8769493,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe6rx5CRA9TVsSAnZWagAA3vcP/0L5J4uPGnulb8eT8U6W\ndHsWgCw8Bjsz/9msf34Q+PHid6iq7e/NHQLZSS563CJGyRNyv/NR7v1B1oCt\n3HqCkvxZjUwV6Vhg2BTQHwOIXdXc1ai+ZXB79v++frBg4VScDVH8gnT6swLY\n8kQKBt+1/p0JexrmnfQN+Z9kM4z8LpXviilFWdOsRn/Eb1Ps+TMCkOmqPqeV\nhKGU0jPsuh5zzC50k7LMfQDV3TZGr8/h/dP8UfxPgSAlaGvzSI1vSAHM9gSg\nPf4ArmHkrGz13eZAzAgahwq2zUpjW6yVqZT9MCb0dA9nTJAsfEKlpifG9mV4\nRe5LpIf7qnEeC1SgbUwXGfFXNnIGCx06jk2q/U50FsZPZnP0UZe6nYqmYrJd\nEJ0SYKHK+RjpgadlNEmE5WwPnm3ZH0yeo7RG/mc2t51hzK2a5/XI8MLwOZSp\nn1swOOByfCmOUmA7FuQsY9OqRTN79PVwJggfzwl2fmDYwjrBoVAP+GOwPHxU\njebSEhsnbLbVdH8yg5hJ+QaIqBle/a8pLCRP0oXkQMqAJx2EAgRtCOa4+4/4\nyWqqbXGnrp2/NNt81clNdN0sZpR6BRGlTXQAQ+2p/+fFIcUiEWVIDqyWYAfe\nizvXoc+Wl4rWCCC4UcNlHpdKeufCBzgv98ZCMCowhnh7A1l0K9KzLrEjpe9E\nvIlq\r\n=ZwG/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEV0T/VhFhFFevOG8ELUppYoPjz6YDeFS9UEPVbGaH5cAiA2bIJ3b2T8JKFQPlUdu3/ajmRyJoD5cTDIYCeZsMkUgQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200618005455_1592441976490_0.43846358987781886"},"_hasShrinkwrap":false},"2.0.0-master.20200622120411":{"name":"@atomist/sdm","version":"2.0.0-master.20200622120411","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0ec8ffefe7e31dcbbc1985a003e8ac86bbeac148","_id":"@atomist/sdm@2.0.0-master.20200622120411","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-uSexD/NXcmHmwrR8WzZEjh1QR1pCMObMunK2bY4O9H3JxilaMRPg0bIz4yTHiOe5MaWoPvG5FoPmanLtaV3ZOA==","shasum":"dee4542c67848060753bd657f19dcca9702384e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200622120411.tgz","fileCount":2054,"unpackedSize":8837052,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8J9PCRA9TVsSAnZWagAACCYQAKAONQEgjCeWcp4/gB/k\nzbGvtRG273hPd/JDKZ3m0oPB+0rkDcTSsPqsMc2GJiSmfL10g6TDESMvdkNQ\nLHX7g54lu3jJxeO23f1Fo/xB4PN4LZ9vEgmWJR5uzB25yfm8iEE0lIK3i3Hq\n9766LdHCLVBW4l5xFqOzcAoOhMrakUIKlsI1YG8Q1tLzWPKNNpvMn5IoI7t5\nhHjAVAuELALLx0jYAUuBHzTlV3dqTFBAibaCCUdmtJI/ZBYDlqhG5LmdfuJu\nuRp/w9GyhSHHXpVfYkgmo5iFu40JP3RuxYZRCtivRFYPKT6HDYqMu5pK+j+3\n+WkoZFwU0khn5/mlMXrJ2cDz8TVwoxCDTWp60Au2souuRpLPsrVmrqABoDdp\nz0gAHfZIlO6WcDBEwzeSwEQg1kI7vaMyTneRVu9b+sfiZaWzw3R06scXSiw1\n9swMnk91NoxIraDc/LE7P8sb64z1cMC2VEJzLovK7+z4xM9iypV6v+ikmsru\nQ1dEKeE71Lszu1GzUt8S92cgx5Vqw80SwzbMFQLBlECNUhwAgikQl2ZnSBhm\n9atsQNukoZbvE9++2977yhySwGrjAlHaG0O6B40hOahAV2ZphWTPocWcw0F/\nEPbASVlXkO/u8R1lk0X9eEncPINtQwGjjEbYfnqp6ZvluopEZh24jxV8FvpA\nfg1Z\r\n=Pt+t\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFRiX69Sfbuivl6Z/cgELa6LGMWwtjBfQPHH/KMR7m0zAiEA3+3bnB+Kgs3E7ek9QQK6UNZ11MoUmOBnt3k6Q5pmnMc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200622120411_1592827726962_0.16725343890584443"},"_hasShrinkwrap":false},"2.0.0-master.20200622151907":{"name":"@atomist/sdm","version":"2.0.0-master.20200622151907","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c614919180492855df9b9f8af7678daef1a99942","_id":"@atomist/sdm@2.0.0-master.20200622151907","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-gcAZiOrYji8gLGeGT7NgfkKhB5fR22p3NOCqVf1SkZr4sP9Tls6hSypfJkBGByQ0pDX8ULzcRJ6mU+0xVHqBkw==","shasum":"bf27120b0d14d3f1db21d7f72c9aac22646a0a4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200622151907.tgz","fileCount":2054,"unpackedSize":8837307,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8M0ICRA9TVsSAnZWagAATCQP/1hh5bxXhkUxDVkAPqCm\nxPVs+YgAnT4UNe+xxHEF2QtU0OPn63vazarKwYiwSRZyuFsJVeTogTXkEzcs\na0Xfa22SpWmB9di3P6oImW0pYLEFImG5tIc3LOfWQ+LbEynDPtucsqm+IA4V\nOP4cmBcyRWpe32MP9UETFt0ahB8Cx5RAvrb2ZJseN34ZhMv8QCMkfqALyEsp\nniP0+yNYJ1Cu+7RkNWpy5+A1lAd6wZAeqFMlauNrenwPdajrmQYzP7dSUoKj\nzx4EmwHq8f2JfnO37T7Ll3N2tuamraHFGrB1G+/iR82oOD+oD2zLV3JOwOdf\nOkThdTlZij9XJawndZEUdTG9XgV5+WXwQZJntchKOyKuvXs0qwKsrehifviA\naZOcoN2Dn399fhHUqIcpeOEsMYKj9y3VqU+hzk8SzwAZetuWl9fGkV8TMIPy\nDdJ/X2OWM/1OMkzOKkQn5lM/HBNOyinoPi2qRQceDDxL43caVDiA6u6E3RwW\noGsze+ftSBjzQQI8j6q1IoeNFSMeKscPkTRx9nLiFcb30bQ/sf7CfTm2EO4K\nFr5j1GjrfPpYdA48ThWJlTRvryj5mFG04UQvop4vK0BbI3ay8c8E2nOo3Gjz\n7E4xR8tVy73AY+LvzmDq9PhVBp+t9uheV0VbYAYFFFT+vNU9MuDDcbSGPIMD\njGRQ\r\n=NkDz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAdcfO9ekMwjBVPrf6gOrFMqEPEv4zCP/FFAaFVbpqNRAiBYF5zF0z8MyBhH5ve76xakN0Iw5XpMY8eg1MMGtSgTsA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200622151907_1592839432143_0.6361157552773147"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200622162901":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200622162901","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ee8c58ee0dc3e9d706528c59df6482aa1f667cbb","_id":"@atomist/sdm@2.0.0-client-core-index.20200622162901","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-G9yaF+xMrBWSIOF380PisGzF2VSU8yMI+JyrXaaeJ6dAvqqQKuQc1ysJIZHFn/DlU85Vziq5o9+k8aqJnUXSXQ==","shasum":"686d4ece5fe5462d14b95d0b4423d37d8c00a85b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200622162901.tgz","fileCount":2073,"unpackedSize":8935166,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8N1fCRA9TVsSAnZWagAAzGYP/i28ZN3QWCEnDXJfI86q\noy5nhFnxtk7e8YOlm+E/wK7KSCbzGSuJRYOmMWwN4yQ3qVsjC0emC1WMm+Hm\nmOek0nVOkTzbpF3Oi9G2pc4f7K/4dlCWgxncbUlHuJYiZjcL6Gt93GvQfdlG\nB2aDpt4CUscTOPhQxEjl66oBg89aKNLjS8Q6uwG84lVQWdpujLtJxVwPdab3\nW1LKgf7tL0cwulwq6GbwiG4Sxc7/kCMVQ/FfKwqQ34Ptv+VDDacquXHyPoog\nX1b2az+HWtCOp1c6Shcwh1gWjux9q7nwxzs7+HqOjeHYAzSgwreitvFEajk5\n1Y4IRnwWiHkOlbjlPP0VdILFpphNNPw5qYO7kCjDNf4hyN+YaXBIQdfFIgo/\nPegRyKTnIXEpSlka2pSlS1e6mb51/tWaZ6mQIozIlhZ89oHS7r6J2QmemiqN\nX2Y4UaLQi2op0R9kSsq38CAtG1znN8vrPsqKylDPSRYi0YyTw+0bCpBXp5Oi\nFuCTBZR3xe3ShUd8MwEXcM0YFdzmiFxh8/8lacUKTHh+9lM20ZH4RPaZk4yL\n6vzUlTyvPpqPuM/TDL3At4wbESXon7ctwk0EDgpaXdS5/HMWBRsQrxur28eJ\ns4pbEKNyZqeHgE3Ok45bzAWv7iWcLMsXH/3Ez89v1XBc4x0bA54uW4zFI/T2\nKRZM\r\n=TYh4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4GXOqny9gazN/DATVYf00YNEVJvmJ/Lsa0y1rrgbSSQIgCk2CLnLBqLYSxZMSG6r8N58ocmEzl4iP7+Xa8mpYftU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200622162901_1592843614897_0.533280552715236"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200623155820":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200623155820","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"bfbde7b5881687d267b4c8500c14f552ac7138db","_id":"@atomist/sdm@2.0.0-client-core-index.20200623155820","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-UfJN+AMShkaEE85bKW44X5lqWuF7J/ezt3CQHhaZuqQftFFhwIhSaTVUY8w0F4rKEgYt7km0Yfgd/9s5NBJUMw==","shasum":"b94d7ea2bfc2f29230e1e45e909b11b057d5b4e1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200623155820.tgz","fileCount":2111,"unpackedSize":9028009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8ie+CRA9TVsSAnZWagAAaYgP/R967lmr7oeFpt3TmjSP\n6kI3FR9ZB/wR1jcHQPFHMMl3t+P/LjnFtgUc8HgxCUxXZ0Ct6xYxBZ9kuZIR\nY8Fmt9qbTrkaRwQxU81DSklCgpKKeY3vkYEK4oq8+g5bpGX6MZdkKheXUXxU\nNVV12rJmfffY6qJhPcNHkXXZ5Vc2RuiG5FXfwH4pN35nRnCdkZ1PcVkOa8S0\noVvjX4j/1dKOimTnERri/N/TCfCagC42moa/g297hyOfsp+yySBOH9J+ppNi\n27lOO61KUw2AORoaRRGx/dcD2chPMYEpAysTlCkJbLuuMpYN0ujyqkTVYxrX\nKM+qOngKGGqu2sh0c8JkpN3Pco9k/fpOuhy89uFZaJXPqe4AsrStB3qxt7Lk\nxgATNRcXeUJGdsDJASpZ88fZ2DyrwffhFqRfV4nB+VVqRqwAdSF2eeLfIEZ3\nkR5v8Bl/t01ChsJrmMndCRFZ7U1tTAE+ggWkv9ttEndnzS2HZQB9WR520K8J\nrMtNa+Gnget+AVJrLfyHOrDHurnFkYRBW+UdaskoO42wKWIGM7GHu2msvWtA\nRvkoEevidFEan1CLWx7d89RjCSin6rvOjtvk6aBOXrF2LLPPr8mWw/AmeWKJ\nN64iXCt/Cbzzgi7AQ0L2WfslhbVkHzp22D3B1w1pQLtcUgWjZqw+GMPWF/Yk\nhdJL\r\n=9tkD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxJnD/xznLfLWAhGSYQ+Gq57qCiEi5j3ucQCm+yNppMQIhAL5YyHjH9+JJfSkNt2clUU/KGSalH3P/tZeYSgwoHjjM"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200623155820_1592928189308_0.21193084735318246"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200623161140":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200623161140","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"212cf8c5f64eb25f14807110e9a382cbfa63f5db","_id":"@atomist/sdm@2.0.0-client-core-index.20200623161140","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ntWYYqz9z384aDC2+nWvMGk27mmcI6NFOnzbzX+cwYap9AE8//9UjgB4mrpBuQQkxrC4FYSJIcRPtahGEnfQbA==","shasum":"44a5edbea260962b79a46f48b2f099f3ba0656f2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200623161140.tgz","fileCount":2111,"unpackedSize":9028009,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe8iraCRA9TVsSAnZWagAA4OIP/0qdXMfURIu/ZA4eE4Rj\nKDKi8K1DTz9oCIj11M3iiQPmzv7yAXMnsg0z0BHEZlcygPH1KoEuwwa1j0Vy\nb0baJw+3y62V88KAV/oSG0RVc/9cnxsSl2vMkBu4yyZ+XPR5oYN4WMbg8tw2\nfLM+OrzoHkLWiExYfcGerZJ+RW92lCwrJpnN1Cc1zYIX+IF6oOfQIDRROuUp\npXUf3E4REwlP7wSAfQFn2DXsx3F/XLUuuxVnmcLlccjGtvgh7uBIp/rdUfX/\nMIagGOsSRaOJPShzo8GC/HVAwcgKwDCrhG6MimpcW+w90TvtiLmXwLUrL/dY\nzPy/tLFjp9MeEAoRA1UYyeu8DEVey/bhRQcW5pHvj5dRReU6iujcLuqZHN2B\n+jJ1PxJCmnR/xQquIGFNOTeW9x/2PkW2lVBS6Qoz+VxJHnyh9jSkJyKHhFDw\nAoaIhcSiI+3Gs0XGDiOTpEcvbhZqxDImEN7vAV9Tj4oFc7rMWQJMNdlMi/fH\n7zYQD8TCVN3R3IsxXNEhWMTQNtrV6ZYcsQJ2jd/XfolJRz5OoSJcoZEEd7Up\n1zfVybDfsvap+jlM5GXsdro3l33uxYGt/J6RbYcMnvM7VHBbBH22wTDl9IIA\n/le1xV1yLD3G1M0G8TYfHp5WPim6TGU5Z3IAiG/an7LgxkQtA6/4lYl7K0pm\nYJBC\r\n=Cf/I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFGBC16BCK928zUnyyCESvb1cAyOtj0oR55Cp2nb0TGoAiEA8ROUZpaIhWf/7kyp7ng/LlshkISml3btGw++/OVTCcE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200623161140_1592928985844_0.7996576876693993"},"_hasShrinkwrap":false},"2.0.0-master.20200624115947":{"name":"@atomist/sdm","version":"2.0.0-master.20200624115947","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/automation-client":"^2.0.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"2.0.0-master.20200311165330","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:ts","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"7cde2cd222fa8e0e36c31f704a33f5280fbadc1f","_id":"@atomist/sdm@2.0.0-master.20200624115947","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-wmVDizR3PT2ZSQ0CGgli2tU4tYcCVzmW0IwDxOJPFTWp9iCAxXCr3vSDUKoJmNOoMk0Txd8G687HyjXBAl6BMg==","shasum":"2c5531c9404f94aedb0ec194bd7ae36094044327","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200624115947.tgz","fileCount":2054,"unpackedSize":8837439,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe80FRCRA9TVsSAnZWagAA5wQP/Ami6Amme9KsbxoGXOxF\nP1JPQWaVt1W/MNCEkdWF1roRQ37NTGjim7hLgu2U6YbwcX3vEJzUc5Q+wS0N\nr4sIsO9OwVsj3yZmaKQrgIhS8jfxXILySm71ePVeivwuMFvrOFS8WCpUymVj\nMnbjN7wWh2+WYWx7YC/LUHvjj3/rd+kElVv07JcWNjYaq27mMCcoIMrI/DHz\nKVrxl//g7uQzhRDd9KV6PGbnim5PISqYCBdtNzJjy2vojZnc3gzX+CnPez72\nmf/cTisLYvvaCre6Z3n1eKWs5+AiOF/FTGAt5IBBL05TwvHdClvQUC3nfEdO\nZPLKLPwoX3mRfUL+8LhhkNRjKABg9c78egCgiNKHVAsVN4HLabko4PLzoDsy\n0P8hlMzNyOMwPeSmJ7cWxP4929MVYa5LRdeWSfNIykG2iqJO9FovnoRm/on4\nuPqqkFXjAc0066rwHT4Z7Nh0Ol2hFUUbrIGpzaw8NcKJ3wIPI6F4cEcwS3bS\nmPvhqpWN2he1tqMVCtr/qTi6K+DabceKOrXtl2Ar0Q2zHhKVUQfohJktaBtz\n4R+ojMC/JYTvKeh8kn5m76DbgCCq0eouQGs9NFpeeLFGgQ+NLvOznbXv264u\n6F9FdxM0C69OHII4kEfnjCgMPrIht8BM5d/xt5QMFntsBViehiL3iJ1KLApz\nZ4Wr\r\n=Glzn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICSm7gtj7zXAUlqGLR79EqvwJFLDG15FpkELH/1CcyXKAiBQ4VEfqZRMUvdFQBPLTC3UCrSEqftwwD0UBSkjs+oVZA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200624115947_1593000273080_0.8942125180229303"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200624120353":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200624120353","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.11.1","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d4114bf1a0fddaffd321a8c4ffae55ad54b991a8","_id":"@atomist/sdm@2.0.0-client-core-index.20200624120353","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-L3yMkOK37PmJtjqROIAviHlJb55omuTC3+AyzuXFRFaON3xKCSDNLgqxxMVejrnjnvG7WRspzb5WULXXOfqTWQ==","shasum":"6a5a0e44c3bffff6c921ad610d1d354ca7e4e932","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200624120353.tgz","fileCount":2141,"unpackedSize":9035113,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe80JMCRA9TVsSAnZWagAAnq4P/1gGVZXdt7Cwq/MCZLu3\nzqMJSxAMrCD3P0jl8x0ll3OJQ0uQW4ak9vwdsRajwHMURRvDdoZF1LEkAyCX\nK7c4kLBZT/Sm3MaWV5uCeHn5E3FiJA8/rexGLgyi0EEj6DiJMsDaPNh6tGRJ\nsqLGtEyAH3xGP/76zw3cyIqUfioa7x3b213/LqfuRuWN47Skco+vccY0HYq7\nUwQICb7KRUP8kjX7XoARSxqJIY+bPZXsL7lTpU/VeJUkxdnklctzca/4KDPc\npGLz/g2g2/h1jW8nQXULKTQPDfn8btc91gGLk6R0yya8+zLbpNA1MoCuJ3dV\n39yVJe4X5tvS+L7sN4RqUEUqwnvEL2JBW6fsV/RTYA2BBWnSZs5SphEDVEKQ\nyNsKg/+GOvlGWsGv4R6hySmI6J7dXmZA1gYLsbk2CDWES3F5naWnjOToSWFk\nVZVg52r+gnPINav+M38f1FzkTT6DjiOgXz/Hx2DG/5c52ePLwQ434fbrP46N\n406j+ZGGfsuRdfAe8NH7/YmfAwlLk/8pGzboTy9PdlJmWbUsWE2kAvmwQlq9\nyjE8ItgJBBY6gX/aEu5kBiT+ll7njMPb2dsVWNcK+9DmyGXX4L3bJ1z+ofgp\ni1IVz82lEDxNznRi00qpXnvjKQauIF3DuyP1NnW0B3/WAu23enIrvYU2eQ6D\nGeLs\r\n=mXcg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDso91+DhqrLuc2hegSTa1jgpwkBPQmOHkLpinJCVoPFAIgNWwuBwrT3RtGa/0Pl8FE0sbbQPRHF7YnzxnPnN9mWfw="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200624120353_1593000524095_0.5972852573700609"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200624191441":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200624191441","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^7.0.0","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"88906919f6b739812e221ee4080780bfce515a9d","_id":"@atomist/sdm@2.0.0-client-core-index.20200624191441","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-sXKELxbVvUBXb/ekl/7xXlJaKDXMoHLugi6I+kSWyZMXcUpZ47mXLSjLNpj2foh5WGA2eve+bU880zuqXwbuFA==","shasum":"653321e91c7ff8e0ea56c0d1342a27afba115b26","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200624191441.tgz","fileCount":2141,"unpackedSize":9084464,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe86bJCRA9TVsSAnZWagAAYfMQAKF7CRDzEcbjnC/9vkvH\nKpHbcqYMiFVdo3Whu2DinfZpLw13veEXoPcEWbBaThnyHf126y6lzTDTu8zz\nNvmIwWvi3UFuUmjicT3H3mSrgorQHP2ZHNZLyTBUpJmfZJsrp5QqphY7P+pe\n6V7jw54U9i80xrOK4JB/bprbXXwj+1pV6HiN0L0wl0eJ3wss6Rd8YhYfwHFa\nMU8BZzveSm48QnQchKBbKETIS0yHqv8u3XeuhPrL8IxkB+wLQSRdEleQWIae\n8RVFuMcy+WiqjXlRKUYVNmnOThE2a6AZJ9l+8ifSoTIHh+69IF4+eOJEfTbj\nAFY9sNObRZfUEv2umYV3rT/hEynMci54U6P0z3Vax2zLufeSogZQH6kcogFt\nfCBkQ8qhWPSCxNVSUtwd1h3fzw2ehcvQTDRju7MnL6wb7fHJSY5fR0816T5F\nKzkdGVzg5jVmSyJNwBklA392G94LHgyEr7feUHDgXQLJdDshYU4NtM/3jPs5\n3pbhB1rIzDiGy0pDc9zYc6btnHwr+Mmpholkw7REYO7d9ZpuTyAStNbYMG5Z\nDLN1p4ic6QbZl+LQOg8Yqxh3APUpAOqvFbpehR+drnn0B3T+964JTnrSsGif\nRn3qG3tp43c0UJ/ubBIPsnw8+2tMTBDC6U2h1eo65nWC6R1/tCKVp0YEdQbE\n4RFs\r\n=O/8R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHRlnKxbwAxlmoxQGOJzZnNo2pesNJSfcvqZcLTjk/yiAiEAhviAc8lGYAlMcEHBZnjBtIzW7/GObzfdpepAxbL4ioY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200624191441_1593026249301_0.7296447031689177"},"_hasShrinkwrap":false},"2.0.0-client-core-index.20200625184448":{"name":"@atomist/sdm","version":"2.0.0-client-core-index.20200625184448","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"0bb23171a48fcec13b48a8533dceab5ea56c8014","_id":"@atomist/sdm@2.0.0-client-core-index.20200625184448","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-BIC6pqZkMj88xHmV/d2HPFO78so8qhi8mGE4CFShgGmo61u2cdZfXmPxK+lHTOtDLAzWNBAeOj8O46y+9f4mYA==","shasum":"e7ba118fd338bd05404520a3335f820d7afba7dc","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-client-core-index.20200625184448.tgz","fileCount":2139,"unpackedSize":9085101,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe9PG0CRA9TVsSAnZWagAAPNgP+gOlfvESi7ii2E4xPyup\np392NAvNE68ms1P1b3FvB2X/qU8fiRqi0inQ2pjey85Y/vm4aS6QsrS1MbvI\n1yvq689O9Ttq/8J/0DfwLWWZeO0yqVkbKRI/s+Rt2OyDQWh52wd7rfGp1YfE\njzzA6zAN5R027Vkm2VlveszikxkVVsk+yl+gFHKNDyrc7/tw+SnA8Zuw7Pyn\nn2Ei8fh7vnYJxvE+UgW60hGZ0h8p8/3pCVNjfEqA0RF5zP8wME6mPeAebuP1\nn3Nezel75zD6tWxfkBRWBICR2XimrUI4fUc13A6oTzpduDJ3bpFRCmCBPBM5\nNdlYoH//ppJh4qwisiHbM7J+NwVEjM0AwecEwoLrSdX6DUGMX53e9yGrfMiI\nJ6q8YdfhrDXgj7Bf5myVgZiKXOsa4n+qpvnCAMHyExb3T6gM+f8OhExg6e+W\nmEQM+dBc+YbmVQl1yorvAZQegmNKKu+2humPqoSTKNOo1hx7ZzHRwMw8ad4e\nEl67RCeOuz5CnqDFsKkCpGdKZG1qzM1MfwX9l/VRH5tsnjf7VHoBbD7QHXbg\n4dkHHr7e2YFsOkyPzjDZF48t5LYCUfA1w2wY/m+4rTOYfra/Z8GH4pmclVL4\nquXHHcfPhHVRydZ5yi/XoxwvmftSxsS05cODFdWbHRt0gBTOWTecLNX8WNTQ\nfpAQ\r\n=Zgi7\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDTGKZ0S+yrM4hQx1aapZYFvnyjjs0FnTDEdu7iUefWVwIhAIW16mSA+8ZFAd0zN9J2UDR2jijfaq0b9g07kQQkEf66"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-client-core-index.20200625184448_1593110963528_0.23098347095354632"},"_hasShrinkwrap":false},"2.0.0-master.20200630122714":{"name":"@atomist/sdm","version":"2.0.0-master.20200630122714","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f32774216e1140d254b08eafb905341384857052","_id":"@atomist/sdm@2.0.0-master.20200630122714","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-gaYzmGcuS1Ax0SUlCVjoZ3ZQL0vGnV6zgPtfnYHFJhidbeN4FoE/68asXDiBUwd0vDYaz0lEBrdsYW7BxpzqEw==","shasum":"da40577019b75095e2f74ae4f3d820fb7424c1b0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200630122714.tgz","fileCount":2139,"unpackedSize":9089761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+zEgCRA9TVsSAnZWagAAgRIP/0HxkBQIiUrgroeLEBzs\nDvUe7gLzpAutBLrZAKEerVbLuv+MakkdMIyPtTVhmMxslbWXYvcuzcNwf9t1\nTKMlHVANHfq4gyzrt7cD2dks1raIPvU7W4HNgID1mXHp0KSaSNy7EKV2KzOb\nVg66QQXgGUc0a8ZeOQ0aGJ0Nw/FhIHH3grXyT7CWl+FTvWmk5U4csdnufsY+\n9oiqHaU/yJtTvuueHcQna5ztOy+zEVyT8iWGupO8n/DnFje1XueBBpy9ccy3\nR0KMXEmrryBVz/+maasSB0EtVWprS+dlqNGx0pAC8sG1uBWXZiIKQd+A5gFg\nWvregpYm9vI0Pp1odu6h3udOtmLF6WrspPw4YWx14e6v0jp7kNCWqtlDi1if\nwz+zwJYFpoFvwqifTQNXZmRANansh60CdrCUkIxGQ4CpffZu2M9FGMO5cnBJ\nz4HX6FpDbZ4aHrBFZb553Tn8IieS1X9PlV7EVu7zJnyKGeYXNJXfxwROohzx\nKC/HEg/EDPakvUfil4QnW7oIGWwENvILR3/SzQ1SRRk6BdkjTJFJ18PNzqjf\nuFVPRaxLXIMcmy+sLeESDLNFuAC10P2TbE4a3KnnPCwz8Pmkw4C0pQ4k6GNu\ne1WqP18C4M6Oh44Nf2rY9ec+ZsFyWLcBwksC1vqGcbnVlbAVqdTOzKK5rj3z\nvg2o\r\n=w9l9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDFOTo6cgrOtnksjVEFfZWB+yxeDQMyc+tl03GyeXkDGAiEAjN06NHfCDZQhrHgR6csbvv3SXuGizIiM9ODcTEcRASE="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200630122714_1593520415686_0.20696603089078858"},"_hasShrinkwrap":false},"2.0.0-master.20200630123015":{"name":"@atomist/sdm","version":"2.0.0-master.20200630123015","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200429110358","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f32774216e1140d254b08eafb905341384857052","_id":"@atomist/sdm@2.0.0-master.20200630123015","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-V5D8jFs385qdtR9SEA0MzJ5yMLkxufimo9o3MZQI/MeYF+PzMaSwNPQUEbExWi/q40U2LDNIJAfMl+3hQmSp/Q==","shasum":"f1e5ea8dbe461fc82d26f2bf0a1ba7a2032ab195","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200630123015.tgz","fileCount":2139,"unpackedSize":9089761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+zGrCRA9TVsSAnZWagAA7fEP/1X4HdtxELr36GUJNdAm\nqzEsFC4x+HJ/NL4bNaqnO4tbn4f8FlycZJmbvrJE82iHdhsMW3vVgMsNMhnL\nQT6B9wqS1CqHblVLo27UjfOjrneqOAYMHQSgo/UpeyqYOZ48oSUsWIUzmV7K\n1SJkav178BY36ePkBxunihkFPPR5ltEsOYCE3T86RmfoA4yDIIla8lxNwJbO\nRdisCpGMmVrAADBc36AIka7OqL2xyRHHAd0QQwTnth60ymbtkU8jHOyiCzmg\nBzW14XF9Mat1dwzD5ebKNLeC6RpV7ObYLOVPNO7KGV6TB0zfBoDGQhUEMdEv\nbgrxQkTMy4LXE7wqNM0RdJXGAEBWAku2wxaCmmiA4SnWGPd/rTr4imSL5K9z\nzqQHdtAmy87j0HjHLwzrG5Mg3thZiA+M/2AhX/T0W9TicEwl89ctD+66BOwG\ngPd9rYX8Sod/weKc7H4egVk+N80E949MuBEvveDx7uSpGWmpfsGZ007jyjGG\nWnGhacgZa8nS25w4BfGB81UTnvF/9PoWZgoSIoAP2F0tjSSpSk1S276a6xT7\nPIosvn8m2tfTUTdTxxCnFgznRC7DLxsvT+o+uFMsv3O7hhVbqog0HpuXpR7j\nTKpn6N+9/11U1GmUkFlkDfO90Qv+/vrz9A0rDvwMwxsrPvkZ+NrXMLsz5TgR\npKyP\r\n=IJRz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAj1/wRHlABTdE7g9OvpUMihdpxI3nN1TZNPuKXsV+p7AiEAvv/npfkEIEuZdMi0+iTgpd6MtFCsyuP3Fbg9vjdYnuk="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200630123015_1593520554611_0.4700653137925861"},"_hasShrinkwrap":false},"2.0.0-remove-deprecations.20200630212333":{"name":"@atomist/sdm","version":"2.0.0-remove-deprecations.20200630212333","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-remove-deprecations.20200630162723","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6c2ccf1b87be8b19c2ec15e79e9486b601d0b0bf","_id":"@atomist/sdm@2.0.0-remove-deprecations.20200630212333","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ynylT36SXPB/NrXsWoLbR8bnf/ACCEmcGYrHwlxq9jRfCAZvErrSXxL3ANULZFK5IPr71kLOdZjYAvuSVhn5SQ==","shasum":"ccfdb118d62d8efc649fbd49cb327ccc15379f43","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-deprecations.20200630212333.tgz","fileCount":2119,"unpackedSize":9036005,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe+65yCRA9TVsSAnZWagAA0Z4QAJiCYpJ1t65PD6XgC/pk\nuYy5XN8kAulfMaCRVmTPpYvWwR/o9p7kwsXgTOV3WaH9zMHqmuOC+lWdkut0\nTDU98BzZaMEY2Z/zZdZzhoCaxAK9pY3ysPT7wYqvkZYAxMFFvav1llf96lUQ\n2cwNQd27EEFkQnOAIC87Jc0HMQY0rkUgZxPHIy4/obpI9ZOtDreMJxxyeTnJ\nccuKFWVgxih8GyFbXbnLC57rVXFq0bzd0Vk+PF8acv1jQWk446N0GZC8Tvcs\neMO6Av1gkFbzaEI6YcpMiOJl0CxnubHakAyAHtqsgIc5MMA7vVW/KcBZnVVg\nN90PCz7ZTMby76IfavPjctqIOLnGwRNuJGcBw1YFcR8MkCvA+RDhNo/s5R6x\nGw7f8oF2LanSf1yiLJ4idSAH32IAWFYOrClVsjiGg5vk3EzhAYdytY7hvTam\nalkoFljR2DWxwuMfdeOEIRbunPwHg5mFxsxxD2TJtZaxNOarjH/+JFq7RU3Q\nC53sz5FcxCZj5zZPNYyhGtUTYdYsBOqRRfmvB0x6Z1wpFEJt2X4jYGidQ0Dn\nm83LMHmu6tusQF6iMT0CjTkITUKqwxCQDgm9NiHVVC3YC9qzkHD5QUVa6Po+\n1AQEu/3NV1nMsKhJw4sOjnc9eb47DYCSdEQcYmnPxIkqn6iJVq6ZBqXF72RH\nF9Q1\r\n=khRh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFn6zvIO7NGeBQ7/vgXIj+DUHPotG2X3tpdiWAE1YaqlAiEAjPCi274dOF0LBXRs7sTRppxuMZD2ZJnas6inDQCRnkA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-deprecations.20200630212333_1593552497522_0.02367598862754572"},"_hasShrinkwrap":false},"2.0.0-remove-deprecations.20200701142824":{"name":"@atomist/sdm","version":"2.0.0-remove-deprecations.20200701142824","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e4766a91600fbd2088fa3b2b44f21d404ec46da4","_id":"@atomist/sdm@2.0.0-remove-deprecations.20200701142824","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Jp9kpnlpeSl5M1fZdJWLJYq4bTcjU75PSkEh0xjBJTtnrah7n5FeWrFetyvuEi14d3cbYMywD079pI6MAbMstQ==","shasum":"44c9c9b0c7eafae61c4dab503c9647c3b936df4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-remove-deprecations.20200701142824.tgz","fileCount":2119,"unpackedSize":9035992,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe/J7FCRA9TVsSAnZWagAAor4P/i6Y9WkJ8780C5Ss5S+w\njweJZAbr6KifzGzbMfZQmm/uVb2iLGArVjI9vRHcQytaHsYb0YNY5OFODn+f\nhjAa+iCWMk6I2Cw3MEbcoW6yD9I2wNv95vzvqWsA5MVvWc2Y13muzq4ZiOll\niKaSzT/Ti+h6WKjlE8RPBmi1CKhFqye0ovtya+rqHFjdUh5ojJFc8XxgyaWF\nmjlrCdslNFLup2MJS4Ts+JFALXmiRnhfnjwaUfmerZdrPHwlIfSUV7Ou5hD8\nD93Rg63/tiaWQXEr6WqlZcaYObsn9YLOVr814wO0M93okapXyMO6WzrW47Sl\nUuB3WSVTzD1uRHkpDBPFwFPVVlcg3Z/sVlB3L3+5gWQKtiZaW0OKfonMNTJc\n+TGZCpNpBTCGD1jHkiU+Uq+l7y/IwOcGAHcmVzjql3BbB6JPlQsU3TGXrKKu\nAyCoa140RgD5Xb62wpEbbRA60eP/0zAlsMAHrB0ATeYt6t80zk5vQu0GlyXS\nQh/GtgU6jLWpar8pBdvS8xSIE6TaLTMTh6/ydQ1Fv3991dPH+c7ipTj+Qlv1\nhmxIOdyiJH/s0t1DFXnNruZxfIAkBf3PGvn2/DomUKv3S7hQ4N/6iMm/zOC3\n4xlUQfmSqXsJPjZo2LAygrFXGiPiH7MjfmKFE9qJDJyYQjMANgvG3XVG/tH6\n02bg\r\n=rKbl\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIApgASik7Qsm+Fom/A1cs+u+JqJWPSUNp7zBVVaatxaAAiEAoKURbnWmPKSDRuRsJg3v10rTsn9GddpNVtVc/APM0sQ="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-remove-deprecations.20200701142824_1593614020837_0.46229190110446927"},"_hasShrinkwrap":false},"2.0.0-master.20200703143732":{"name":"@atomist/sdm","version":"2.0.0-master.20200703143732","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"99bd4a149dbfbef456d5b21b93405451e5a1baa7","_id":"@atomist/sdm@2.0.0-master.20200703143732","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-StLP4PTfk3zhIC7f2WpwCilKsXy6bQL/0mPThQW2we5exRgnVZPyxTGKPAQcnmGhX+SiZeFVw1U/AWcrqIDagg==","shasum":"b0b890e3a5460b099e62d0fcd76eab86098fe62c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200703143732.tgz","fileCount":2119,"unpackedSize":9035604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe/0PsCRA9TVsSAnZWagAAASMQAKJijm5S+GZhYOue0OXl\nZeUQgvgKS4E+ksi+nn3br3GG80fmcGKpO8iFiQ5R3PpqFxLfszxOcIG3WxkR\nk46Fclh7xNkdDZZEblkItHseZV2dj7OpCqvKy9nR/vXF8H0SyLUuYPZ0nB9e\nbib0MZJH0ELEWivGXOSaawKALxavzSRn3m+HhJhkj9Id9HHU64RVSPrnRTdN\nHnJzAvHAm1nWJopzgMZl3pkWgo3C4lrzfkZhmy0KxdzZl8PAvTttgqOJHK5E\nnqIMFWNiVEb/BVwZjBPMzdEY+j4twjS+rKYhLhVDUfEWuulfiVuKngYD3Bil\nQvxODw17jG78dKqT8P3Z9BemssCYFrV0Z04fYqpZW5xDewFn2rU/9S4//Tn8\nB8qAAmvx+PFUQSCMsyueBumaLhgwR1gN6RNvjzWC23Zj1vkyMMDCw797Cus9\nbCMQHAbBNdUZrCeJOtCrTDnivo/lCYvCZkG4FFHjYtN6WCbGxJKitU7GhYNW\nZAYlIl00jwBLDUXwBo9ugpgYe4dBw42Mau3YnQH86MkOueVf+xXiRF0Lq4fQ\nR2fJW47LYUCkuzSBarD6wwEzg4a1E7b5rpjmOGrk0jZVmlPyzR5kaXf6G/Vv\nxTlipk5ni3PtDYcyNRDfuVbskyjJquXOxwzofi8vrlUSUPg1q9iSYiYdUlye\nanI4\r\n=6WFD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEQxWzjPPoyH3ZaXkeJVBBbLbHAiZQgxvdDomFA1nYLRAiAtsB92c1Sd74/ztyEByaBvNGKODMt24uzqX6CQewjSXg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200703143732_1593787371481_0.558390797367547"},"_hasShrinkwrap":false},"2.0.0-master.20200703143927":{"name":"@atomist/sdm","version":"2.0.0-master.20200703143927","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"99bd4a149dbfbef456d5b21b93405451e5a1baa7","_id":"@atomist/sdm@2.0.0-master.20200703143927","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-OWz7fhGt2PRkrup+70smtTHBT9RvTGLzmcEa32ZibG0kNS95KuAdys2KcTr8BzLmucsuP4pWEP4u342RWbB3ow==","shasum":"7b3445fe1a54f30ef13a1944eb6a7a0ca2f59fe7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200703143927.tgz","fileCount":2119,"unpackedSize":9035604,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJe/0RHCRA9TVsSAnZWagAAZ0YQAIV1mxCgL7iYXzc2OCFx\n95/UGi8tseR2qsy6nCqnzLAXjiWOR5SwLUQ1ExKkwU4e7ogEGpWZ5TUu0kNJ\nlEtNkmnhIN486Y4iJ6VGWTDa5CD5qgpOk2SuXNb4bBZnC23z82swoTbswGcb\nCuGyTcx8eQ8y9veq71UW4Vo9/s1f5WyyDStOS0eTxE/ifzjG5Lwb4ESfpfX3\nA4qqs6vwcFHiOFj2EWYTuO/14hGbz+ccDN1S8l7IUQYnbXYvVhIW2ASPvQ2K\nBSeq/3PXge4HPaRqUcGGulquBgDV2zFUr3vdDgrnGL8jxZE4HtgdePMkyymD\ny8UeTDsgfiAZbY13JTuiylujATlakBEAvnvUYe7d2f2CUxHy63Ha5Co2EKJ6\n87mCl5A23JdCFpMkvMhr5kSteqPuNle9s8wzv/Rtq1LysTFmZy3cuvw8oJbR\niX63bnxHnLyCNgPTsLf12x1yJ2A9SBjcaNqtUFJ1wB1jZbkAKuBLjXagoKTO\niwWx8smlbzfjwsALFGfd3ET1cUn1SBstSmK4NVytvCwwrqAjrL2BR8faTPnf\nHHIFpm978BMLJ1UVexE0Yy0cPU6yJA4woYBG/utbtrwNIekuhSMAVXnzW+ev\n6sKovH2GAcceU+he/JJUHYGce4RNfYBSnzlcKGkr5THW5WjY5pMcNu1NeuXq\nKhlC\r\n=vv4I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCpO2EyVa3JuMLA+Bnuu+I6q4DsvrXiZAG8VdfK22V7UQIgL10kEp7qfsxxoFZDyeVlNWoPClhgXKaYon7THf/iOYc="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200703143927_1593787462944_0.5933678954523929"},"_hasShrinkwrap":false},"2.0.0-add-node-pack.20200708163326":{"name":"@atomist/sdm","version":"2.0.0-add-node-pack.20200708163326","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"b844de8ae9e66dad51048d3eaafa07aa5ec55656","_id":"@atomist/sdm@2.0.0-add-node-pack.20200708163326","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-NYL07kcfCgA650yqjXQagkR0S6THV3t1oPA3v77AQvpV7sOH4KVHAhk/5IjP+W5Pa8ph9Xs5ecEWom/6V6awgA==","shasum":"fa4dc391ac4d0e655f24eaeb94cb6a50318b5107","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-node-pack.20200708163326.tgz","fileCount":2159,"unpackedSize":9143827,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfBfZyCRA9TVsSAnZWagAAfq8QAIpGWMtTBL/2MkChAZnU\nWh0uSrlhwavG1P2gVC+QhlXMzFwbGCJAxt8vxH24bmrM4PuRF4K7hZDUU4p0\n1ViB6KTapfidacTzbPPN4fYAOvFuToy8DRG99qWww3DpHuOqXaeu82MBmEWS\nnSMRU865nhZFaDKIzKF+KsRovcCPjfoXA/nhs5gOiFGDBNHsoEHa5ix+8Leb\nAsvicWW4iduhmjhQSG7joCdCxR5ComV9l4TtuLhIx3na5gsz/dcQ5NxnwGkg\nI2g8fkOBuAGU2f1ruVqTdoyQMuxXo+Tpibtd5roF+frBLa18h3o+qqZ4MYfd\nFmObBPneMbfN5GBVCk7JIa8ueFZDJpiZ8ddbkzCz3raW+8/Sg1qu2VRnCrAn\nSWj4OP8eB8/bGkyt7uPqF0e0Ydr5vCsSFlsHfC4LNr90vbD2YshI04UNSzZM\n0Zz2doGN2APap/48MrRmP+DKjeRTs99N8hf2HhGekiwRcaMRryZID5EwxfvR\n/Cqk7+OcvIm97XVb+SavKLxMe6DScRqnINbT8AVDKICU3HonraawSKCSSGp+\n4WOyaMKRVaAg5+mYSvii4ixEbkuxPDLhp7zLXUxVitc5sQsMij1j0fYTfdAF\nwxf7hgFYkVWAPyF7D5jO6ikI0BQYNTL6jfpKCrSw0Ut9TGdxk9SVuA7JzOxd\nejqw\r\n=0o4C\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFueQLRGtWPJ92zmE9OlvSbLEo6jZpl758nAIJOA44ZQAiEA3aJCbQaz6NWt1DIRSwojriz451cAzLBfBA5oXR+EbvA="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-node-pack.20200708163326_1594226289323_0.7430654224522879"},"_hasShrinkwrap":false},"2.0.0-add-node-pack.20200708210024":{"name":"@atomist/sdm","version":"2.0.0-add-node-pack.20200708210024","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2107af52dfc9572a5dea3731f149324608468618","_id":"@atomist/sdm@2.0.0-add-node-pack.20200708210024","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-q86aRtb6AyI2Psi9tTUqRI6VVFZgb+20sAuvl1ajvSoG/Ge9jV2dPDjHf9O/tyAYuCjGF/ujSH5I99nh2evEDA==","shasum":"9e9a3712e0e09bf87410fcd22b6481c08167724c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-node-pack.20200708210024.tgz","fileCount":2159,"unpackedSize":9143827,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfBjU2CRA9TVsSAnZWagAAvNIP/iC9q5mjnEs/9L5eiWA3\n6IBLW56Uhu7Y6gWSbG2c3T+7d76QCg8vUvNqhI8/Y5WosJfGfGOPPh7jrU65\nsE8whoWtAXcVlYhhM2g9UFVoZi8RMDsrHvOUHbyfAQ/L8+FjrWWxwRD+V7qU\npX8VeEECzcs9dr6e5Lg0ds2zh+z2SER4RBQ35q6YnnjRfS9t0huNnYyoWnTe\nEFTygmO8ESNVLOTwrxEWvWzJO6b3rLM0j+kzHHr95/lE+sKNhFXjV+IUQ6AO\nq2O++tzxbvmaj5QW6+e6Rd8gCnwFdzBGgJhbxlMNdORoQNAn5cvDvUeZep+l\nJBeeoTOm5nr2F/W59A96hwpJhmyJxnOqdvk9qt/+sh0LG0kiSDTJ3Bmz5xMN\n7FHefYWC7ABu3o9KKEQMP3o/M/S1bfJ/unIi+k0Q9bD6aBrcejIZA1nOzDYs\nC0BU8hxJ1p2FTQxjjHeYtEXzLA4zFLAnQJgwD7rPhL9atcrbONQLwMxacyCS\n559ryw6uruAySviARX3HoFR9TpM1/i6ofVie/YQpPSTZp/HDBSblnCCBRIzf\nz5FBvvLRwVV0mD8U2lLAkEU71oVRXRHmiSqAz+2wrJ+j2Hyoa2/PGGWaFqBc\n3XLh7EPQ9g5zVpT3N8p3Cy6yh2y7X1gNdvno7bNXv7wyZ0lelJlNLi53PQx7\n7rVE\r\n=q58+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFQXgnifsfMACApEES/7AxL2avTwYuMorJve3kIpdicAAiAlIzKdH0jPFwX7X4IaVdjarrSR8qxWH1GLJteZ/09wNg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-node-pack.20200708210024_1594242357680_0.5076548363902944"},"_hasShrinkwrap":false},"2.0.0-add-node-pack.20200709125731":{"name":"@atomist/sdm","version":"2.0.0-add-node-pack.20200709125731","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"50ce62f00da960f8b6fd3e44a4328dd0679303c5","_id":"@atomist/sdm@2.0.0-add-node-pack.20200709125731","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-P5CI8qYa1t3DjknGCor1u3xaFSmjxgCFfgj6Sxc0+AI8bZwEmj2ru6mu3+VBPY9ohBm1PVMiQc2OQm6tKuq8+A==","shasum":"736e181e3dcf195a11d63c8aeb0eb156de532d9b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-node-pack.20200709125731.tgz","fileCount":2319,"unpackedSize":9452127,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfBxVmCRA9TVsSAnZWagAAt7oP/j05jalR21+BcLOpnIF6\noqYhMXOhNBVih5yIva3VsEMxpBUZsZIpYLaofJI0+qj5MElvt2aM6+yMJYS2\n7kNwMp4TMQHya0exJ+b0nAjDamKhlBvuPDelAnzeChppgOaUyfHATqnqLp10\nHTXMfbflI6A/EUy/ScHeYkrcWoQ4wMU8+S2BK4m48lFXKGGv8D5MLrTkcbjX\nboBu2o3yi+qO+rRDlexgzRYVpaTaWh9hxJS/9BxpzJGhfrXeO+1jhiqsbvvi\nRdm1U+VS29IK7ZKYcDCS3MF8G8HS5nti0bfOQJWpDME5qi9ICvs9phaDWz3Y\n8vBJiHfzngkUmFukAeHvWSHgveiwx8tc6Ttt7g+9og+B+RGbfoJHF0pG5Vws\nOvpcHn5j/mZeDYJBLNAw/IIpCEx9xBmQe1GMjOqxhsO+o6rv41POgKMxDPU/\nPOQpjHPoIIfPquddCflpmMwdWSPgiFLHHLQFXuTOboqJyqWw2Th/zli2LHNN\nOxvorNVdFN0fA99OzQ3pLIab6wpJr7n9T96e/TccpnWHMqmdRW3NWi6IY3OF\nc4A2q7+YAcd6xKb2QIjj6rcCgrbm2jyUw+g9MnwYsAFS9zaMjei0bHMevEqp\nWVXZQljdHRvoZktkkWA7oL7I0beUprjFr7N4tKGpt//LW55SzGdAV74RGrai\ndY6C\r\n=mh/Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD9QY+o7mdJ+lfdtm+I/gTCX5+B+H+FYx6dDTRiFNJj2wIhALjWIzfuFmcYKpZMa8qkl2YL//7L78A06/dBK8uITX6B"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-node-pack.20200709125731_1594299749312_0.2593787990642724"},"_hasShrinkwrap":false},"2.0.0-master.20200710134535":{"name":"@atomist/sdm","version":"2.0.0-master.20200710134535","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ebcf10ce46c12df5fc23accd15ad5823c751ff0a","_id":"@atomist/sdm@2.0.0-master.20200710134535","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-9Pe+WSEceO4eT96zueYiyl4mmFNnK6UOJRBhpA7P2ZOKQvqWIwIZqTPExcuAm2Oi/mxRME9j+qfdh/lY/HquyA==","shasum":"0f9fe5a9dd20d7c8979728f079c7cccb1c6c4452","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200710134535.tgz","fileCount":2319,"unpackedSize":9457337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfCHJTCRA9TVsSAnZWagAABWEP/jP/jI08KIKhAiWbQrE1\n7OjlLz4B+mimNwbrb1ZXXQ/nApQqguSdumdBa3Q7C+MmT+RZfIQThv5clVUr\nnh7tLppJStJyzQsWSmM0ASBy5DsAlpPlA5WY9C8y88EkG5tR1jkaV5dpCL4h\nOgg8/gshbzMFyfba2tO46bp//JS3Ql7FQc9EcI1qcfhUUF8VSjh0IzI2hm5D\nK1SdmoaudKYCqPtzjVE7E6qBr+kFJ9vGvg4w3UQGR8q0teQzmrT3+YBhggzs\njA7WnI2AeLSFaHn3TTlGUaLDVHm43r19UOG6R11/sgVSyWrhBUSbGpf2AF0t\nsIs2jROtP81nQooEbwch6iecioW9u3jEqNwm1Htp80kZM5V8FdVYZ7fJA7ox\nXH8SeTXwaMo9+fMY3UZlRkZQDMe84vdCYaV9c3bb44Xj83jhiIqUNfCB0A4m\nZwhcCmcduXSZGPxk/NkFt46gKHwkDwx2Xp3U5HswN9wuiZJjq/JWi0F0DXZH\n8ASX4Omv/93WG7nxg2A3X6AYTR2UWoVPcUU/Q4xpfEk2HAlO/gjXDozyM8KK\nGeoJexnuT8WSCLDZnuqgI9XKP1z8s1qnIO0CIGS4RaGQ+aC9E/3V79UAcJ7K\nQ/q7hVOyo0Lz3KuaUt+Z2pkALm9vvoYRUZD5+DBhXdDFyaY4fXS0nG0Donhi\nK8/1\r\n=P7Cf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICXeyKBy5uecsWDZ9dOaPFEa3xbjaMDKr3SNOT0/KGmOAiAfqR/BjTFwRISW7XqhMEivj571dA1/552RR+stCqm2zg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200710134535_1594389075160_0.45662243980833894"},"_hasShrinkwrap":false},"2.0.0-master.20200710134728":{"name":"@atomist/sdm","version":"2.0.0-master.20200710134728","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"ebcf10ce46c12df5fc23accd15ad5823c751ff0a","_id":"@atomist/sdm@2.0.0-master.20200710134728","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-P7kz9OD1q/qkfTispbIQrg1py/3W1OJ/dS9oEOlk4rQPElruqV5LKNPk9Eb6KsjpCQyt09lG5+aZapMm4hVGig==","shasum":"ee53cccc7a2220e7cd4f58bf4630830e91c7defe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200710134728.tgz","fileCount":2319,"unpackedSize":9457337,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfCHKvCRA9TVsSAnZWagAARXQP/AjSfdNajKQf02a8sCEs\nPVSiNFRYtEqSmJf3WXYJU25VLP0sre8mP4J0Y4pFeY475eZNhLQywJSY5FA4\n/ltQ4z3e0wIx7z9SD7jhEcf0w9ap7mwkB6FeY/MA5IOfIt6B8HFZp+Yylq+B\nYIDd0bsjJu/i4M12cQf4dz3BKI1xKyVrSPu68ckIJ4XWmm9Xeh86cHqrWo2G\nsYuQb+R2x/qamjcyF07wMrEElJzEU8OmtG4F35wvsfaRM+DlbxznhG+T50NH\nPiuB3bFBhtdnny3sAl3Q1H1X/M+lYQrtUuPxermzGms6qIxXVjGJXa6LJV8f\ncgu3zIVoMvcE+/Xg790VEGZlEl39ljwFNDY5LfonM97yVSJ4GhFLteLb+/EL\nfetB2GCRUUaPdg9StjCVRvcqrV5yKJ6/TnM39GrqNMo/A0Gs5NrPT59Fpy6X\nXno5LbBz4oyA97W60xAFAWeVM/r2yYIPoj5j1ZyqHZjqp3/oalUZhZKJNywF\nigT4HN8J2c1MLh9bnN6pAW8uzTRbV+gPwZPumjb31n2YW7mr5kbE3r1P7VXz\n3mhYFNO97TJE/dH3311doA0t5G7CeokvEsyJ/nU58B62xodNwh+IsHu4hjbE\nfaH6RfTvvsbkBUqWm6Kgv4B9jZ5IcaYm8D1GpiYqS3zSy3/3FJYYrWBgy4BW\nGADF\r\n=xtZM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEmf7CpX6J9eqmRy03MDcBVipT7+MdUMAXmr48Jw2rCRAiAyK5dMk8wDKyenNn8AuM1w40CIeuylT/8K3AmPwVlLtw=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200710134728_1594389166671_0.9494065946880481"},"_hasShrinkwrap":false},"2.0.0-master.20200710165243":{"name":"@atomist/sdm","version":"2.0.0-master.20200710165243","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"301e90f4fef15d49165ea85d2ccf453e616eb0bc","_id":"@atomist/sdm@2.0.0-master.20200710165243","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-jeN1RJWNTINuyAXkgz4r9Q6vumNM465rVdAixr6pMBGs72LEF2kqAekfUyPoFeJTVH278R4ejSgJgkqxoUHzoA==","shasum":"b4b652a948f37783e08242602c8f6446c4d54776","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200710165243.tgz","fileCount":2319,"unpackedSize":9456710,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfCJ5PCRA9TVsSAnZWagAAJ7IP/RD+lND4nOWjFARVjR9j\nXkag1123ZjHZNm+nz0Zuu/jajQvehrBXlQNHsG+7vPPQv4njQY1LwM1OvaHx\nSBK7PrrDsyTaTElyEhQygH9zs5BLWOcg1LBQjYkn0GnLqOJ01LmxUqvVwkOV\n8+A4SF00UUU6l9qZMkS5Ty0ghhOKCNgukINGSe8br+BxNTfG1RoPYLGBsiyJ\n7perJkDcpUV1xnByhxS5XusvwGsWaEWeDl4TLs0kYSz9/XpMOqrEP85Qj0di\nopB+WdC07xkfhWwIzS1JjexPrkz+kWYUZn4pPc7wJdURguTsBFkK8YFOYS+O\n07kYYRtbKc3rfBl0GR7pnhd7O1LGeqvZJXJVHmb41uem4Fw87bNlFcDF+J2y\nXkTjjK3SmrlTiXOv++AUYiOz0WvE0IjU8pt+codWRRdEzOyXNhpZpggU/QoN\nhPzSyHrVXR6ZMiq8DcbFGnG++k4SpJ3vCGG0hQtCC2betA4B7xGNi35+LR7u\nugUK5LoMnDn4Adp8QvnBbEj3WrIzNHUiILPjjfdXA+ncclIl96jf2ZRDQGDo\n90teZVdvwpTqqhW81xNPyr+0fVOMLTayqefsC2IYGNaRz1oVVngg8YlTtAM5\nmoNBBrzGf7ohgERLIB7kXpBktQTTGpIvs5SZjzm9A1FNPOP4IglVNjMsaD7W\nU5cx\r\n=APCy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC9mgL7HvSaen0hOlqseeOB1F+szHp8q54jHECQ+XzKpwIhAIY/ATUlA7GARU/0nCCAQyC2Z3UsVrkLfuuo5AAuHxwy"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200710165243_1594400335005_0.3265556487692647"},"_hasShrinkwrap":false},"2.0.0-add-jvm-pack.20200710165350":{"name":"@atomist/sdm","version":"2.0.0-add-jvm-pack.20200710165350","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"d56c7440523b19a21a77b94eca8946a883057371","_id":"@atomist/sdm@2.0.0-add-jvm-pack.20200710165350","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-SUFD/VIDMIgo33u9gLLFDXbYVBRIwbuRrhThjjwNGusrviEc4pAwR90Tysl8GUqegNe0fNGjflP2aRHe2jIIyQ==","shasum":"cee63b62d19d9f6a891af6b0532f5c56acd825ee","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-jvm-pack.20200710165350.tgz","fileCount":2434,"unpackedSize":9662375,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfCJ6GCRA9TVsSAnZWagAAK9oQAIVozsTjYlv/e5047p2O\nOI7E+FPbqtKuLOCurX4+/iNCX2KpQlaWKcSNtLj5ZAg0N58GpYwdFVsOQqWq\nmfa2OryEABTJAmA9ZqTUrzFLP6x+GzwUsd8fO5wFGmm5fJPFu2Qm4+wrZh3L\nsFDInUqeNV5pEcOGIp705vwKfq71CJsFFPKPFBxTI23VsmlYLXWkC/1p7Zma\nOiSLi94A727rfAVq87N2KKg2VFzbb+aYifGd9nFSsLx72aU/gGt7Et9zn8Ik\nDSkh996JMTpruEzA0ef5osKSj+p6Fg/pkZsrrGUPx62zvIBc9X/QwPlreZv8\nhUIMwafg0tkwqRVmOELGWxpeXzlgk4uj4mXcWXSpkfbFjmLNYT5n3IAsYMXl\noDPEZYU46b9WtWFjEPosfbJ/ChIRqsPSo+qbn6CsNcP6fEDG+WDQYSduZTAQ\nxaGXzcF96uirLRfTtyI+HnjY22rIlTqhpa4aE5ZXoaHUbB+T7LDtoJQw9S6r\n9B6iFa4tGDQVL2f07v4DKwWPMlO0q9LPblRu7tDsHxw0hFcQ84vrzbanQY4n\n8t5f9WH5f/KbKHHKc1OqLKXOECOVoXLH+juswagi7iIvRe51b5OOW9KqSOlC\nzrfyPVIU33KLk2qYIjMVNHWI75XQ0QoF0OFqMW+EwTLZNe4WqKs75wQ/bTHX\nux3P\r\n=Tc2R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF9lFKvfeoLYQ2GzBHCjs/MiPQuNaw8yzZxbpjpa1HkXAiAIKSQnn2h3v0jd2NVf1aYnB3telmEOME33fdrfmm4WOA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-jvm-pack.20200710165350_1594400389505_0.4452805774401636"},"_hasShrinkwrap":false},"2.0.0-master.20200713192703":{"name":"@atomist/sdm","version":"2.0.0-master.20200713192703","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","yargs-parser":"^17.0.0"},"peerDependencies":{"@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"2aa2a612c0b24445213cd26f72015b423e6b8ede","_id":"@atomist/sdm@2.0.0-master.20200713192703","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-B3P4vOjLpKaFcAVMvw588xjvxcEZ3lPLuHj1hsh+CV8vgxMD1tA0dyLYcu4JF4X4zOXlk9k9dExsZLG8SEH+dw==","shasum":"a7bffd3719fecbb8e930b6a2c391293345fdf1ef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200713192703.tgz","fileCount":2319,"unpackedSize":9455563,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDLa0CRA9TVsSAnZWagAAO5AQAIdslL5lfQAzMz1FsBkA\nLh9kFVmeN85L7ClX/zr0+2zisEMLWG3o67InyElrcdlgeqWQBw//6dnMgYy2\nEwl78y41mNUfYoQa1EFphCAES/Yc+ELjqQpmbC4SH2i8KMTLrnRmHPE6uKvi\ndC8DgzHoMBlGipR9fzPzVaovHNf6UAaXuUGFxlqbHLFkltJImDQRiQLgLXBh\nRKxd6mww8HJXSgVFiWVTJCh0FFBinHXWA3z49SuEyQKXSWag22NPQ7NPlyds\nB7K8dWN5SUgaM7tIJNKH2u58okrqwEGvmnuYCgZ468Rp3p8+/a7aznqB/zaY\nlD4hi34B5vDKqiU29gEffsgDCfc03H3gjCm98U/uyY+3Fh4Y1Sw2nsG4T6XW\np6PBgSAOQANgC7KPJM//UlSbTd97QXB7K/hwTTLamcDJ4S4h04zwsJmaF49h\nQAu+Uv9AMajimPlwrnrrt47gNm3OfjRRMvQL39z3uckP1IRmazkuRuVmT8H8\nNG0m1mNI8X+dEVTMru1ed7pZGJGP1eFTtxAcl7+ItQHmh6ZJ7VUbfQNbTgZd\nVBMRb810gHh6CpvB2mLgHpgg/3HymJW7KOEhzQFgObryalHWOcWpVmV7sgf1\nw/tpN14134rv0wq4n/uKRFxwqnhCyGURJV0DTzgn8aMw7ug0nPUllMtyKMmb\nKpo2\r\n=Demq\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFt03eTDnKuDeRZ2ai6fc4VScxwC/zrX+fiuWAkg2NQLAiA/ziGRKXePv5E7wPh8WlB4HLWxrl5chfbkcmVdosb5Qg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200713192703_1594668723903_0.5900165923280938"},"_hasShrinkwrap":false},"2.0.0-add-jvm-pack.20200713193338":{"name":"@atomist/sdm","version":"2.0.0-add-jvm-pack.20200713193338","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"3a6df369885e25a379a1256ce32284ebdac2585c","_id":"@atomist/sdm@2.0.0-add-jvm-pack.20200713193338","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-BJkAkf6JrGxEWXf7jO5ptUlnBKgBiQ4pOEJUtgxyiA7TAUOoow0QYxUgd+8KZYkNMOFdAAV3FiVGb66NExtglA==","shasum":"6b9982e56765d40f04929edc2f789a6b7ba6e2e4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-jvm-pack.20200713193338.tgz","fileCount":2434,"unpackedSize":9661228,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDLhOCRA9TVsSAnZWagAAD38QAI5v6qOw31Tn1cyDZRTp\nhXTRWhjczfpyz5c/84cdPYd2cK3jnFftm5KvwxGtHJuu08rbIttwCOhzHXmO\nNdMIiIOsxegDr4ev8mthoKvR0aT0gbsA7lMhlx4Wfuw91LRDFfBHK6BdKggN\nYbX19RZRxdw+mX/HuUGiBn+LgWSTBLnVDqVUbJZaEGRoM8IDr+Szfz8dlV/1\nCL9qspRe+0WD8ncHpNPA10BIyMV6S5/PwRgEZEwcQQqxpzWSr5liB8NcA66j\nnNBLBAubX8iHrTlY2B7ACXL2IZ2TOg7fGyvhhiouTdutFv770gOPN6B2rTMd\nZsDAvbSu5wDr+wPtTFmRKPvRHRyDtRLrLQD7vyAxWFCunxNx83bhXHRiJ3I7\nM5HNKoIsmjc2f4hek5MG8JZodg99WiL+tfJ+ILO2k58jaWrEoERMFaSt4Hmc\nOiOfoivub59erySjEQewV8ZSMcfwr2crhp65ESplihMRAmgwdCQLeTkR8RBy\nEqXHcc/TRcavFBwcByAzwnRTCT2sAjava+gVN+d+JNm0LYZwEO/OaTGADGNC\nXxhlRA2gwbe/F7Hqtf9m/dm7+FFCRzyx7O7P5D7AgKzsCLtlpiucIuplbWcF\nNH4Oz5ONJ+yRFo0IoWSZGoPNDkD299yUuaoeLf65tJqJ6N0KXzbQ1aC9PlhV\nPqcv\r\n=4M6Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCLolKS1GAnSuptJIAY9Je0DXKAmikusWMQpwTO7Qj8vQIhAP06QrezA9mAeqAiJU8adECYBwFIXMv0Kxe4kQMCAHNb"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-jvm-pack.20200713193338_1594669134212_0.760829373537385"},"_hasShrinkwrap":false},"2.0.0-master.20200713202241":{"name":"@atomist/sdm","version":"2.0.0-master.20200713202241","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c24d916fa2073a0087c3650c917ab8ddbd49d3d4","_id":"@atomist/sdm@2.0.0-master.20200713202241","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Y//ox9OLfrJhfawBuzvUoTdYMtpLLRz8O6xov+N9E51rFVyes3vbBfnp/4X1GnzLc9kr+FRmDmytK9+q9H311g==","shasum":"d7255f066ceb882774004c7b36cd025ec3350bfe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200713202241.tgz","fileCount":2434,"unpackedSize":9662281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDMPMCRA9TVsSAnZWagAARbkQAKLhcyBa7sipYwI57FsF\nNmZh/sPsgkR7yL3AW5sAd4i6MHpv1nB8aFBOlixktHQVwv3Dz4lHbxVwiCtK\nJiWMCy7f4HB36IcLyhOGb5qafSYwzRr+cDKY0j1eP0SBppJc3NiOIOYyNSRh\nDwM8Tk6Ekz/Mbg1FqITHZJaW5ic3105Uk6BLRAb3yPsUBzOKzKSYzrWX+Mqy\nxFbonXDbmPs0yBWHEaow8PCq7Cymcrs7xp50+NQ6EmV4XVF2KlStPbOQizBx\nIEqjKzqErO6rZfntTMy0MYUqpmLw+AGr8xuwHAU28qGakIvJtZO4K6mKCvj/\nnlSqwnoACKogpR0Wvy5ZJz4ZIT6BtGnwS9BcWXWE1AKMq9Rz2qkU21SYGAdr\nXtNaCRhV6gVb2hmxI/84WhCvXLhE8ObnnbogINaD/9zIMymVw4zC82+jry/y\n0Ara4UR3TrzCC/cIbuWIfS8EqZGPqxIGQj1BV2mXY+tQTt2pbJBRLh3ow90d\no6Ypyhoi6c+tYAM2CBAk9MtGKwfL1UPv2X8MmEBms2tJl+LVecNRLH4qGGJT\nzWqxu1wNl1VcgsoQitQUOy7VWm7VneXdFi57V5MoX1+WzZB0SYIrpm/smtSf\ncLUJS0bGMwlNYfVSOfbb1Nxl6/kG/hYgqqJJx8AbzKiuld2xjWErBbAey4jW\nM/Bd\r\n=uA30\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC3d75T9d+gaA6odkX9QXEYSLLyiMZ3tdKGuFyy+21SDwIhAMCljFf2C2iyo1yRntzjqvJyliGeIg2vZGTi6ym+LIRZ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200713202241_1594672075961_0.8098257275790015"},"_hasShrinkwrap":false},"2.0.0-master.20200713202453":{"name":"@atomist/sdm","version":"2.0.0-master.20200713202453","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","handlebars":"^4.7.6","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework.  We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management.  Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-*, you now just\ninstall @atomist/sdm.  @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements.  Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\nPlease also note that deprecated exports have been removed from SDM\n2.0.  If you need help moving away from capabilities that have been\nremoved, please [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - Automate All the Software Things)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c24d916fa2073a0087c3650c917ab8ddbd49d3d4","_id":"@atomist/sdm@2.0.0-master.20200713202453","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-pbEGl8NccjqC9B9Z7agSK7yIcp3IHE7vcGLRY0lCVzV1salzaroF5bSBSLmimrNs7lme/ITw1eSJzPiKo01dgA==","shasum":"12ea2f889e91dda95a723a80ddda8593a25c07cd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200713202453.tgz","fileCount":2434,"unpackedSize":9662281,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDMRaCRA9TVsSAnZWagAAy50P+QBvE3LNk0u977PmIgPV\n/Dr665dIk0F3Ty7eIvOU1cXmYI6aW8Ot0msoQOeDdU6psDxbRBy14Xc6OreI\nbBpGKORZ3i5X9x159Ttf1M2mYDCJLVXKSeC/AK4YJ1fL7Txwh3wlaTLaPy1i\nGPKwKRoBj33hGAXONUtcIfMRHCzaiGB2RJB6V4XIl7BQo5IEluML2JE9L7Dh\nVukaSL0SshWTYKY+ujTizwywuTqntE91nd08wZaRN2TCSSNxXb9V+ThcqMSi\nRw7xZGF3rkPJZBU6844VWfQY/TDlh8TiUSOFmLs0jWGVvqp5I06gtAlzDmYX\n7SCeQCWt+verg4exVsB+ykbv1/FaRlOtYFNvSf9YuMoIloCPOst5s4oMJi6H\na3icJBfip5OXOx7XRK95NVTkpdJ+xfWdLHTDI7b1d9CJV7rtdhUDWyMTmi4J\nRyB+ZaNFMrKvmZaT03R8zM/rjGDlT0l+IEy6Zar//kMPeOkznyhfneDVo19/\nd3Jc0VPC2k2VO0zeLhrsJKwCsCnwaLKn2bCL9ZyCNP/9eKZoUyfd6kkgdrbs\nGeB4o1QIQiQqGRBgjrawAIPat+uToPiUW/lTlnOw5ZWEEuBcekTwVMTAeFzv\nUqxOuxS9klnYAYSTLG2D9+KiVaa7uvHt2VeFuIea5zhjl66Tpyhkh4F2PrnK\nPiHQ\r\n=a8H/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCBOnDYBII0H8xrMfJtu9CHCiGOjcnV17HKkwywTTkCuwIhAPRnY7UlLWogOKSYTiz8p0HPhltzv3CwlMOyN9BDduiY"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200713202453_1594672218243_0.37236668859870026"},"_hasShrinkwrap":false},"2.0.0-master.20200713214952":{"name":"@atomist/sdm","version":"2.0.0-master.20200713214952","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"d539d966f9089fe78b45719904b190a2200088d5","_id":"@atomist/sdm@2.0.0-master.20200713214952","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ys8xo4eO4QIX0YtnxrPa8YQ2sgBtLlqVmP31bu+mu12hKgs+8sFA5ZN0qe/ddmuXHL2kxUWHK/RRuVF4CbFXYg==","shasum":"e1b218daa57cfab71a57b84d6049e2cf0791310c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200713214952.tgz","fileCount":2434,"unpackedSize":9662835,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDNhVCRA9TVsSAnZWagAAltIP/jcz3WTVUZDSjqNUSpSg\nE8I8Hr7d2xHfgv/whfjRMd+2YOs61enxb6/Xsrs0IjJjwQnRode+UvvVsaYO\n3Jg8NOeE05UHfXjPPrIVPiEoWmgUpWX5ahLd1JGLnbh21HszoO3d3ForXuQo\nyGMXgkSquKDDHELrUomQtyq7LpCn1e884GAUiFtGUiRZDARZpWyEDpm1Q/kd\nGdg6ZPlKTCeOa0MxvXxD9aJO8BlzyDWO0AptNzdPleHfSU3SVq3p7Nm7quVu\nVhQmohPQSRawjUKyvuaAyP8C6NNhQT1UG+YlpNdQDq2zGmg470dVkAJtZDNW\n8IajIinh1/4rzq1ul99qSscQnT2rXI+kF9sgqE1/CR7fICTSBBfztN9JP4RN\n4wS7LUgVR4BC7m4PXo5zyvmRzMnauSvWdVCET+1dWX4WF/NvpLEG52daw45N\ndAJRTmTPXO5Rz7m/EFe7dpYTaoZFdufEgoMYo8AhvitSyLJcLoT50kPGTwRi\n1G/ffxMv9GGlBcBzxXE4/9s5DRCUZdomEx42Jo8G04KGLJdcNwG39i32zMc1\nHgHYd0hzofO/zcD5jS5Jv2lwvrWVF/O6URtyxzcouYABsUW97cSB+WGO5MVa\nf0YvjhqGjU8LgGYK/B3M1whDx6cmsu/KrqEoeBBq6CYsUj3ZHKmpxQf9X6K3\n5VSW\r\n=xitm\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIG8pceXqnlnVwmj8GZ32eUo7XzNI1n9ObRxZ3JR1rq3sAiEAkO/9uI1g3yy9Ht29ZVjm9I5eovS3yASJnC+s+mE5WpU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200713214952_1594677332714_0.9509205011084585"},"_hasShrinkwrap":false},"2.0.0-master.20200714004626":{"name":"@atomist/sdm","version":"2.0.0-master.20200714004626","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"56ed64888308b8a757507b21110b89565db3c220","_id":"@atomist/sdm@2.0.0-master.20200714004626","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-BIU/8ySopiSfNQrXG4ctv7JeQ5rrAXLbzcqgcekOOmdcIqOB6jh89XQddAjjSRqTMoVRuqg68XMrgoYYRUS5uw==","shasum":"1ace1ef6ab9d542cecc7826dd0ef82edee5bd795","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200714004626.tgz","fileCount":2434,"unpackedSize":9665540,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDQaZCRA9TVsSAnZWagAA3n4P/joWyJ2S1pkt45LADG5J\nUcufOeKtlAX3iLywSsPsyJ82UtoK8IEKTbM9NUddAWE389wI1PsNr6OiFjkm\n27944hVfzNEFa01BbOh3r0WoiFSlqnsyykfPbEhIFSlQi69v2nURpTHOi8ci\nUUJoDp2cuFgYjRXTQFYF89FQa/Z9JixnwXrQbXHXF9lhotzNzFJqt1N8j8Vh\nv0vhITS/kpl+fmUx/D2JckmQ7jiEFlfpTzshBIKj1vZi11iYRldB3E/crYT+\nZuXXsMaIuHb7yKMfhi7/NHz5PTjUuYxqnYNUqRL44q6p9HIefykdNABDOIL+\nqWydg8xNh/Zgj8j0V00/QRILqwlZWGX9RwxgodZMJkQaLh0l1YBCNtiSjP0H\n2ErydfOvuVOnSbkC4YAIBTTjmzTEDHMB4WR8j5vjo8TGs4Df4u62x3nZJZpM\nU3IjhlBpf/rj0I1I2qg4eoJ3th4x1r9SSUkzaVjk9NXGQvY88cPo0RdGhNzy\nUgCLknJfNtShBsMeVQK6wLnxcQ1MDOBOozi5FxJ8HpPJQcNxKjaGUxK2dvw0\nSVtuT00H3e69Rwrybji1nulq7XzSZUQnmYJIxoYIA3QGJfrwGxMCaEy5IvQk\nyd65xWKwe9dwgxyN4GYSYaca3kas7/1uTL+cVjubrtB0/3CkLl1eebEbSlWQ\n6FCn\r\n=ASAn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIA7QBRn384V+3VIctFPF1oUz5lj8NZDwOuh6qIbFM9+8AiEAiy/Yj1/RTc/NITIWyeb+LF+ezdksGU8DPAvdkKg9I4g="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200714004626_1594689176923_0.9645410930007383"},"_hasShrinkwrap":false},"2.0.0-master.20200714011323":{"name":"@atomist/sdm","version":"2.0.0-master.20200714011323","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"56ed64888308b8a757507b21110b89565db3c220","_id":"@atomist/sdm@2.0.0-master.20200714011323","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-AkIOaMAbYWt9xQpHCy5HBGkAQ21tM0pbZA6LljXfO/Nst8jVWuOCK7qY6CN9ST7tGBNIvwPnUbfiob043/bm9A==","shasum":"a288769a556b2448117546cb25d166b4e8dd4b29","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200714011323.tgz","fileCount":2434,"unpackedSize":9665540,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDQg6CRA9TVsSAnZWagAALaQP/RNy/YvfcrJcoaje7Fwo\nq/wl1/9xrOfvnkJnLxpYuKuZ7zNJ1UTweIfSTS8aDm3BCp3u7n4Svi+0X801\nOc1ah/zjITe4K1GD72FmMgM4qj+cyGlqF4DWbRGr+zOcJVvAAonZ70TceZkn\nkfCY7URzWhXZqRD7JHIZoMGROIukI/gowARmDfMm418AjoYFoCh+6ecvnWa6\n+yAiI/ZQtKxzkDqvzKkQxloO/ZM7jruSPivQWpVKcP108QtLlILlRRgV5w5e\n4crkqkpe0J5/s8W+bip/2gMtqtVgG6LHLCl6bwPLKtMx0hVb5uUpf/AZiFKl\n062Q/oB3FPyxP3d2uGfUiehRP7R/GSp59Wkgs194AOeEPuLNI/+IEWba/a5k\nFWVgkkOh93LMFzi/sjUrZ583oi6fLjNIUlPxGFdo6y2QDwMFQSLXK22r+ZUE\nAwaUY4tOeAQ0MsWId2TPWrtS04y/K13UkpF3WgXB+oxAYUOCoq3m8bTEe/bl\nje1o/33OCfTZBVGVcYuwG/ZgVe7e4NmOoW/fFe/BBGV4W0gHj6M5KyOplIt5\n63cSxTZ/u5mYrN1u25XPPlejhqspBfldxXLBnd78hqKoKgBiTo7sOWw2onnW\nEIGD4pQWClnd+8H9Vi2vSAERTWGl8tuHeS+hPv/nBWLbm+PmKmGY6DlLsdlj\nDRni\r\n=Tqbo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHzVop33mfVgvAFbnSg+2da5SZKqcts/PXhHzcTTKf8uAiAP8cw/ebH9varzVYrRSt4yr69wWZ5VHtSVAk1S3leQNQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200714011323_1594689593552_0.26690192754539876"},"_hasShrinkwrap":false},"2.0.0-master.20200714021215":{"name":"@atomist/sdm","version":"2.0.0-master.20200714021215","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"4daa0feb10c267f60629c6b0069248c2c63f07ee","_id":"@atomist/sdm@2.0.0-master.20200714021215","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-EFgaSWUjVbV/2wLo5hXdJoY2ccsJ0nHUUNAzN95VLDMPVyE82Cq4zMi32ZpdIU9a+rtM3VGFvJbTDcxxKHIUOg==","shasum":"321e0b1301751c1b0baa080e8ccf6d88556ec27f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200714021215.tgz","fileCount":2434,"unpackedSize":9663358,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDRXVCRA9TVsSAnZWagAAhYkP/ip4Ckvwic97qwiz2/GC\nskR5aU/JMYn+22A4buoHkyeO5r9lqKHmm658tZJ7V2Lz7ciqj4m+B2NABoc/\nXLoM/DY3wQ+szAcQ+z3OJWFIhS6tW36fWL/6KZwXdXYs+urkDlohwBKQMZkC\nE5KHu6avrf4cFZUP/8MoOU3T/xgqwyy/ffRL93Tm2dRJRfX1rLHBzOXgJ50R\n8m2xELOS0rAmmXq3jMsHTQKdDImqxG8MLSDpNOC+pZEtzu3rZVuRH8xPd6XA\ntwtyRNJzmBb1Ayospkn/kr0+u2LLUQD1wgLGp+t7ljpsbR83s5aPt6SJQ4tu\n0GdN8TbtUfU+vDwdB61F15Ye41C3m8fukzebiTBlrxJgIVF80YbPjp80VxjN\nFbeOapZp8UxSGpwPh+eK/+3mm8/JbS/0TJGcpQ7DdvDRCPfKP03fexssapv+\nLLbDDrQHC1DDsUFu3IBtQtzn/KVlO6gV8ehc2/GSgpzqNLN3uEOnCmvazCNu\nTtW46uaAAoRVqLZNYAZoBElRGmAg2qSPFgtV89l4mg0en0ZuT9iO7sGEI65+\nBBRjLGsw5GXamTDbFRBdUNDLIX/vUig7Y690e+EhGBPxnaKgkMmeDcSZYc6r\ngslYNGckbUceA4HMitjFKQP65weoBRk7wlO90Kf2XMgAVc+QBmNyTjQiyss8\n18rs\r\n=FNP+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDbe30Df4r2CPl0Vl0SGJs5yXBGYZg5fjygSfRTYSnV8AiBjlOjL0r+U4ZcOdMBE1PSdwQTmasp6aruXQcUQ1tZPAg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200714021215_1594693076857_0.4314135641012935"},"_hasShrinkwrap":false},"2.0.0-add-aws-pack.20200714023912":{"name":"@atomist/sdm","version":"2.0.0-add-aws-pack.20200714023912","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"56aa3282a374912f3652b7551ff522cdb00eb5bc","_id":"@atomist/sdm@2.0.0-add-aws-pack.20200714023912","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ymw6qHlL9MQNVzwjYeLyR61hrs3kXnDgIpfP261JWNCGKXUfD0r5Q452Meho2giEcudcB5JjvCapn0YddJnYng==","shasum":"ae037377bf0c0296aea14aa7eaaa1a252b8fa03c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-add-aws-pack.20200714023912.tgz","fileCount":2444,"unpackedSize":9682611,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDRxDCRA9TVsSAnZWagAA4PoQAJSNA7Fj78wEWK04IK9j\nLMAdNqGD8tM36s4GgBv01JcqJM34j+/buUV4Ckov/SiS444HZldV7WHGKllg\n+fDI6p89Qh5T+vZbo7h/L5Qb0yGRMusA1A3bfZgTWh7YVNd1W70zKImnNTly\n4wTU5ot5qw+ZC8wRIwWI3OH3f/Hzqc4P9kWfXlC4bhKfl8Fr1mvzPheiOMVL\n8amW9jqEbYF91n9hbqw2K0aHtZlXeHwuznxFsJCLBP24fcWdhRrsa2bUo85E\nLPOZK1zSj8OfeifJy55uM7NdO+aHJhw/L5kfu0DY6mJ3SU+3ZxmbMGIveI61\n4SDynS/aDAf3j3aKjaNq2unQMEjHD2F5zt52uWy6zCCagSyMnm8e21wlUcWF\nOWKhCip+vcZaknzaZ0jj0uxDD1H443Swuehn/ufD9PKsybXyWrEEVJWe1Lt1\nXLRSt5UpPzmgsRwIpl+K+xVJsuq0rD56csRtKnFRofS7sJz+p2QzKJxx6SXT\n7QtioBMVXJBO5YSgNnX72De9tb9Aw/zvgLD3gSa3NVA5mF1duuJWAcxyVU9Q\nB3Ej83+qZw6OIb9I1ZnsbbHWbKoKdKl4pXMJlwn1JQ58fud2GNdWsmk6j7MH\nn9/wu6X9Ybt1mELrnOWdtvwB3DJGAwTrRb5ywsEVH6ftHTDdh0nOwbSn/Mlc\n2jWV\r\n=wIyZ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBRZlS5uZ7Je8dA7J6HOHE/cbwWv/CN1BPsQvesMz/4tAiEAhyElOmaIS1fBZG++V0dAxf2I2AdiMgHFz3wrdykx+0E="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-add-aws-pack.20200714023912_1594694722641_0.6516216100789711"},"_hasShrinkwrap":false},"2.0.0-master.20200715111022":{"name":"@atomist/sdm","version":"2.0.0-master.20200715111022","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"2b86ba9a922c7b001ec70f21fc54f521e4fb06d9","_id":"@atomist/sdm@2.0.0-master.20200715111022","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-kli5qv9oXJsatGb2eI271za/AUXX7I9HMe/uz5DSP2QP87reBOIjNTMzgLGOzY4baaZX9aR6FHj6l1TKOfqOlw==","shasum":"8c43b028ab6fdf92f090b221b34497b19fa05ede","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200715111022.tgz","fileCount":2444,"unpackedSize":9683972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDuXzCRA9TVsSAnZWagAAqL0P/04Z3uFJDnqaO1T0WdgO\nSBviKFozI9B4hIt7ArkKHM6RMGr8/erTncG+idy4kpSjnrF87X8lyx0cXKKQ\n4qVoE3MOvlxsIaXrA8bXY8jDBm0LkR7hxO+uF9gqIbpbEfAF7wopCz2lA6y+\nYuOqc44G2krdh2G01KYafK369Ze9tXHAh1+xOjYx3zuXf7FXumNQtuWOZtUN\nJaHjtzNA6NUwx3SEephQjeckCEqsYcSFyi5TRbSrFUodD2XPD/yXAofdEmEZ\nBxspcqtQJi+gFqjUJLm1biGJ3pw8OffPOXyHIijKWG0pbUl4ZXNsX61gUOCi\n2OdlLPysNTa1TRSpny0DsinyzZZScRpw/ZP5JW0NUxPg6ut8Yx5vzlG/fRvy\nBc0ve3CS7Vbid3u5tJyTI6YKROmtgmyXAqcLBLgs1NnRj7NXGLrcSjJxlmHD\ng1O1AAMpOvDrmGBE9t4NDgZN5pXymzTnuHxROJbYyQtat8Hpl5eVnOnXeE74\ntr85an483n/mq7W97I2AP+3Jk8Sn+6DTFIF6fibDBnBEQWMDTIsi3ZSqtlxX\nDWz6KAnznVxB5PVx6dfhEOKGKnzJLQ+fm1hMEZRMF4LMJkTaeQNkCA8NPWF5\now5gkMJ7KbDms+nyjMzOFHdaiUjef/ZcVBDrimJHfKnXn6rH2CdcE9fu7Msc\n/9Br\r\n=Ykft\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDiOR1jGOOffkOccgyNfBvmKnIw+cAyFAC0TfQYQlNZYAIgPywIwyQQOe7rUaFiOuufs5UlfwM8jCnZs9LzXVB7QmY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200715111022_1594811890701_0.45672570199912266"},"_hasShrinkwrap":false},"2.0.0-master.20200715111358":{"name":"@atomist/sdm","version":"2.0.0-master.20200715111358","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"2b86ba9a922c7b001ec70f21fc54f521e4fb06d9","_id":"@atomist/sdm@2.0.0-master.20200715111358","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-4J6tbucl77KsUgI/0SGmZsURGFE92ZYCahCoSQhZp2ESr9FKReX4kyzzIyXjtjfsiGYS8e37tfyqlw9sTBZjVg==","shasum":"21cc3966fd5e7824187d8868500b38ea04ed321a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-master.20200715111358.tgz","fileCount":2444,"unpackedSize":9683972,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDuaKCRA9TVsSAnZWagAAHcUP/A4hTybcyhvUz6cLPprr\nxtq/MY6p9gCkh1o4uN+rznK+mRk+ZyRKjyBvwjgJYWLlfHq1bYmoENRs8086\ncDFHLI/osJUKAxx2tej91suIIw07535xsBB3SNTb0WDf6Or5oRFasP2TYvyf\nEp/EOpmTp+t/21sGp4I/WnJgNDoocRsf6lHS9+pExV25Nc5iOeYZotrQSfOm\nJTSkhk4stmXdvBg1+N0710i6R+OlaKGL74F8QAse3MhMc4d9g7oAa/URqA5p\nDhgPU6N7RZkg1RSIekcST/mrj0p84btPYwLfD/mH36MBwaApuk07zLHSRAlg\ne9cc4Ur+TEGdT5QhRmwtHj+G2iWe9a2PZAqmETl4V3x+B+ppepJS5BMNltkG\nfelDtqy2v/2zzQNWZa9vdkBtlDIfu1vHOW+OmP8wdnnDF8JbR/4uo0+H+l9S\nR1dkK0OthYmlY6jwMtbCxEfN/r8WQmrCEY34/KCPOZYBitaM3dx4LXkoyMPe\nKRnGFRpGxiYjByNXxPGjnvd4m5iyZlrUx6FKCUWJw8Omh2GMwYxOLzDMZxBf\nej64l80Dh7D70fL7ZIdrML0oma5JIb9qnFUjet9BL3OIjuPwMqyue1qzrfas\nC2ACWYXvZKVGHiNCmDAfMBtvqoi94L4309Pt3RW3xQBAagZiQcXniotacpOa\nKKC7\r\n=Q9Y1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGyXgkYspUNPhsx2NuoYlUCavXoWYUevRa5G9Iu/khDuAiA694PvPMpg5N/XU/IXqqOmt6KZhY6p27Ib9ksz1GbT0A=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-master.20200715111358_1594812042284_0.19006403605283695"},"_hasShrinkwrap":false},"2.0.0-ipcrm-update-s3-pack.20200715113557":{"name":"@atomist/sdm","version":"2.0.0-ipcrm-update-s3-pack.20200715113557","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200701135449","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.7","typescript":"^3.9.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ \"Atomist - Developer Quick Start\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"77fb26648c7ea9c3f939a4b9b8beac569a71f525","_id":"@atomist/sdm@2.0.0-ipcrm-update-s3-pack.20200715113557","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-LVjasAEmlUU29WzpAcxovCydMg0rBGOCcfXti9vojhGqlHICM6fW2p0yHhQTv4qJpjMDhcY4zUGkPZiXus+Kcg==","shasum":"aa704d679bd4bb8b5c8ba2320f53f775e51fab45","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-ipcrm-update-s3-pack.20200715113557.tgz","fileCount":2444,"unpackedSize":9684941,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfDuuoCRA9TVsSAnZWagAAQYgP/Agbg8Z5+cEPt3rVurGr\nv2hkFF/fPvyxScsAs86C0wLBw+uF4/DtTYq0k+WKhSu6w8mWKSQVAQ6kkh9Y\ne+woGL43KAOBcAYBg/NpEPH59UsctwS/XKtooKBTnqlJcKdOW1BaikeDH2+/\nVJHRawBF2VfSM+PBXXXz6IbWBJQWfszFuEzPqexRR1cA/zsTb0OG6y2heRNC\nKK7pwujSUCNxzOVoNWs6eN23I9oi96YNpaJ6QUFL2OK67d+nOqdYtm34Vdid\nfZqrAgvhsmJffdn6mubo6dR48Cguqpo015D4SXUHuyoacmoo7+5XpxJvLVJX\nvTUQExRGkrg12aK80314YrpRljohVE9xHseH6vLF7/PLPpf5Q4Xw72JpXbaC\n6KDpBaYhsnBxEjnMAHN0ofTtSDmtBpcwW3v8shSyIntaudtdwCstCNMTLbef\nJLxq8W4y+KqynTnwoljnltXeFkGzKWIftK7Fgpp0AiT7aH2SS4+CsN0Nkri8\nT0y1nVJ3bcGo3gzqipKI3GqkSqEBD8nR7v+NgJO9lLMNtOC+yyhNw5IGt0UM\n0lanYUEgW20rF34b8wlN3iTbhx1huWc7usZqIOdRmgCx+QkLt63/OofaPeoy\ny24mfzge44OJlxNDWiwUNUlPbJvKJNOkBshbCj6jXbcYjXYtMZoZEl+pea6w\nM68r\r\n=/wEj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3JmeRoRhn5azwLTDmgzymlhLHQNKDWoH5eIR8vMAJpAIgS6pwNeRtj0krdYHXPvBrvWo5mzYkwqmkN/p/DraA6b4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-ipcrm-update-s3-pack.20200715113557_1594813352071_0.34758801493337654"},"_hasShrinkwrap":false},"2.0.0-main.20200720151816":{"name":"@atomist/sdm","version":"2.0.0-main.20200720151816","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"05c71c6b847f5adf2ec0be45710afe25a71bd0ef","_id":"@atomist/sdm@2.0.0-main.20200720151816","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Hs5/KdPVfUMBULmWlvrCJykIdvmhl651D0drSj+yHcYeFpC4dQuiErBP+W+CG4KO9Au3rKmJjCqUmGURZkeRHg==","shasum":"146fb0441389996a9e84d6162ea7cd98eb6a1a2b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200720151816.tgz","fileCount":2444,"unpackedSize":9688289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFbdNCRA9TVsSAnZWagAA6HgP/j3w6cMdItsa95zJxQue\nfimYrrNuQ9Jg/ychIjWP1FjqybPaGnNH//zpgGM8MCWMcM3dHysZowk7o8Fa\ngPgYvkIG8Kgxw3EsbgLmR2ZgsWYa5JL3ondzkgFRyNg6R6XJWgGMv6XxXu23\nW0LsbWbKPbPBIuVaj81Yh5XCgkx8UY2mzwZMJo88Ezc5jiwka4OqAK8AUDPO\nUUGpHEhZ9NeBybj3vkMnRPO58iviTD+i7FM7O87qHsnIPgiC2zR3BtOq0Soe\n0tQOCdFGIp4soAgQtgh9Y/hKrNF19PF0oPcIvwNfso2evnphc7BkRX8QwS7f\nj2BMO7n+G4jPGCZCg+O2PZweRAw8bB/bbDXN/8lVUElgxrTbT9LLOAghcpvT\nolv4sXqrEYLlWR6uzNjxcADmDbO3OaDt2aEueFLCnhuU5CaLWcMG6SIbouO0\n0n2o1DdthcSpyQof73fX7L7ybgfcLljzZdlJSd3hWkQ97u0oeqjxb2pizfql\nIeD3YEOLuDAAmdE/lwQlnYRPt4w5WDgMxUSh5JAc1qeDhJoOX/P/vBLGqd2l\nTlRy3tZCCZAKafbpSN05+7jUto2xiCy9hwHgQKsnFZ6yy2FyE0//ULmo/83a\nKk4Yr85rMk+m3pgJYMrpjc+yIQ51U8aX73PDt0zAFwcpkuSLQkVOXUuxyLLV\nZA8w\r\n=QTOc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDp/V7vXTm6gbhZKDLthe4uiuH8TZ0kvbU6RAvVp5AsgwIhAO0nQFvQZS8Dx1jq33i0LHdRgqwik14MML5YYyRTvo2A"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200720151816_1595258700722_0.8021538269124435"},"_hasShrinkwrap":false},"2.0.0-main.20200720152036":{"name":"@atomist/sdm","version":"2.0.0-main.20200720152036","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"3.1.1","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"05c71c6b847f5adf2ec0be45710afe25a71bd0ef","_id":"@atomist/sdm@2.0.0-main.20200720152036","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-WvMeZKmxgNJ9RfH5nnilOdSoK44Qel2zQHxZJkGXxl5QtjahCkl4jome1bB8tlVZ58Ixc0v/q3H6DgNSOKeE4A==","shasum":"483350942d1e9096242ff7ce197df76dbbf93843","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200720152036.tgz","fileCount":2444,"unpackedSize":9688289,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFbfWCRA9TVsSAnZWagAAlb0P/3iPDWK6vGAI12Zs9Fr9\nQD8so7ED8MJ7DPrRBUGybGYA3wQ/xUFFMpOVve4mT/t5pFPDiOYwqpDYvOQV\nS2MnX6M+AB80Hb8292saGGzU2T4NpHcGupN36uj5ne+6nR3R0F56ghlDI5o3\ng7XLRHcpKhaVtNzK/sUejm2ClGNPyqzpTez7EoF2CdG9fwjSQKrhOW98a3/c\nlq+7Rrj6t92TSUUJz0gJpKRHOWzb87iKPtvnW5uMGQ8mxyqDQC9A4tmseyaq\nt1WUMj7eTGfzuAfvX8sLcnyX/+WWrCBYBLuMm8zhNwZwgr+7KAOBgOCzrLOx\n9M5S/hsiMYaKcIKNIjt6qF3YXkXiNtAf+G+eZ5a6X32EZSKIAyGxkNMrkBw9\nID065xek7Dnn2XAynEelzHvc0TCgbSinYGXGshBa+BSbP1ehoVHGXOucHypb\niB6CkOmZuXcoflDCwWxBy7ztWjHAtZ71Pk+I0+uRV1jJiHQq2xoNM/hdNDwi\n2zvnKfDrlXgFr3pnCBS9jHCQkJvmVJEYJ/Im8YLfiikaOSKJ2iseyi8PUyKv\nKV9+ugqaMP7LZ+KKYjkO8JuGUQ4E4dVMXgDrA+90/trIzydPMvsRoxwz5gLe\nyhDcliR+Ny10W+xdmDBNLC7ldoBtoNt6YhAK3vVm86t84EcTopvF+6lDWfjS\nSCY0\r\n=zbK4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPaqhDnImpw/LwD8Zl7wNBpkyqNnuZ2F2saJY/IwFiHgIgGXgAubqcL4UGz9aKzTvto18SecedS3VRWIVzSLcTQ/o="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200720152036_1595258837833_0.6037958011604498"},"_hasShrinkwrap":false},"2.0.0-main.20200720184558":{"name":"@atomist/sdm","version":"2.0.0-main.20200720184558","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"042d5c6cf1a7d801e31abdbd3cc44f7f9ba08106","_id":"@atomist/sdm@2.0.0-main.20200720184558","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-1Hvo3bY03etubg2o6LO4kZ0eJKU+Q7HNlBSNR5/UyXlE3triFlHXebXQRIBpcP7pu9lcCHOfMSBy2ZVC/rviIw==","shasum":"6ab40de81fb1b6c8faac81a8041c502225a4e56c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200720184558.tgz","fileCount":2444,"unpackedSize":9687914,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFegDCRA9TVsSAnZWagAAaBQP/0JyGIZFkQs6OQdLHbx3\n7KdMlmG0qdgp//6c0EuJbyMMQpQW4QikuING0pFxbe3MfKbTUeAvPTvyspp7\ny3RleLQOaXbkYPjUFuGeEd+suupk8RpVH+n+VMtN/VgEE6BH5PhNZOwUVj9H\nB2OCwkhVkquwJU4vGWlmx0pWsq8ds7F9Dbi6UIngA6XcDWanBuG0hUo2aJKX\nFKf5VidGAO4RnrADLdq1VtURMcwoedEswH7tPxjOYu1tnIEVepZlSpjCBhoY\ntUaEHABvHG4r/btorLR/0KYiXH6/p3bW+MGo4er1kd1V3PjplQBqiJ/PUYqj\nnk7lwXfhvaaioItyvgNZLtBRc5Oecam7kISFMcoKqs0nvfhkWRvVYWQLBJ9j\nDrqEVy5kx3v7WmuzC0VjCPycbE+TwEQT+vj+Ydv/pn7abP9RCeeiexCLm0zy\nsIkzYWj+nN9XQHgLz26x5aiLKdv83Uy+hQlHuMHA9mtla0ZQgQWOzl8ag7f7\nHJ+entyr1ZaYS1wBtUUa6Zznm6oGF8QbojR4RShvpDVNFZSON6kDrVFdxH96\nwr6WKSYDXeFUqWAeGdOO+mca5G75UOTY/84kfrRhovuN6LVH1qoxBR+O2x0N\nlj5e7+YEXAPCYo2px19fhvpWNA8F/MwWWTHBZox7cUZHP+qaYSWTt3/aeApE\njbel\r\n=cqqu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEIZiDYBWWLu0+UzD1j93ouBg3EmDcJUK+X2TESzkldsAiBFl9TnqYWTEuiB6WjODwbZUAVQGGRJYv/OqUlPJCSo1w=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200720184558_1595271170423_0.5144681433076492"},"_hasShrinkwrap":false},"2.0.0-main.20200720184830":{"name":"@atomist/sdm","version":"2.0.0-main.20200720184830","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.139","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.15","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^17.0.0"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"042d5c6cf1a7d801e31abdbd3cc44f7f9ba08106","_id":"@atomist/sdm@2.0.0-main.20200720184830","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-56sipSiQyTEOmt6xjOaMpx+rl0c7K0fu9CXxVNeu+cToHOmtkmdtK101hDNTfkxgHO+7Voe6tv80ukhhKZZPWQ==","shasum":"94c867b44ccf1aea4c9849e64587d81a3ffa016e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200720184830.tgz","fileCount":2444,"unpackedSize":9687914,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFehsCRA9TVsSAnZWagAASkUP/3FxAnfGObH70BQJsQJH\nNa6RzEtRZiU3bMv1XJ1rUerjOoAp34Kbtq0vZbjD5cgofxTYk14SkqyczBLj\n+VQrRXPiRUe+vV4XQoP/6HqIpPXuG5/K1Ngdrx3NMgTtZFsnKvPHi6yCa4I+\nAqQG97GwmTTOWASsdCAcKm+KYFa296S9HJ5iUzQ9KD4iXbs++045q2nJw7pA\nM+V+ugrM6vifXvVz2r3h+9i6M/NRzA1U3OWNPzY9a7j5XTKXrXtkYPbpu6w3\n+w/A7jDFZy8gOYsxvUcComx9rCNsKxExMLgRX+8HWNOTEMOnQHHcbdcEnSBh\nCmaFuDAEW9Zn/Wgtyllgn/PO/UpTscsRhIMFlzMW5HY9QcewAaSHwHpkyq+y\nINYlSSgVqatPeQBuzvD+mmM8rcKi3zmg/E9NrboDdWU0rLds4MLQveby0y7J\nUHenkvRxDqPAF4bsAAOEO5VvCoTZCFXmLk+68mKrTi8pRUtJHol8bvAx0vqE\nFIcflUpsZWm77bFUi6CeaD2nhLsn656rXTzVJC8jljDQkoggNQxXcbUrg889\n2jA7uAaqkTQNpYKvOru3DAmlg9l2zg6tiwWRM2bD9y3ZCGXSyNNSe6SexTfT\nK22DbfdKazt7GxOJDMr605Jm4IpDGEwxH8ZDP8NW2AfIutX1e7KXEBuQhtJz\nkbzT\r\n=Y+99\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCM6WQkE6yUZkYiHMrIxgHeiEyRsiBNkZq+8yFdDVxY/gIhANrPQE7G4eu8nc/eQolJ5Tf7Dqqk1aC4Qal0KAc2UH7a"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200720184830_1595271275405_0.2542154281184872"},"_hasShrinkwrap":false},"2.0.0-main.20200720194232":{"name":"@atomist/sdm","version":"2.0.0-main.20200720194232","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"cf21cf3d31719499d354c7fcce26c1afbdabb3e3","_id":"@atomist/sdm@2.0.0-main.20200720194232","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-UeTfo/zkC5BorfkmN2+4NreomI1E63hPRo0CwZC9lajuAwx1gIsvOgMTvqSxUZcndlTPvixRsfZtw7Nyop20xA==","shasum":"1ac7f8a9d451f2af0b72342403bf96e8b5786153","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200720194232.tgz","fileCount":2444,"unpackedSize":9687779,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfFfULCRA9TVsSAnZWagAAoQoQAJbF93X7ltLjvvQ08+FG\nlp7W8kD1jLK0jLkwxd/ZOBYVfONMU0/6jN1qtMuoek6q2uJB2JwOEPEr0vYb\nIkan09ktEPy12MtDzrL2ABRs+YFKvOlO90Wo1FhjkQZCaB/jOlXJUShYye/G\nnwqVcLR12Ygr/udCNKYwJS8e52lLb1W2unA7veQ77UIW+LlN1VD2cYhYq/sc\nWzpBFGj1Zyzb6cSeIe6bjXkEAijlOLZnQSEafnPK/eCAEaNcPn7muwlVQmvI\nIQgrWKmWwNJ7VrfWr+ogOhO2/qWjPdX+6PbqyOY1Jb3QGCos6c9NnORfS4hr\nDwDht0UCrKmVhX/cjC73L/HSGz5bTYT9spVDXoFLfMJsAJHX7hthVXOdHbKE\nyPu8+aNCsjkgAzSfNgJkUl+rl89GV4KATyJ42mSOYcK4JQKkYMyR3CSJ4mwV\nYgwSzwLDE4sLr0RcI1KJj1uzV4K0gvZgSZ2BGTcvmO0YbBHutECl2mw+cX1P\nWrPtDoF07FFIyRqjCocIXnHXH4TL9Iq1pY9dU8IBNvXTviR8xlJuc/ZB3yT7\n1dCuqKnxMlNOlf/D8N3JK8P5QkPK2eKZUlo/d77JIe+zEwfIDuuy9fFRrrbS\nd4ShWRFvnrlvx42kaHgj3Yu10glRFTwAqFklwzeeMnAJ11fUyd86ovR/MljD\n8Qdg\r\n=ppPF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE5ao8wKso9IkdSd4JAGPuhE5Zkslxrhc8bBqdcbywKuAiBMoY5Z4OkmIv5rxNBctlvhFRMVZ00mMgvuykB8RRVlZQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200720194232_1595274506532_0.4950983404416278"},"_hasShrinkwrap":false},"2.0.0-main.20200804173404":{"name":"@atomist/sdm","version":"2.0.0-main.20200804173404","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"0a2e5ea02ba4013e094cfb762079f14405657d06","_id":"@atomist/sdm@2.0.0-main.20200804173404","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-5PVIPxhMpCJfwA/yIr1Bt9aHG8v/TsLYa77fDDqc9GzPEGqkrBMjQigIbo/0aZqh/Eb4O1ZsePXbWi7/07OV1w==","shasum":"538c01a199d40e45f901c91523395185a85520b6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200804173404.tgz","fileCount":2444,"unpackedSize":9688732,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfKZ1uCRA9TVsSAnZWagAA0ewP/159hrJ/jMfUsOMnyM+I\n1nMMTOdEn95ogEH3PF5k8zodR2GrGMHyTwiD8w+c9L9Z1TR1Rpv0tMqWOoDy\nFIpE1IGWI7etEUmalHltvCmkYP2w+H2H8b6LklV6HIFGxOZ4zrGkE9PgbK+s\ne8QDDa+EM8DMOPQt1l4AvXU4THxue/LV0W/5rsUyVsm3dcIs0g4EKn6OHfVA\n87U7DzqwHRjEERadD4G5kMXHKp5a8gl3IOzakXOjQqq57wjrrJ7PJZ0jnUFZ\nvhGLITizaf0AeuDSUHSrXHeg8iUYQzRTYHssKMOGHgmeuTMWdmOacPjd72Cw\nijXuhfH4UkzXYKSbgvd9/Tk5IilGAUxuNoIiEOYEpPDpdt902GbfUjX7C8UB\nd6x8Y8Mgz1PngaJRNlmPAvHNGj8aSA0NTHiElNHNriTc4zkb+BWB8fyl+8LZ\nIOLZUjaw3MqaV2aljCA+WFMGqo6ZI0yLmYWWAy/YCGeNcEG9c3NiQVwmZIdL\ng9i7YxUmEQDQjGHlUKxdKx7xsmXM1MFmR6mBfiAWIGcnf35HQonRMsOZrVjC\nfzNGDGGV07gc0skiJSyUA0uEr8AMTh60w+pxm53IMpNJdDyLfIPEWU9jZFbh\nbVt0lqgmjhsnlfnuoJRkvDLo6pcAV+wrTP3ZcaGoypdMdu7lhoCmU9G29x/w\nefXH\r\n=bMyF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE9/VdPGYJ67LAMUiECrOwLEVS/y3kyOMSspmzeDPXq6AiEAk70vFlX61YoVF80vQ8vZIjf2q2O3lzuHIpsC0+AHqxM="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200804173404_1596562797349_0.4417368305856395"},"_hasShrinkwrap":false},"2.0.0-main.20200817164942":{"name":"@atomist/sdm","version":"2.0.0-main.20200817164942","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"1e213337f140f561cba33564c46ad727796e1b0b","_id":"@atomist/sdm@2.0.0-main.20200817164942","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-PjazQimB5l2Jnb3yqi67FcpejmrIBxxRP2JkY6XcIKg/hhNg/d7xPkMe4RbV5Ay0LYJ9evJcrSTjRmc7j28w7w==","shasum":"13782b295b8c4dea62401641c457a7c3350c38fe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200817164942.tgz","fileCount":2444,"unpackedSize":9689768,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfOrbOCRA9TVsSAnZWagAACk4P/ismRxoXJrr4uMfcL/lV\nXQYm2o6F8hcwqSN9L/HxqrxkUzApiwpBzL5Qhr/CX2eHMxA/Qr9fN5XBU2Vf\n2prkMFRlKs38fE3qWZHM01CLYQ4PRftJZIzxuL2vQM2u97ulhBDsmyw3T5hG\n1cYi8Lgxxvs6wUGrUitOfi39cMMlTPd040Kw7VRje3I2Zkb9uM9oDbw32pBu\nmEkPUoL5UItek4pc+UdOxxX6uOAFDrNk4TeEMnp3kwnvq8ZOXmwBkf3m7INQ\nnMhptalICjB1ZyLfT0m/kOwRrnoWddfkxIAJES+CjdYacK5brSPGj3jS446b\noPOkJc1KTxg7xodNLfM9xMwV+D9up0P6QaeoDWX5EEHEaaotbyXaUjcvi91o\nr79v8FnNR/62Zo0xuC5SRKio7jsrA1mRoL8jY0+UBet4jws0rkM8k+ZoPOwl\n3SZwHDWrA/F6gZnGAz5metwElddYTUxvCGR6s/LOiY/mnxZatGP3jw8waQbf\nLiztNepd4OMvvZ2Mp+qmNeOsbRxaSAyw1KrMbN5yiO3Fr2KboWRBFj3sim3l\ngTkqjMpIrWuma34BOW66A6m/iD+/cSkS83R/TmDXkMiIAfRpV6W9ZrCqk0op\nJUx/FQx9FM65eAxsfUFwzxFTRa0SIVpHVBioLYyEIwoml+PSMKvTAXfWQqwT\nyA4O\r\n=s7xO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCCCWNXQn26m8S8HyynxwKPRifZGXj7vGCxkAUZVzf6pwIgKPXYfsBqemwNYLxtaLTVckE/r1emDz9OLmoKcNKKPw8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200817164942_1597683405492_0.5662254135376183"},"_hasShrinkwrap":false},"2.0.0-main.20200817165734":{"name":"@atomist/sdm","version":"2.0.0-main.20200817165734","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"1e213337f140f561cba33564c46ad727796e1b0b","_id":"@atomist/sdm@2.0.0-main.20200817165734","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-5TzirQ8u2/yia7qboieIkybHsFC859MEAR+JXh88Z9dG1GnPyxahwL17zzvbFx7sa0x2E/z87CA9MYOUm8KxXA==","shasum":"fb8690dd2666da20707d3702899407ddcf040c82","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200817165734.tgz","fileCount":2444,"unpackedSize":9689803,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfOrhnCRA9TVsSAnZWagAA4uIP/1uU7aUVHvNhzW6HiWbe\nEP6ofiWcKvJUmJDeDnd1cPDgSwmx7jcVldPr0ClEDppCuXvKOazgo9CUePex\ny47FR8CfphTQN0SvfAVjVJjK5a/8KqGl+SYLpnPZ9kXpPV5ZTsdUr9z+WpL6\nD+r3SfM3WnbXMol4LcqOi8N5eipwjY86BH8K218N2QEs4OycxiPxQ+Af622t\nRkcT3nQM6sIoMuS5s7niMLr/3LvfeBXlXvSENUKO+jA/b7LZv64vUfwmA/nB\ndsflb/gQxy2xsaEJpGHyk4ETCtlBrA1mnp1La8+0E1UTD82+Arlzlk9siNBx\nogzJpvvL9+4iTNTueuVZCVy9C0tGPdkUuD/W2FyhHtSfvPMkPy04/LlTj5Ak\nfOvvZolAGGAzw4+THEHF7d8nLmvBXm11/RfZ0ujwj9U7gTEmt13BEpEMNjLx\ntrwjqtrdBnbFR98Fq3zAClHIoUri1bTLkmdqa0WAFABg/hJuBxEhNXv8FFpS\n+YDNCm4ZI62vZfiukw/Rzev6RCaJBpr9S69R3n1Zhjk+dPQhHb2rJXy9/hJx\n2Z0/5j+Y06fOtfZMH4kMTy4hpT7bPup7x/Rgro2a+bYXJ8cUKEHQ8MeIb2uG\n01EaCAHpjHi6AGBDCx+Xp8gF5DurYc+ujYo/ku+4HKMY5GQmDbWy78GeSWVA\nLY/d\r\n=wd13\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFzWAP4vjmWAsil0/K8AnZLOx530jwTeEEW798EiqifIAiBon746EnouhTLaRus6fKhDRLOBfxOg8SGocClKu+9K+g=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200817165734_1597683814290_0.4403277884154779"},"_hasShrinkwrap":false},"1.9.1-update-params-1.9.20200818141602":{"name":"@atomist/sdm","version":"1.9.1-update-params-1.9.20200818141602","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"c1fe226f86c240353d434a7060121cc61df32958","_id":"@atomist/sdm@1.9.1-update-params-1.9.20200818141602","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-7CBQzFUCukzrd5/Tk+F6IO9eu6yDCYQSFsoc+zs3enYIyxTgtKqOLDvgGId5BLyjJyaqfDb6DgLBpRSTswIxYA==","shasum":"8b2f7c1d9eecac72769eae4bf1f8570bf832c0e2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-update-params-1.9.20200818141602.tgz","fileCount":1239,"unpackedSize":5812504,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfO+RqCRA9TVsSAnZWagAA9xgP/iFSpVcGSAjmjR4lVUky\nTFwNPNGjLnqR1sWCEI5GBX5UiVzAw7M225yG+xA+Jx5MLXiW9x7Uwx9wGzpL\nubifTz77/ivMIL59VpKV//FxrVUaLeEzNR3O3efHdH/8VzYGHN09a6ksS8Up\nj90+xTMZoQTGNsbXEccBluIKXqQCvJhllxsx9BR6DULo+TeAJc8ytzGrPAC3\nDg27fSAtWDw9qALzWAJbYOI7YSXb6jYD+T30HG7+G/LT2qu9nYcDmLK1C65v\nRfREN93J6uAxLfHgwPB7g6jGc6fnj3fKSATw4jM/mMZF/07JxxNgCEgutRU8\noa4DeRX3kOFDG9jN+3jnk00xVDM1KiLdZlAAeB1SIE7AKhj5gfLRdU7VRkWx\ns9iYBKoNBrAFFX13y6IY1/KlTp+V4W/L8o2kek38az9TQjyTMb4ZNi95Lyky\nahqMnREmj+dO7kEZRe/kDt8Z7Lsa/UIMVtrb0udEX9/yRX+EkJlbBFPfr6Dp\nvxQje1g0wNbdjpfhMvgmdzWcXOG6iJeXwcKUkJXDb3BuzH3ef+KXpddq97yc\nlag/266bST0kAOLggXpxT+VXyiuy5fQLkVI6rUT8qLmK3kOO/l1GXhlDZ4A8\n7G79vD+AfVWqInxoA6iamcuZP5uEKivQ3ZO8q4jM2d3Q+SSTqvPY+D2qyXQ5\nFMl7\r\n=5LFV\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIB0j53zvisgpOxtCHzneYfK5N7FUfNvktJJb03y12jMwAiEAxhnpwdr/HQ2cFEZNByDWQlsdcJhpodqZHbmuArQY/L8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-update-params-1.9.20200818141602_1597760617675_0.41905094771212714"},"_hasShrinkwrap":false},"2.0.0-main.20200820104521":{"name":"@atomist/sdm","version":"2.0.0-main.20200820104521","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"ccb4b60cf7a1eb51e81c42c8443a1021c773024b","_id":"@atomist/sdm@2.0.0-main.20200820104521","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-b69P0BRCJabCpYYuvn9yMc9P0V8WrSyTNdrMPSckI6y4O/HtaqR57rheyhPclm4bFT3VDYzRGQ/6R0xc1ZHZOQ==","shasum":"bc4844ff1849dcced40b2313b2c5dca2eb5462a6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200820104521.tgz","fileCount":2444,"unpackedSize":9690130,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfPlWxCRA9TVsSAnZWagAAov0P/iAnP/ZEL9/E5F33aadF\nzhV99axdFpnWsbfP22wIHSj4heCHzTjH1OTFMTlsl/nOZGA1wQpEfAQDA2aC\nA6Eo+WzEROBiaY6ix0X+VR4Mz3YjtPN980KY0XX+wqFieUWaLhhwK6BT7AJu\nHTmYhodTrYYb4fQ6taUkmahUhe3B+efJlWbpuqrjfJHcgonnOIZVzjDlO0t1\nI7mlQp9cbEZHNbMhyczDr1lJYyfjJLFP4PdD5XoRYhIAOLiOMQhpJtxAdaIh\n1cq3v6gx1InLuV/YqnlTb9rRI/1kGujHx2xAz4+bA/dO9W4o0t29A1iL0wZs\n8SI0ZQyjM4hjGwOYCQEUsV7qfkBLp/1PHb58ASbvUkUdDGqwQzOGgTMPLPlF\n93bseNx9eQCu7cTzgQOGwr9dDXqVKi/IxBLNMzjT+cqQm5ELnIwLtL0fbC2M\nB/mkl368HeRQV6rksY0ZT9lGgKPNf6tvOPuK5uByDiyMduv4raDq5QjzzRoW\ntUU0H0Aro0Oi4tiIY34PBErrvnnhL09sKpEB1Fesa7ZKNrTNBDhrB5Lhco/u\nOT0MbumybLqRlD0jFwv1OLFFFWXDLL94/N8DdYP+nLHpgJY5KVMU/v5vhNta\nUcgHcdpiL+nN8XWFLiw+0afQsSyyoM111+axpHtm1mBPE3Rtj7MsdVU92/Jw\n+xcM\r\n=CoHI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCU8uPVDVCu/+87Jq8JY9QMXIO4YlAdroXO9+XAKe8giAIgPHeUW+Afv3IgMXyUM4JtAmXywBsYyoXi2hh0fH2YEB8="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200820104521_1597920688720_0.521702508603821"},"_hasShrinkwrap":false},"2.0.0-main.20200825161405":{"name":"@atomist/sdm","version":"2.0.0-main.20200825161405","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-master.20200713182021","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.157","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.19","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.0.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b84b98a2be0c51a5627d471a43cd74a24d949710","_id":"@atomist/sdm@2.0.0-main.20200825161405","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-2BqlNbwV+v21sr9bU0ObZGd8sVDxkIcOj31buwIaTJ7X4He9J7wzlyfAHEAw2ZENfRZaw/itIlAXpkcepFHVjA==","shasum":"6f481b23843e3c0e134d0e72ef1a8e1f92a087f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200825161405.tgz","fileCount":2444,"unpackedSize":9690075,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfRTqDCRA9TVsSAnZWagAA+YsP/2asbSqi4hp9nvVWOm8t\nWZ17313u6RpimozWmsNSku4ITGsufvRbne2SoVGDeLHWKOOP6/2uaqmI477B\nDwb4U6wrSnTUT+lUeus+oBTsm6MpNAiCntnWJQkyMnGARURhjzN9neEIkZBe\nHOxwYFk9M9HaUjSc8rMLo87lE3889yo/UYmWesEgqCinwSA2hdUSULfajeLz\nZvpJEPH6eofMB9cV8KtQ+GvBvCMpX1fg7jaCxxJJjn+vpecJdjYpWyCZvXnN\nU+dv5P0SV4F5FJ7JNG3Tt1YiPRRJk3Pw6UwuUKVN1Qf8rpKI7TAGuzx8jnHu\nBFnqlGVQu6akEwCuC1rWiBaS6cJm9KQzFT3UOtQkozYmEuktEpi5utdXBheF\nKrsCpDVRZVmR/pooZK2pwGyCQpe9hUMpUAIoYcl8FElfVMR5Zg/9vYKr/cMA\nk4xO/tzLG+wnDaLbYVhZ3TbJ4GBYYwCZE51cCorf92qFzhvPiSK5gqONIXcb\nUS2QrxFu2t5zLadANxEStH3nSq+MvXa8Sx8QkimNp+gIxxX5ebapanVQYhnH\n6zxbD1G3G3JO7TrNuOKtazk0FBHqWC4twr2441/1f1p7k2b3RqVvdBICCH+R\naN1AgXOfcJiPbJ4s1tEdYQBPWRXCpE9SYIok6/on0p3Nm9G1WH3fvrmjfAsi\nWaP2\r\n=/0ze\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCJlonFy5sGxsiHdEefFSHwjORDRVi9/vU/Ks0i++dCMgIgYc7O+WYdf6qWLKIcln87gVfNXFT7sm4ypvoNhQqTlHg="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200825161405_1598372483083_0.6097081493102778"},"_hasShrinkwrap":false},"2.0.0-main.20200902213654":{"name":"@atomist/sdm","version":"2.0.0-main.20200902213654","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-main.20200902195500","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^4.3.1","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"ff2ffc1e8ab6b84549334b4072bed5fba72e5a4b","_id":"@atomist/sdm@2.0.0-main.20200902213654","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ZgemT0eywfMF2dD4cQQcKHCYt2acP1cNEH9wZHsI9DNU3p+0bDgYCZxVtq27k8TDSePp0IowZkg1WQkpZER6Bg==","shasum":"a6cfb65a6991bb99defaafbc7f23581ad3fe6df9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200902213654.tgz","fileCount":2444,"unpackedSize":9690538,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUBI1CRA9TVsSAnZWagAArOcQAKOtMtYFt8BNz2jmTtNB\nfHiz24r0odIVSHTRncomtWxFfosFPHVomX55mhTO5uaHzwbFjaynpbu+xUJS\nqZCVVcnMaoglYegd7OQi3UCsq+txX+guOkFw1EAe+FBTQy7q2KAw6ormu1xA\n94j6oveQmTZ656dBtpu+yFRVW4dsW6ZZnAOYF6pQTnWzWqVYLRJWqEggnymt\nP281kEinSPZSn2nTWArY/5GoGlj4RwcfOxSEjctVg7GkZAxhsvbfZA+WeDPZ\n9qtoxv/6kE4Bt25QqnQQ1KEavn7sztfgrOzpc1dL0bxGrMLC1tLc3piINaAE\n7Zz/gmOofPz7SmY2rD5sBmeok80n3aHFIV+YvYHE1f9KZ2kQ1t96EawNdP6J\nO437V2qbZCIBXluQxQ3YJkm7lvFh6XQWqSPCnKfWouhtetbSa8Kz6AcxP7j9\n9QckWWS0Wy5HL3ZtTMIeveHYx8l+ewh8c2JUpOkufFqd3FIdJip9DJxIjUtO\nJxjaQl5UGr4r7XhvRVpnGtOV2YSLqZPJmviMFOcKVRMDsvhnsr41z8IDQIvS\nfREeSrF8bL8CZyZwhjvdEk2CsG7m6QGY/gksqCb9IDacCNSnSQf0oeAuHG/t\nb6uF4ntVGkwQ4NLn8v5nZlLuCY5Q5K6zAwL6vZ01i5P70smBbSyrzYO49k24\nywls\r\n=EcYL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBj1Vgk3ARozqr210NJ8c/mka3JT1ixes70Pda29K3sZAiA7CIcE2UlNPqQYdaQrUqG3EpciBwde8aWIYVzZwnrXbg=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200902213654_1599083060414_0.5524354284080082"},"_hasShrinkwrap":false},"2.0.0-main.20200903122727":{"name":"@atomist/sdm","version":"2.0.0-main.20200903122727","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-main.20200902195500","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^8.10.2","tslint":"^5.20.1","typedoc":"^0.17.8","typescript":"^3.9.7"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"f35e318cd94a5e7b55d1c4a90ff7c641ed64d427","_id":"@atomist/sdm@2.0.0-main.20200903122727","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-bIwWOrPFlRU/mhZsIREi+cDdm0PEzKGPACKPsWfivfC0JIHsntb6QOPpbtRVwVv73taY02d/VYAs4SWaA0jQZA==","shasum":"5b4de6723b1e8cb859848f83ce316738326d0348","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200903122727.tgz","fileCount":2444,"unpackedSize":9682201,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUOMJCRA9TVsSAnZWagAAU6UP/iovU8yoRYEpZCORT+xn\nqCy0zklX+pzgtWa7pFrBYaKvPJj6cz6B2eXXqx+6LGKlydQAOJwKYV+zoS6X\nhJXcdY1lvvajjYzsU1qOdRBMY/ITtY/Ehb7F9/JWz6Uk6SkuFxHDkIEcARsN\nqeFYIv1D+apugKkMFcrNWW+AYWiAaZpTEI+oXGalOAonBkqFAUgmgPrCgVn5\nFzaIKWYuGNLmpYHpvRgrC5+p6H/L70syV59l5fg/81vLRQ3fczK4551lDuBJ\n0HsYkSoQn+d8UwojA98DIbpG53Qn+zkWRmv3hcH5/1Zk8qIaylfINuxRkf6n\np56/CpYeG45r7S5LSXZ7S3vCSfMxDK+wK1RriP0LXzRELWR0wkJIIRIFo4Up\n8i3i9L5ZA7xugLg8dsR9rI/dIN5uJoYDFx+MQFRbH3Jm3Q9DYv2teM9/x8dT\nxgVtUbXPRcWkkC7ip5kwivIM89TAAueHRMVd2caESscINzGmw7F4vXuXNvR2\nWDecaDc2jgPx780SZ+yowT8H8q2EE7LI51P0M5S5ij2zXeTdRlvFucP1E4+f\ngdFxBRWHQW/mrlDJcdGY8RyGfSPpEZuRJIzJNIBAPg0PxW7nm/vo5Tm3HysS\nZcjYIjTQNGv9MfzXwmO2GuTUgRxunm+pHcMcUfGDv/byIgJW+RDCX8n8hNM0\nc0Uw\r\n=VetG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCk5euohtu9cLTDLQIf0AQUQBi98CXUQLnIvdB4XuHbpAIgFAgmWz+QtX3jJSa97YtnXfZ1M0CQWDjgFUP1Sp3piZY="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200903122727_1599136520913_0.24520703896878993"},"_hasShrinkwrap":false},"2.0.0-main.20200903175511":{"name":"@atomist/sdm","version":"2.0.0-main.20200903175511","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0-main.20200903155129","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"249027c71eb29ebb3987a37d4ae1c05b201b6de2","_id":"@atomist/sdm@2.0.0-main.20200903175511","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-AWpHaDiPAJ5FYAPzHSUPOdRfFe3s8+8TsMOvS74i2+mS3igIBtCT+U5nMuiTu3S6KORGtYKc2U0WmwIu7hwoaA==","shasum":"75679c7c060f8c0ca617a69621a117bcdde95d3d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200903175511.tgz","fileCount":2444,"unpackedSize":9467989,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUS/UCRA9TVsSAnZWagAAjlMQAIZpLoVbxPKQBmLy2yoL\n6FhrryUfTx91XnoGFT62WHhcck/HIsle5tIkA6Z0XRSNoGwOUUlqMRwKsLs3\nOfQ5Pw/Fx1nr8fVSfZl8gaIezLxbmXgWtSrtYfsNRgCmtGVVFpxaoPyWGFp6\new3eCpjw59I0k1qm1De1XylznZ7PKM+RV3ap6unDI4apE0Kgd5yoNeWot8ww\nG9QU6jYonOO4H4YNLIycLf5qPHvPx1eFHhXsgVpMDUV9Akj05p6dOzzrcOc1\nGJvre/52sUsvHgcrlVCQfd2+D5ep6Ox9QJGr2j+22PgvVEVNYe6J/3Vmtbja\nn1b+rDKNMN1UPdBPZZh4iC3MbZHGXUHRntIWEfsqOlDdwl4sL0R6NwtlWrxZ\n7PWnmUUnl3xGAYC6bBZuZrIhXVJAclrFJjsHu5Lgfl/xQlu9G4StVJ2fruwO\n2cPGB3hms7c+ZCabg7IAn/fa9JRTgc5q1+2fygzjlvs6lMXDHWr3cZn+X9zp\nZS2QvLihcl+5fV4n9AXn3o54mDDK/21XuAl38Af3nn93eRgkMkGR1KJ8riTu\nfSU5/yi+K1PouM2lW9Hf44aaBnWA3xaF+xVe+35YVUf0OHTS+AXdCE+kGfK0\nV05mghCUpMWh7hWWkk1TAJ/R8rwHHvebtnkbPNMDliGDmNTCCKbQ2shzqmyC\ntVpD\r\n=UNJu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCe0zfLv3GDchQLtPqnfSRXSoE17pOcx3HEBZh6gWGrlAIhAMkbX31/yQ6U4vWM6+Wjc7XSsOV1tFclWnRZjVJz9zAZ"}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200903175511_1599156180292_0.7380078722167789"},"_hasShrinkwrap":false},"2.0.0-main.20200904015632":{"name":"@atomist/sdm","version":"2.0.0-main.20200904015632","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^12.7.8","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^6.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.0.2","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"e1b25343f86dabc7cfe65933f2656a50646e042e","_id":"@atomist/sdm@2.0.0-main.20200904015632","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Glk6N0q9KnQ9vDeFWAuCLayWY8q+RLCSazgkBhxXwYx6w2SzALXeKDghdpZe4GP9oUJMzyfSXbeO5vUQ2v5MCQ==","shasum":"0b836e7c9affceead41afc21ad50cedbb8ca155c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200904015632.tgz","fileCount":2444,"unpackedSize":9467949,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUaC9CRA9TVsSAnZWagAAbpwP/3STVuJi4zhxP/dm/WGh\nw+ZjpqabPfSUUxtHh0sewPR6GqMQbfyZ5jYSqDtIm4WHz8i8vkmKcnkLIHhR\nvKDxK9XQGZlH2qP03/F/TLXqtc2oO8kpbQ1SHk448wDfE52q12faWCXVC8bA\nKA51LVJiIwKxN3V1G8sCkoF8innBasU1GJ2ZZ5zjmADbo96RQ5KV4IuOtk2t\ncbsh+HLcJ5LmpWZOou+g7vsSUrKwrhJiRp9PPXKL4ZQl32sd23ctY4dlGTXH\nTg5KYKTOBs+5c7pusbyQeSHUnHEw48lkh9v7dQPMr7Fm7X3I0uf+RGV7FFHs\n8kvFLS+G5H2Rrq2epjM26oswtqWTBGbMkXDjO5uobMTLH351dwjvfJx3gTgI\nMBUTNE5iAiBBRS0noLYnPWXLnfpqiQDQI5fOH4Bi+Uu0+SedKwZsoFul/pFi\n9koWVTbsFqGxj+kgf+sWj4Xdf8ZT9j8XwHj1c4mIzKkTLOMr/ovqT8/JMS+i\njtgWTF8kNgK9xnEakqnI6diwv6DOgAEEaPEtoLgAMa9yDRFJ9Jbpniooq3sh\nQZzYvVQccGMXhcSPwtHSWmFKWPp8JX4JcaydxaGaMzpK2GmC10w5twE7AGSj\nkoX/ZsKLo3QNptGSxXlTXuWj3FAzxXXgpwTHchHs2jBzDKSj5ux3vHZHsZcH\nSyzP\r\n=7tfe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB7Ln6A20oNTeTgnE4ByzbxR1zrkNXviIqNnGliOxiyMAiBOYu9lcEowix5DoC2HVLPJI3UKVdqoUbVbdGYqkGUAmA=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200904015632_1599185084499_0.6005741337831423"},"_hasShrinkwrap":false},"2.0.0-main.20200904144019":{"name":"@atomist/sdm","version":"2.0.0-main.20200904144019","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"aa16a215166e2da41f738d80e5c4037da281d459","_id":"@atomist/sdm@2.0.0-main.20200904144019","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-634rLcUriZ8nb1/Z0FjewiwLyZVi2ZbvcECPBobniB10BEkFCBaV10kZ39yusQGDyFABVaDlDshJ1WVlaAhsjw==","shasum":"c8f6cca1bc269f299936eba7f8a3adc95cf8d9a5","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0-main.20200904144019.tgz","fileCount":2444,"unpackedSize":9468080,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUlNRCRA9TVsSAnZWagAA5HEP/R3Rz+Ec89K3eHXEB/iY\nZ4SWFSHomgOoq9aehueXi2fpfGCQlXhhAQ0cSRdwAK5NmbzOZN0D4YmPKC3y\nIQzPxF5QXgrO2+I3jiJkVMKhXNn2SeL5HdabeExy72aB2JYeNI6v33m391+r\nftbFypxBnEOGSSD4i9Ut4z12rtUkbBPkz5kIwMSOuIliiI2e/CQdSUpKaMtB\noCgS+n6qIBXMD9CcIS2/7kicN+6ft8VKjxQKZ51h+eEHPNBnsKkGjRBYrsxe\n03ifECkFiYIS6erhkpczSm/zKBCe4fslndZ4vp2VARMxtfQ+uuerAsHy3sWe\ndULs8ILO5anKbuojvJ6bpEPDq2f0Jjy9kOcXQ+JXlOWZfs0tvHuHoEML/UuB\nzyJfr4pZRQYLmLBAJaBygPyxhs8oJ9iIDdN8e1vt9xVXxhYN18FMZCsAYIVr\ncDnrjNWUZOkhhCoOSRxE1XI/ckoS7NaIYtZTQWyGCvBHhUyvdgq9BdrbvXjQ\n1scz7L4658PddAnVLbM6LBkC8le5M8bnKdbfW5mpXa3vSKIDE4WvscQZ7Dz6\ny9KhAttlx155//VSkL5lHv7zWlfTJi/0CUpiamX6ZAVzFfh4wGCa2kpOMfQg\nYBl82yF5FjxVu05kYsCHvPeLZ37NLyt6HWLY0aBdNNDX0Y0YjfCXjUpCpIJi\n4aos\r\n=ph02\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDezYW4dvmSzNqzPQeBpt9tJzo0VCqnGpJxH0shU4skQAIgGrQh3RvLl9/cRVzJ/S4tcryYa9N72UYrjXniphwHIWU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0-main.20200904144019_1599230801316_0.20263908003072761"},"_hasShrinkwrap":false},"2.0.0":{"name":"@atomist/sdm","version":"2.0.0","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"aa16a215166e2da41f738d80e5c4037da281d459","_id":"@atomist/sdm@2.0.0","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-F4Nd78qzlsFYLL5MqS1XzstOvtV/aw38Rq+3Ywv9G7YhgdC18I46isZD+ls1ejVUggU3xxsaTIoSnKNIuRuvnw==","shasum":"a5444d034f00fda9c196d6d9fabd34e7b76a89a4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.0.tgz","fileCount":2444,"unpackedSize":9468060,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfUlaVCRA9TVsSAnZWagAAxKgP/2g6KJ278/j05kYchicr\nVpAzunQFpDh4dQIf26rDZ4hu+93txmkXruo1WgCRT7j55dgKYSva1d+4+gtC\nA1EVRxDgOGW5miFzyuMYztK6CbZRrlDlJPs6tjrcrk0Dk3CVGdnGlvDi95jW\n5THAlXoIM/bVYyGASnM07GmyKaPwJXWMLFkH0Rg7sL29Mr1DQuYp8hcO1Pex\nqgs2LohYKrxErIRV807Yv0pvp9AWVsJxsBB15MmKzkqmIVVcHTJMTpmitQa+\ngP9Trs3R69hiXE0kVvY/rQnXQSURKl6cgmgV3nw4HmFGmJjY9ZtekFcOHgDK\nNhsVwWkwt3SWef+J5oKeQu87i1tzFNh3WIkoabJ5P9HAHPWKyXRKOqSZrOAM\numJlOjLPU8XrCnF909yNBwPmACU0/2WRY0zzY+49lHKuLyw6Fl6YjKM0Bb1s\nl5oqmV9OLbB5oxpQtIpOPIzG6EVyCHW0/+y3gvcMU0MprEoZ7a7/oLhdvBx3\ni+Se4eMUKo01y2vQ2WsEAl/xJT/PKsYwMBrZ1TldbqhPj7cKxi/xtAjaEEi1\n0u2MxufvgqrsfEMJbFTVltFWW74p2W5WapVvIvJSrqruqM/A5GSB3tS8/7Af\nq6UDK4qMeqrpMgNY/kTVf3DA2EJ6WKTEwR8ciBBuKP0YP1WSlfepfk20Y5v5\ndjJd\r\n=owtz\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCvpIINabXwW2Jq8fVfPQpN+mwLfA8pxS1i0i4NrhW28wIgFcNePZ/AtbRc+IrUI7QXLbC0j5b2ZV3mZ+61P7r6rsU="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.0_1599231636664_0.574820901014641"},"_hasShrinkwrap":false},"2.0.1-main.20200911130604":{"name":"@atomist/sdm","version":"2.0.1-main.20200911130604","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"32da9acdd8130a93e6123795d9d56a37e8b8435f","_id":"@atomist/sdm@2.0.1-main.20200911130604","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-c5BE88M9xLmroesTHOvOMDDeSRHPmc/9+wYBv3r7qwxF6Nrb10HcRRIPFDOaF1HJDB6zlosWTRb06+goYP4/Kw==","shasum":"80192f15b3456f043692b6fd36cc62a2005f5ef2","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-main.20200911130604.tgz","fileCount":2444,"unpackedSize":9469114,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfW3fgCRA9TVsSAnZWagAArOAP/RjcVCeEnrS39gzHimj1\n006qY3Lm7P4VkpAS0ZD6eir+09WjSoiXebr3ZtOD1QWC4lKtRSI5bPYYSfPR\nMyScsAAQUb8Zl0mlbwwuAcdlOU0n8qboeHbJoDzIJnsADMMD7ZKi4aD9AGoP\nbkaZVPfxaFD5blD/RLBhEPcVXS1U10xyUDopQtIlOXO7jWL1IjHCg8c5ZhLv\n0Cqk8NJiidE/AI1RQA1908qJFhUw5LHvbUQf4eFnN/0l19WoAOi5AggvDXvo\nV+lOBWgMGzbUZwvZKY18V40ZZ4UzEcETlqJUSJM/qvjtDzLrOUy/qWlXzHHf\nG0/wZ6Ld3cm+sgFDbTQAomzTRC5o1LXhlU0lUAYeRiGTwWx3PwLy4UIBvkSB\nWs0aNhwV/4OpKCHM9TFo9kwOUzmJsbgoTWTMdB24ilb2bTN7Zi8eaE59Cq/a\n6EIqKfD6k4Nw4rem36r1CDnEsPKo5drbOl7a958pY5wFeP6kxKgYi3JKEvMq\nZoMNgUORarZWwXx2SnjhWqp3IIiqKQWK6dBFayWvZyHxqGgp03xNKn5h6s2B\nNzb0EGJI+JrYtWrOnCRClVyNRgQ3rEcvC7j/XstBNvk8HE6Bv7XbjM82gv9Y\nsuAmYBYbbY51WXOBr8yhmRt58iCybCaLaTbO16oC2XMfbBzGa+vt9NtHVU2P\no6ka\r\n=xbOg\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCp9EdkhHtNGjCQfxaH4LS7YGpZtRefTQTQpVMe9l+gwwIgZuPLKUrd7zwb/ftFyw+PHz7RfbhM+SFf+1I8tJzEVa4="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-main.20200911130604_1599829983400_0.2574051777799162"},"_hasShrinkwrap":false},"1.9.1-update-params-1.9.20200911133602":{"name":"@atomist/sdm","version":"1.9.1-update-params-1.9.20200911133602","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"69579301c04afa4e169abb03b17bd6affeb2b1f0","_id":"@atomist/sdm@1.9.1-update-params-1.9.20200911133602","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-N+udlagINwB/n230fbZvm3ra2CxKIrgiiw5rUv4hDnWx2kSXgdTQC+34uLJWRVLZHnfdP5MJsCJk80TKMPV0aQ==","shasum":"b1623c5c29883228ef3039a156244f974357a3ae","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-update-params-1.9.20200911133602.tgz","fileCount":1238,"unpackedSize":5799898,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJfW33zCRA9TVsSAnZWagAABt8P/RSbgmgbRfTj/Cuw4OKV\nGrUYlEOFc4lpXCAeCUZT5YMx8QqmZoF6ER/HDrlb7ke0VzFgKZvDNbm21Chc\nLOtQW3XNIxwyMs5JpoXkAxgW7QMudTZdvCUb+chTGR6W4ZPP/ob3Vf7OC+iI\nA/xUzBiKRT8LwuaCglLZy/O9SAWX9We/ZgKNIgdcEQDbmoSjmYA3edg//twW\njO8D/eFPstfkMewqcEsQCv4HlX6UhujAbSJsXi0xVrMc4TSQeNHdrj//4tvN\nPbiQVYZHfjK+bNb67UrFOwMSNpMi5WSLxc2M82eNqIlLJGo6/cYz4lDtVvPw\n3sthsbG6CqqDZimq5jA3y9YjFfaNw+BYaCEn0nXQF4HmSGv7tfgSNocHrE/3\n5pVjamxfPPFPaYSZQmPi1XJiKdF+wYLtXC8obfAjObXMw6AWwDIP0Ix+Skbz\nrnh6FCmiGpIg/WIzrKlpMESa3pC+OcyejEwu9cb/lTW9HdyHoavhsknZncdV\n7IPKOvhlVbHFOI1gbY5L6nAudUw+fxGgspWKTn2GUaDmu4taMfecwXrpSalw\nSO20eNnIwTaXsOgr4700i+71b/27C/hAbBQlVyiYLl1h0SdsuyogW2mrZL5W\nCEIugGpVGUHxx/jZonUpfXH/smnIu3ugXCTCKEk8ZQswSxfGnvFd5hY6/d4b\nI/38\r\n=peTD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAYqVY21dFh0CXO1hp/GJVN7LiJkpzBdQQWH17JT91WpAiAHNvlL6DW1U4FtjtWLy1e7bCZ7pFttWdBh79mfogmsqQ=="}]},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-update-params-1.9.20200911133602_1599831539170_0.25170434982141776"},"_hasShrinkwrap":false},"2.0.1-ipcrm-docker-changes.20201120154543":{"name":"@atomist/sdm","version":"2.0.1-ipcrm-docker-changes.20201120154543","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"883b055e78f65c1451c75a2ae580da6c14f8fdf9","_id":"@atomist/sdm@2.0.1-ipcrm-docker-changes.20201120154543","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-vJ+eFtqWjL92yoIrMjRzrB0B+HUw8COJ50F3psu/+o/Dg68y0MYtFe+Y/pg1LUWxIhT7Nr5EHqxO1qYrmq6yZA==","shasum":"45ea38c390d7c9883ca342e08efdc45080e96a3d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-ipcrm-docker-changes.20201120154543.tgz","fileCount":2444,"unpackedSize":9463842,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJft+ZyCRA9TVsSAnZWagAAKzkP/R/eW+cpI+3wALqbVxjT\n2tMt8vAt3NqLrv74ufevIepEc+/oWyqmesgCKVCTcRnXAmdz+uK+QeMctPA/\nKVzgdBBF7ODkJ7eIPSX9dSHfB5PHsIge3c7xAnOis7Kh06hIoT6Rc9yz9643\n38NS4uDfyHAZy15rorX2nLI3xl0Sa7MRWnIUr2rfs6TBQCylOxOgFsezQoJ3\ni747xNK4KaBws9Hps8n+BpWejh1TaaUVvsavl4aWg6pXOCB9ADljoMNBSZt+\nOraIsDKttDj6yrVRlR0+ku2aeFuCsHad8VM80FwPysMZOANIBtlpc+UpI9nO\n5Pes2dofCBmwv2nxjM/D/Y1AnbO6c4lorUb4+Fh8DYDdgImB2waK/yiZgdJD\nhgmpbSEfEAF+ywpLpn+5YOQUgIyqr/v/3NEfMMSa8T5nz0t6DzvSZ3fYofNQ\ngTUgGiB3xfYWN6vETv/BS+rePaJpQB47hUCX0w4hE8eLKyKFjM7J2FmHtvBX\nLnXoyQKGaa7X33SLnRRjxVnoD30b41e8PWBU3qmqhmR0d+6sLtQ/1i2AiUjY\nVfSDI4f5+WYOvetzog565ZYr82D2OsfuZctYO7uEnUAZx2dwHKDVXTZ63iIL\nsPOa+qfohAZzNXxlZm5mT0q+WmceDwCH7+5otxEeOcWfqoa4EBNcHB+QVfe1\nDjXZ\r\n=0wNi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCz1SgL6x4jbZWks+KxPGIDiOlR2IDgX1sWEDAZ4SD46QIgZRYlS/KuTSoRBudvAzFuLj/IQ9Xj68h1AhyhGLsYhIM="}]},"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-ipcrm-docker-changes.20201120154543_1605887601620_0.581422100364082"},"_hasShrinkwrap":false},"2.0.1-main.20201120171347":{"name":"@atomist/sdm","version":"2.0.1-main.20201120171347","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"aa474602351f68f84834d7cdb52d73afa047ef28","_id":"@atomist/sdm@2.0.1-main.20201120171347","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-4dx5hv055oYTUwwE01PjqW61g5qOJ9MG7H59NySP8qMJ1IzNOO7DNiPwQfrX6jcIJzyWKFRgERskll4cwWGCLQ==","shasum":"dabdf073a367f0ab20b0e5d970f92120e869ce54","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-main.20201120171347.tgz","fileCount":2444,"unpackedSize":9463826,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJft/rtCRA9TVsSAnZWagAA0cYQAIkzIi4xCz1n2p7+vpYd\nN3rTfxLkBy0aYfOFWgPIIEdKG+UNX6L0hEBPITioUaKC/gh9f+qf+sFOt2c2\nJ11sftWfQbG5pbNWwgTY1rr/zHF/97tSH3cS7+M5gB0qDQoEVdQLIP0ZxHIq\nS0odqmGnwaZGcqh1MN5uDZpQcM0iWgcoiC7WOxYEpTa6R5FptfVgIlkfTB+l\n+7lN3+oMvHUHzwosJ9MRqHyv7SiVRreQXm0zVoA5NRlJVdJdmgcxdXsfY7Vs\nHo9Kyu5RzLnJuy2riq75Zj+fkGzhPIBKkjH73i0Is4iHFwkzSuWD+9EnrS5e\n4tW0PqLX3uJ9IIaEJqQCVzWTcdtvE5krcEDS5iQ9Vej6tQ/Fmc8qIqhFVtgJ\nlJJs1K2ft83WOF9ssSF2HQcTfhD6PA5df80HafBms+QaByGWC8SW3Rm2CnKm\nZltVPUNVgN11YP4VvRDWcs2wFtzK7lrOOJFAnccaY1DMNeSlWwdgXz8klpJE\n9KtyOE+lPK+ECf4U2ROw+P4uofi8lybNy/O/6jRrlr99SfJml4uzHzQHJo11\nXrGk5M1TSSDaC+DZZwbLxewh41oT+u9yXrAMBz4030LDr7NUINJnC2ChdIDV\nxTEAfRXnz+RUnJs/Gnu14OrIRuKwCMFeMm5O3ItUQXcJ0YgkIAZOi/JP7SPu\nOkt/\r\n=80wj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC4XPIc0CcmrGt5HggOgrwaaDQFZArAoNmYG6GGv7zFZAiBTaF0v4uJQwzC/x1VXmAIeReTev0xfoo8bdP+WRgKxDg=="}]},"_npmUser":{"name":"atomist","email":"npm@atomist.com"},"maintainers":[{"name":"atomist","email":"npm@atomist.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-main.20201120171347_1605892844361_0.13735578865900933"},"_hasShrinkwrap":false},"2.0.1-main.20210108042958":{"name":"@atomist/sdm","version":"2.0.1-main.20210108042958","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"bb1adeb95bd492249f3b70783ffd83f64accee05","_id":"@atomist/sdm@2.0.1-main.20210108042958","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-XMHqntT6JW2rol6Mhq+7hTI+orolcemg+3P5pk0fz2NoMhl0SER9KuS2L25gznC+XCWkxYmC0ooKqI3Mognp0A==","shasum":"61b88130a09272cb8c411aad29744b6974fda7b7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-main.20210108042958.tgz","fileCount":2444,"unpackedSize":9463823,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJf9+IACRA9TVsSAnZWagAAycsP/3TLNlULwB/+jIjjIiii\nrbe9ZKjtj/z2LLMyEbp6eIPuiUyYnXcekw5+F5sNQ1USdF+BpUO0bJpG3Klk\nDL46dMNIy5fuWyV72Epgc44uPFH4KoReNr1uZZiHXdBu5Rgn+Cq692x+kwot\n9ZT76MSd3x87ZCY1sO49xIFaYcx3StGJBSBQRjQGL5YP7z/JNwZPzy2vGG0Z\nLaWJvmxX7BydnV6FG8YQ6Rec4KeuVTfejpkldQ9Q1ofadDqz1rzdjL8XbPyi\neRN7hNNDGB3AjvvT7WHBwasoTmUTX+fbAGjz8mWKr5cLAg5tKHXkeg3z/hbf\nkuUl7pv2uuoTx5P1hYl0lLdYhELB0uYbUfDhCXg+E9Jv9gGtlqQhSV9mkNA3\nkABfRaXqZga02sVLhDF2KmuGjd6bt/8guPSc46fRHmd3wkdxW6bJWMw6I0UU\nAE8UXA+waGlOsBJpRf+kIRdvGxMNZBPpriTyb5VpIhXG5KdRHAsljHOlo/kf\niJeg5klp2rC9qsocnlrn40qTixJEgmqbXeJNIXLEXczSG0xqMJqx3Pc3SWsS\nbiQv4tofDemdGLy0+8FGCHlhCo3U0svg9jr1f2u40qUHHeYw6mS41cgzhqn9\nF9teOKLKh1jCqSKP27BL5Q74SbxpOMp6OcAomAKXpHrw0Ja/uUdZ1yUTe/sl\nGIQ7\r\n=ae6h\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGk/txG6xBeJiX9I7nzXYB+Djf/9WyXtr30pzmvdjAYLAiEAno24AiITU2jQxgcE2CWo1g9rvmnNxVEwa8tagCWLQWM="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-main.20210108042958_1610080767697_0.9531123068021046"},"_hasShrinkwrap":false},"2.0.1-remove-skill-output.20210125164846":{"name":"@atomist/sdm","version":"2.0.1-remove-skill-output.20210125164846","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"98b9dd9bafce363ef317d3e3fdc8f3f5c72cbc4f","_id":"@atomist/sdm@2.0.1-remove-skill-output.20210125164846","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-wn92IesdSq90e4ARNR3xAKMUUxMh5la6J1tlDmx5sZeEiF2rH7Ixd+9ssyiAkXYt2oZe5PdkQi1OtrvfE4V/LA==","shasum":"53db96e46469fd29e8164ee0a7e25fabee879e6f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-remove-skill-output.20210125164846.tgz","fileCount":2425,"unpackedSize":9997012,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDvgXCRA9TVsSAnZWagAATv8QAKLO0ixI6UUMc7GjyjNR\n7wQcIDRtdUyxHOLGGu7MHfX4PMtZaGjRvxMV31rXsiY6x6L4bKkUvSKVYtcr\nHx86NYNZlPf9jcq4wCZepjJF5Nop8SUIdb3Laq8EZyzsojfSocv15GNiUJsL\nFtNcAVjAfW+tagTaIwI2Kr1gB0l2y03y+4m01mmMcH8/9TyDxFlj846qiCZ/\nTdJVeTg5ZC5C39KHfFjted1/PgnFnEge8q2UzL8G+8pxXU1zVbgBkDwcxRYu\n3HnCaWrP1lg9krQxM8yL5aroK2WzOBVufiU1Z9PvBAPIY7qQpJvTv8k+jUVD\nIq7JSRb+Wm8Az+NzDDr/8DOn4n4NE+9Vht0EdcmD9NDQpDrpcCH22BjJLVY+\nEpkH63TQsT45qJE9Wfb0ldR7yo+ReuT7lVpGxTRZUeU+xEy4B+f8rALSMt88\n+YugEdrPQsbnW8YWArJQuYn2KBIYaGLtJmpZQNES1s7CXCixhxUlsX1iEXmG\nG7HR49whZhU9+tICvKU4MtjrbLqL6HM49te3zDwVjRjy/SAxgyzJ/p8OBT22\nOl1OJAWiorgURxgea7Sf4Tddxg53x0OS3ekFTt/yrbwOUTbHmDqTdUEsSCZr\nmhS0xDFEO/Y/Rm4cNyPv/lQotmR5/2Ksf8UdKRB1eHoMAHlRfWU/YXYGQiWo\nJ4Ay\r\n=oqDF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDqUKugk0HcJa5SFjxJ8zqbUSk8QOqI2bPiHxZBsG9PlwIhAJC/iTvRc+JfkAoMIYfRhEKpKkf5QMy5kOi55hQhGubj"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-remove-skill-output.20210125164846_1611593750998_0.14210571661024685"},"_hasShrinkwrap":false},"2.0.1-remove-skill-output.20210125165034":{"name":"@atomist/sdm","version":"2.0.1-remove-skill-output.20210125165034","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"98b9dd9bafce363ef317d3e3fdc8f3f5c72cbc4f","_id":"@atomist/sdm@2.0.1-remove-skill-output.20210125165034","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-tjFF+cHAEs5nvQvgaJZxK75vEoomrzeHC8X9BLt+odpfK70q/ejNBm163zbXymzYszL9MizDpHXOOX/DI4QoVw==","shasum":"965b25a2485a156fa910a5ed54aa0e35da5e6918","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-remove-skill-output.20210125165034.tgz","fileCount":2425,"unpackedSize":9997012,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDviWCRA9TVsSAnZWagAAzN0QAIM24BID8kR+O/9cYnKH\nfF7dwdyvESQ9VFJTJiMnqD0rs55VejSRHN7HUxRoEZQ3lbJTSCjRUeBsq9oH\np5bbW70r7gLVd+fhM5sIfjwT3WS/GMOGxX/HPL/XRP1dmsp+4st8G0eWbQCC\nrglZkVs96XCiucBrxDXTOwzjUFGyHLor1/PLqpTPCL3Tv95IVHTdKxjKRVDv\n3WAvzNdhZIIr60c25Dj45K0pGZVlY5v5GnNIdl8NfHBc6QyAfA8/Za65jpUv\ns39L9/kpr2TrooYpCDkpfiPag8CJVhdujo1sSyCBR6UMnZvj/GPwmUxnqhQ3\neIl8QX9wgrnNGFJ3iNKSTzCJCb9HJ9cx//IqvG74El/Z4qxHr1CU5sYem6uU\nPicV2OR7pwy6DuexybauwOHGhFo/QVzJVlxxQqk1/h7Yyj0HXgC5WoT/aNlr\nFgE5+Dbp4Hj+pE/qWUheAPN7q+R4BXHLKoNl3VUmTCciltHr2QCDiY7jiDId\n2Dc8+oSMyIeYa0uPuuiCK6NC48eY3EDn/cxLS+DEMoZpLYyyN7knXz5Mmn8m\nAvkEcggJKgtb7Wza9BwcCN09NtDzfCFYM7bImy++iCPiy4hpFosJdyFBmFup\nhHchFdLiefE4jkpXBoBwZ4rtu9sTERhubDxZCpc68oS+9lslmHMy3Ghdb4Iq\nHax5\r\n=QNuy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFqyO3yddRhmtZfzJYJHiZ8cbXk2SRu9N9tRvBBIkv4mAiEA57GrAipsYJbyO7I4J9UDiu3SyYRkX4g0AcNNSAaI0vo="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-remove-skill-output.20210125165034_1611593877739_0.19550584783057845"},"_hasShrinkwrap":false},"2.0.1-main.20210125170131":{"name":"@atomist/sdm","version":"2.0.1-main.20210125170131","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"079495edfa3aa3ed5e3ac03df2b0188e228bfc8a","_id":"@atomist/sdm@2.0.1-main.20210125170131","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-FZR9UdopF5FWLZ+7kdbD/stA7IqwaFETjLWQbob3IdWMPzx7IQ3VkNqYSFw88PXxI6sU+hvLa5y3j3YPAyMLIw==","shasum":"0792b1dca985ea5c8768dfbd58a394c646d81c41","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.0.1-main.20210125170131.tgz","fileCount":2425,"unpackedSize":9997165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDvswCRA9TVsSAnZWagAARLUP/0ARNbOliz3nSJFOOr0m\nyoYJ5Z1U+yR2F6uLy7uLAp0lRBmB/1olxcdSYYMep1WZFyIQH1HaU69dIvK+\nQ5DTB26mzKrwPS0prqTlDcHYiFKNmYYgq9SVhvnmWlKZ+Kuvd2D7tKdtMldV\n9DWWECzxdJXFj5+3xPlk2OdJ8TbdPgWmBNy5sKGlVHLTG3xN9Lz0r+4LVatX\nFWcPNoeBlmdV3ueEokjQI45p2ydiD7lciHh5yiPvkGFmiIbumyaDAV7cbo5N\nXxTth2914NRmNJYjeJZquxFpnfmMfm81juW9Eyht9qNN7Zioy0+oGFpv2MhH\nbvWm/lyoaBwnji75MgPb7VyeWJ9s/KYBaGbeyQ2PuaB85gRhBGasdXXVlw4f\nTk8URCfogJ5Jp2oBkOHbMy4PtfXK+Mrm5Uv5l9I+ud1zKXzjNhsoNBYUhC4H\nAiGLmAGHnChXZy4GpVsQV869BpVv8BN0Ak04xbNctKkM3wkbc0vzqD2ct8TU\nbdx/G1W4fXecjjgs3hRa8WNJc4lXBMIn4UF/lU9es8geDP/BHJ/LP81t7WWV\ncoGGlxb8n0YYSJZh5FyWWmkUKop7EkowzeOL1k3ndzI8E/2/VLJtwgnBdgy7\nMhuOaBalMoKJMHikTHkYQ6pynTCtu+0coJu/XQhTThwfe8rFBkipcxd/8+3l\nlYTY\r\n=xYCd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDyPOfhoMns85cX1TUkamNa+U+c88YRUojuwYLX6mumygIhANtB7NKuA674FhlRwxHW3Ij/+BWsr/3V+UsTGkHxKIJ7"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.0.1-main.20210125170131_1611594543326_0.5673764889909851"},"_hasShrinkwrap":false},"2.1.0-main.20210125172757":{"name":"@atomist/sdm","version":"2.1.0-main.20210125172757","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"079495edfa3aa3ed5e3ac03df2b0188e228bfc8a","_id":"@atomist/sdm@2.1.0-main.20210125172757","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-FKNV3BL8r66xHwiZbiXmfkFtT9koyB/fOh84NhXRavGtCb+b933rIjhrho1lqnu4JZBKd3KtxwfZG9Oelszyig==","shasum":"b6ed03508cab2194228b982f5d75197fb02f9ff7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.0-main.20210125172757.tgz","fileCount":2425,"unpackedSize":9997165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDwFfCRA9TVsSAnZWagAA8aQQAIggEUzBHneuQpDkG0Kz\nFBEKUSitx5uBL3Pus+ntBxBoNpD7bKLo6GNEIDM18i0rMLMyY3zqBLLwda+o\nJX8H1Yz+wH6PeZlYXGrFnBCincz3uCDwtre2t3ogw75To3NGl+TfIoE8mD0g\nIxpGZR91cw/3MdADH4boPBGwPuzb9xhw0VL/1cchRPomWfdehyZKEJD+GQV8\nqwWwiqdfyXGt3GZhH1ttT0bHmbVATG38q0WgXtq+io1IUC5xRygDthkFrvQB\nIR9E3j0FWI2v/YDzOe63Pzr7b43oxx/pzbIWQ4zTJtl7W6YDQUtFxmB51FUz\n+HC5SKqV5ZJKXGUwVGIIQnBw6MCn4uQFbmNvIRRIL7PRqmNbNqpdEtnP+iuu\neM9NFnuYmY/SLf/Qr9VjIunLAiZKI5xEuQBxKXDY+EYR2BaSE7ioR+91BS42\nc0SB292M8OEvQzaaiGYf6MoXZr2dlN+I3aNImcgJXZ6wcsP70QiiRrkYK6+m\neC1UXKLB/APwJoFep2UWtdcbYsSXThlzXiGJ3u+mu+pFvmDzGs5INxaPuycm\nxpt4fCi6RStlOypPuckoax2Q5Ka8pygHZqzweh3RFozTGONkv6BWnJjwoh3E\n06XYbOfTPqdNfXtK3ruxBwA/Rqdb6EiOzGHXX4u0NQTMld6qxoVlBgnUrEdg\n71vd\r\n=N6OC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHqKl5P930vSaj9Fl4nP5mtmmKA+2IBImN0g2k85K2tGAiEAnxWjQ73rxPRHWQF09UpC/QQQ/rJcDuMBctcUZXampCA="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.0-main.20210125172757_1611596111400_0.8372373560404598"},"_hasShrinkwrap":false},"2.1.0-main.20210125174106":{"name":"@atomist/sdm","version":"2.1.0-main.20210125174106","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"cb51a66f3b1d6dba060d95b3a3c9015b79ac509b","_id":"@atomist/sdm@2.1.0-main.20210125174106","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-18entFee1AoVDJOXGbUZu72DkL66wpN0LB4byX8xe80jLh+jtIukgoed5BcaUggnclGc2eigwRV4CQFCRxKMbA==","shasum":"e3401a5f5958e7fe1071d9a2169b5b7ad47b2e35","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.0-main.20210125174106.tgz","fileCount":2425,"unpackedSize":9997165,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDwRUCRA9TVsSAnZWagAAb6YP/3ZWip3CoOoj9DternuW\nbBQOQUnYGosjkKbAzpkGhfFteQREg11Sb51ZyVjlr+1dLuXFGcgUE1G2hoCl\n2y2Jte0EEaSOAnsJukfaaqojIJPO+2aFE2S0D25dyezkAXaBkto1iF+jwHHR\nDeXG5TAUGAJ+0MHr/4Rb/DZsRX067ZJ5Vt7JouB9sER7i/HfML0HfuA9fkwi\nnbq3/B45I8eeQ3ac1THQa/lF4IzY2r+q0vHIxZ5a5CziwP2DIfD5XnWq3OS4\n11WBLUCsUW2roHkL4I4d7zKAcT/6+4Iq69nKywSguUzqdP1eNtBMK82NIN/v\nP9rrf/zeqP5/SsN1lGI9kxVZUskftlEBWqherMBcggFFqx8UOHB5mrXXdrY6\nWlBMo6DoPuwnv4nfTjmStzObQA4QD87iREFVUg5bBNNK3dCFHO36ayKUr7h9\nZpINfQwGN7CKVLamk3BroIfRMhMOOiA0bHkNH9l5HQWanpjHvlerML1KYLId\nAmLkoIFcE51KYFxfAZgSWhmH4DzYnZ3kcGTyxdHipZpNa6ozbs2umDhWbwsm\nK3y5LQXbV+ktRwapc+geAKsr5TXIV3y2bm8ld068hVZUNHyvdyZjokyryyQp\nsUkjOV6+Xo4TRA3mSIaNL6VPLfXxb/Ug2KZ3Bn52uu1PQb6HSZ7cf6PhBo+i\nMSoj\r\n=w4Og\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCID/L34rM6xW5VjffugUeNgJ/VOR+M/tw2mYFcXy76wBsAiB6t3AoyWs1fcqE6BtpHl3hSBpIFoTSowXwQGUMP9NLIw=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.0-main.20210125174106_1611596883757_0.17851164901479688"},"_hasShrinkwrap":false},"2.1.0":{"name":"@atomist/sdm","version":"2.1.0","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.1.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.3.0","@kubernetes/client-node":"^0.12.0","@octokit/rest":"^18.0.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.1","@types/js-yaml":"^3.12.2","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.1.7","@types/license-checker":"^25.0.0","@types/lodash":"^4.14.161","@types/micromatch":"^4.0.1","@types/node":"^14.6.4","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.4","@types/semver":"^7.3.1","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.4","app-root-path":"^3.0.0","aws-sdk":"^2.713.0","axios":"^0.19.0","base64-js":"^1.3.0","camelcase-keys":"^6.1.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.4","dockerfile-ast":"0.0.27","fast-glob":"^3.2.4","find-up":"^4.0.0","flat":"^4.1.0","fs-extra":"^9.0.1","js-yaml":"^3.13.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.2.2","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.24.0","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.1","request":"^2.88.2","semver":"^7.3.2","sha-regex":"^1.0.4","spdx-license-list":"^6.2.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.0","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.15.1","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.0.3","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.17.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.1.3","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.0.5","rimraf":"^3.0.0","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.0.0","tslint":"^6.1.3","typedoc":"^0.19.0","typescript":"^4.0.2"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"3f989950e347ab1c9a58a61ce3f4805477d6c197","_id":"@atomist/sdm@2.1.0","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-L+ekLAaumSdJcyfRfNnd16SjN/GyAHVAPdOi3gMwPtRKCw6F6EBNJCZIjvozHIerq7RUu6viaFNzIqGe7i87lQ==","shasum":"2132eeb60a2c9534d88d4cbc638d3350a23b2f1d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.0.tgz","fileCount":2425,"unpackedSize":9997145,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgDwi4CRA9TVsSAnZWagAAQfkP/iTj0gLHiZMygyHvqs8f\nAJcMhAsRRSK/y4VN5jBpg/JmWmcUO8743e+x9SZXCbXiMEcfdvrkikCx4f7+\nvfQswDNQSJO7CngZxQ+k0ehgZ534WjhQ6F1ywnxwEA+xtD2xWuYLKIvjFKIl\net/dMuRAwr2oqnTVhfdZvVPrzMypJfbC3IK9dkYDxBGctrOZmSL/h9Elz4co\nwwN2j7GWYNpgG5lc0he3vRux8BEnls9hJkoNp3ks4hALMhDtHdBzdkoxBNiG\niGGdShwz0lDV4nSAblsVzAqtFt4/c8HteNDJnUI2/VElph6RqYrKn2OBDFfF\ntNDSPtpat8GzA5tYqvTE1aTXWdU23/BqqZxMdRDbAIIdQVrNg/AGaQ5NkSsI\nqXU1eUw0fZDd0jCLZpLGvBFZjYPZKL2KXG1iPVt7Zyvm1UNnjWdmLN2rCRv8\nvSXtXOGuJVKG2na2wgzO8cRar8UTNfQS+Zxe5JID7t45d0BiRDZKsVu8fNwB\nUyPJUSwb0wcRS1qAu6xn5rB/J/UYI++B07wt7JYO2sCcrOQa6YyyvUQtJP2B\nL3yKzyooPE3rlKSqqiI/JARIvfO0/5SPBDWuTH9HEk1hnzlLuD2930n6lBj6\nZJb5A8yemvzBS72IJu41pNcHnUr3Vkz7vRJTEQNyyweRFmR1Mdp2Y8OJTBbc\nJVLj\r\n=/Cie\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBDTn2qfAMhIcsPttrc348bSOhNSeHukYMoF37vMLrY2AiBXnwf5qM6RLxWC9JxrPDVNEMWLJvUVN6dwSnyhM7M3aA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.0_1611598007629_0.1894489407270299"},"_hasShrinkwrap":false},"2.1.1-main.20210203223457":{"name":"@atomist/sdm","version":"2.1.1-main.20210203223457","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.0.15","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.22","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.834.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"67b96054c1304bfe37ef547d92b6b54c82a6d305","_id":"@atomist/sdm@2.1.1-main.20210203223457","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-q4UE1niqwzTmWHLewsXCRtcj0KZa+qYGWRTK4QI7ptCuxmTxreVQzrPjr0+Zbi7ZuoLifnFQlLq7F6Hn1sluyA==","shasum":"fc2b8904bf6c6e413208dab05ebb089fad60dccb","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210203223457.tgz","fileCount":2425,"unpackedSize":10000519,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGybYCRA9TVsSAnZWagAAoAAP/R9Oigo9KJfpON6vic8i\nIRAE/iG6jW5hLJ1iSy/ZhBDL+oVHRRYNwD3HGBMI6NWLYGPJOR82mol5JwZX\noI/8ejmer/Gsq/H9cyX2X8R7tTNxMMbNb3Sm7epG6T9sCO6U22l2HneEm+Xy\n5uBR4eORGXzmIjV3bfUfv0SZt14UwKFT+udFs+wC+1EAe8PXjdm1Ju0qLc3i\nhu271z9keMkphdVdjIBbbNlS/Lmr5CenaQUDytpKwOcMsctU6XqtoO1MKJho\nMPbDYcoxNTEtK7PO0/oguDBvuM3/XGaMYKOjO8Gv6ue6rT2Ib8PYwgpIG9mW\ng+b5uOvQdoM1x14ork2h30u48y3h2h/Sq5YsusBNi6ufee5a3eK9CQZt+7KE\nO0Rwf5mYVU/nVlkb++MX510sVS4xs7MdypnI1oSgnD2Hw+HmFocGCL36XhDN\nVRb9rwx6h7otbqElvgsh+kwKTtMrH8XMmLa/WgCEkj+v+Oe/ZY1XbH0kSn3q\ngdij6xxVd1Gy+hMYm2jL4cpVr8/Esn0+zYZ+WesQUOD4SvmiFiAtCEdANGE5\nFgT1EIQ0BWA+jo+9RYGJ/MoEG/VFKT8VUBc4oJREW36/u8G0qguVfOjQsMnv\n7wF9uMy66SfQef2FzBYa8hvzv1Fs2zWzEJmF/nf7H5veuWbpIk2ZnYX7f5/J\nRJHe\r\n=YYAG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHJa53/qdyu10SW/JEBsw5GmLK5VAmRfp5/m1FD/w+ouAiEAqyyoGPQ1rFSlEpfaGIPh6ve2ZJt8b/vzXjoM4FX5vUA="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210203223457_1612392151004_0.7683916592932711"},"_hasShrinkwrap":false},"2.1.1-atomist.npm-update-main.20210203223639":{"name":"@atomist/sdm","version":"2.1.1-atomist.npm-update-main.20210203223639","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.22","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.836.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"45ff8fae9370013cf1f04fb8760e3df0ca513ac8","_id":"@atomist/sdm@2.1.1-atomist.npm-update-main.20210203223639","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-sQ09u4iZb6bRy01/ZPIWr28MVIKIeBdVZ43r3JSr1gLVxyAzsn93nDMAK74TuI9DZGbcP+gh2+M7P9vLqWsLFg==","shasum":"6a15088f3f90d832ef7f2acd5ace33054602a575","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-atomist.npm-update-main.20210203223639.tgz","fileCount":2425,"unpackedSize":10000537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGydqCRA9TVsSAnZWagAAiX0QAJBxO//AiNnuTTsQAD+A\nGGyzsGabwTtV61gEJ3NlNOCasEAIHH8NMVdMCtACsJpju3cKim8NemvT7/oO\nRJPPsVyH6xZn8e2+pmlh/tT6JwO8RI3scjH0UR+Rw0CSpffVw0NExMI9PWR0\ntRHnKTG+yUrPswa5uZpuQ4KxNvr4etJPr8bLaVIXg0l6thU7KOIOVOpIxwC+\nnqitzjql7+AEvJLiRkaT/KUmi7qM/slhbNt4jGJ7UeYEGLh9+kg3f2Cm3Xe/\nzsyOcW5NT039GXbtI3PYmwImtyuHkW+z4nkNORcxTXi705XrbiSQQ0FQITFR\nBSMCkHX5KfMfo6/E27AaeCLYO3tpLXin8OVzwCplgTQupqWcBZ5I2rP3E1XT\nV1PS0lUjBqVLRtR0A+IOkFUA9OnXQQMilYqYOup3o/LfbGnvcD8W2ZmIa2ii\naEKs6C3qQcG7hqqtRE1B5fzJRGdFHTMuNHdSOFId7ABkrrY0axo8v24qGeWo\nPb44obiPiyisqh349qjABk4LuFu8QvayNtGVXtKUEQlt4oL9wgkLIYBDPDML\nnpx/4lCXgI42/oRnI6M0OxH7JmDf3OQDmeF3NNZQ239I5q8pLTYWKTt6/SxB\nLM/7bVgcnniPzhpkuYCQwQt2N5yiGYCAob6bBENNW4V2FBhriD/TIiBVYL53\n5ayN\r\n=Fys8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFyRSpOwy3iTQ+pZc5vWy0VB6SM2CgLr30so5SFtt6nAAiEAr+z5ekB+x9PXzNjj+gN/4zZJzFza3a9XWFvDFOlSUQ8="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-atomist.npm-update-main.20210203223639_1612392297706_0.6092071806690902"},"_hasShrinkwrap":false},"2.1.1-main.20210203225026":{"name":"@atomist/sdm","version":"2.1.1-main.20210203225026","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.22","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.836.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"64fce8fa5cb633999a5404a3645abda9ae804495","_id":"@atomist/sdm@2.1.1-main.20210203225026","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Z/GEzOAFUDBxnMgWV779pvyeXseFalCxpM3C0isVJZarVFV7HTDPIIC3ninvZRfq/ViBX/WzX9zwWWE13zB08g==","shasum":"baa64e92c35786b19914d840917af131e12c65a1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210203225026.tgz","fileCount":2425,"unpackedSize":10000518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgGyp+CRA9TVsSAnZWagAAcWsP/0vo+37tFhDvEqlOPDlg\nkuHne80U/AT8cljVn4Mg7ZXOZReoELcKXUaCY3biHjPntj0ImFKDVQJ5geMG\nNabIcd5B+dz0qti8JiSc+B8+4Rqb//0XJH4GEnJysB1n6cNtPsJXDaTxoo+B\n9/HSMnq25+1MztdQ79/8ycFONZZNydfRinvcadK2quW75mGJDn0ctblPSBs7\nPqtuAfEIHcwvDaU3qZZ2DhuCU0k5JGpCNrdjp6hwEnEpQl3EGhn+yyGuFII4\nFBjGO30NFksCLapT/hNwBqFCMcXkbkCiIwBbisT+DT4pUtBISu7WF/i1zt6O\n7fhRmupwZbZfGqb3NuB0gokKSmN36/hn3t8VbT/8BQ4R23h9jmpFPzLFY5IA\nbxPfRyEeJQtuQVAA+YQofdi5Hgngo4hgbUKz3Zp6gfK95eeWu0GRqbFPkwO5\n9y/dJMKOwEwIuadKB+ZH4JTG2+wJPZtoadV9gnWvP7BwGddZRNdw+ZIR8NKa\nLfjHNZ76Yf0ofWxzc9Oc4+qnO+rIJAHohCnbX/yJoyBLQktcGWwusXIUv87T\nqsUNJ+LP3fBw7jNs2JRviIAGKIv93xxCHZbK8u3hCXEPEXeaUjqBaVSebJ2D\nF67RHfb6DTm+MDvHS0frI0ZWPSfUES0R1/6xbT+qfj1equ/LwWRkXlLO/THg\nV1iN\r\n=tIPN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDxExH0qvXubM1GY6NJyKqjWzTwlLzI9x3Zdh3EQtkZrAiAEQj7DgygWzftk1QKMiLrFZhkA28wypNt+j1sRoR5lnQ=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210203225026_1612393086238_0.19890854613663467"},"_hasShrinkwrap":false},"2.1.1-atomist.npm-update-main.20210204150539":{"name":"@atomist/sdm","version":"2.1.1-atomist.npm-update-main.20210204150539","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.836.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"10a341c23f5e57c4687bfa733ab0332e1590eedf","_id":"@atomist/sdm@2.1.1-atomist.npm-update-main.20210204150539","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-vZn82l2mcDvRZFnWQrtNaAbiVIsZk1mF4IZHkTGrxn730RFJ2UkW7cjvb/4gOdpKLVV+u9O+5GOIU26/+KdANw==","shasum":"71a0b96e0458a6e18811db40975ca01695f3564d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-atomist.npm-update-main.20210204150539.tgz","fileCount":2425,"unpackedSize":10000537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHA8JCRA9TVsSAnZWagAAQyAP/1ZAy5kbODz2W07s8H8b\nWn477iFlPHoqQPQm7LQaDR6DuPHIq3PyffWHKwWq5TBwR+09ST3SBoNRLBcn\n58L/wtdusdlLjbMYvsBcxj/Z6B9tye8i9kN7vlm5yW4L9Lzb2P36kKufcoxZ\n+NT/9Rpfvafxa2s86JwBYIQjM7RJ/lydGW+0LGfLMdkO2GEUlGDoBQv+OCQa\n6qZnYwaoNRPmSIyMRGrY7i/dx0unURATj5lhnjBz5MF72vMMIKSL4GyKoRHz\ncH5TG1PWKVq84baSkHXIZi2ORUWdqk7SJi59/WhDKdFa6ufWe6Jqk62CJrLe\nBRhfCw0iuhqAQ5T07IH/LJ/eEh2NW4AvVW9r6vjgfaV3Rj5tdoXD+MZCnVZh\nZENUgI5THLfQ3S+Gt+KOrOGKaZ+8eIm44rKBRYfuo6VfwWXdm9Y+hx4KoZJu\nLBXvm1Y6mJT7ylpBEsDMMWiVhuhvEENo7tEdz0uVhgxYMNGBOCYmqvsIQmrk\n6YRRUXz3sjdJqx5hBcxAjZGnuBmSPW/L70m4rBARF4DKD4MFnG/t9rF7lxns\ncHgbefRNsg0t2i45MEI+K9CsmTqID8f2sc+p3vAXkW6ngZCeAcJHhZIO1X47\npDsr6LLO7gpXgSPeOGf0Ao00oD3lFgoqcqdvkjfFvfoRuF4IPCtgfS47suXs\nhuxU\r\n=34MR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBBPSDVDV7ScBB56+EcRM4Y4evHfhoPwuWzutdA2No0UAiAHEYg9wISF0y38OUwLVSqzmYX2vqE8SovpKXt6ZVeXNw=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-atomist.npm-update-main.20210204150539_1612451593306_0.26042279605428864"},"_hasShrinkwrap":false},"2.1.1-main.20210204151835":{"name":"@atomist/sdm","version":"2.1.1-main.20210204151835","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.836.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b0374274b0bd4fbc428af78060f53a90e5923809","_id":"@atomist/sdm@2.1.1-main.20210204151835","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-6Ys99SlfmbwSi/LssibQlVzs2CVaE7eVV5LH1hfZFle4iPwll85D/qmvaPFYbizLUyvLz9DwcsPW269+ATiqUg==","shasum":"a73f4375a9bab6d6574b14bc4abbca55ceb0c824","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210204151835.tgz","fileCount":2425,"unpackedSize":10000518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHBIjCRA9TVsSAnZWagAAI9cP/3AgOQx/MztY/3yANKri\n7D2XRe7VaTWv9bT0g+/45VVc5nG0Ev5MAuEot73CV+13ExLGGvCKbYGwWjhH\n+GGHUXcQ3G2BK9ut4ubRBC9AUFDAgyfL1i0QrA+xpPeXg5TScINS8Kcdbv8L\nPTgNhlYeeKIdjQOjjlquTHV5Aa3BGJKCDlXRYeJWBBfDtyz+s9kCvDOgrYwO\nnrRHp3WnCUBn9cLEla2YstpuGqDHJhR5qWs3PCaeoEByOAlf99TjHnhwQmqB\nylukRXRYE1p/wdVjud89/xx1CnRhXJtHSAsj9bv8kh6Ayzz6QvlOXN2DSORH\n93aiE9oVzpsj4w+XxMUD1Nja2Abt5Klwnw8c8Pl1Y0CsdQtQJn+n6u43Yqwr\nIfsvS5YBGwB5yJb+VK919SZf0X8YH9i0NAR3SpuOfjGdxDdP4nDx/d3NbA6Q\n6WmQPQYyY2K9d9SduGcjj0PD9ezFZxD5cQsZzxDjhEBJG0z9J8dhZGNzO2GQ\nYaSDgqkdUDHwPWXWPp54DUWpPGPmsxv+uCBC4EAI7vP99AC44SscQRN4NPcx\n5aVoyLNcBl2QZRDCl5TgHW4HujZiTMQH4jKesQc6DwcwsgpxH9GhbUdGP6hP\nyqfzzJ8MylFY458OiWAQmd8HZ1Quu5c6ZbOzYp9jRVgRa6Din9Jcn/UanPoG\nTVNR\r\n=0Qhd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICZX2QI12pJEyfzeBI1nrdTe60QxDr9IEcekpYLQFN5JAiAfBXQqebI1lhgXqNijYzaIdIkgvxMjYix9RAQ8d2s04A=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210204151835_1612452387043_0.20775855511614694"},"_hasShrinkwrap":false},"2.1.1-atomist.npm-update-main.20210205150724":{"name":"@atomist/sdm","version":"2.1.1-atomist.npm-update-main.20210205150724","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.838.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"320bcfd9aab0cd5412ad7983acfd8f67cbe1fa00","_id":"@atomist/sdm@2.1.1-atomist.npm-update-main.20210205150724","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-9d0hHnijT43kuUiiyJrpVunesuNeAL6zXt0PEPcf1no1Q1lETT5H3VO8iGyjzyghiRRMgzjyeYvM0ChCYifs7A==","shasum":"df9a669196fb14e65fd39bdafe63eb45de6f57a7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-atomist.npm-update-main.20210205150724.tgz","fileCount":2425,"unpackedSize":10000537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHWFNCRA9TVsSAnZWagAAPg8P+wc7PfW0W64kQ2L9PY+b\nnlf4wkkHQqA84UpmqWZ8mdX2yhWqnj1R413/Be6424HVtW4H/bbmP7zThjvq\nYa4UbSIxrISh6J6ykOGwoHyTcoG7/qJ68rMxWHUhJS5Ld48h2mI/tms2jRvb\nyaxbJp6U2AtpanvD8/oROHyW15F+pciBspsbvpCNxbQLpBSY7H/xKCiSkEEL\n5+kNfga0ON+A0vI6qXgS/LcyorS/ln1xNu980LN2fvzWuswsgqBGIk4HEiit\nPu/2ZULIN+clfQ3W9QA3TIbSdc37DQ8CPJrkVqLQJpU65mDn+jxlJ83cHDOv\naySPip5kU481fQwO3ot3GdGW8LGNTuOEJOKZlksIkuKqCbsLH86DepGzHQrd\nHWMVDCq/h8uTPYXE3OJXiTchZOckGOjylDdV1VnW82ambYUhnY992GK0iNXG\nVpnpo/vPFWLlUY/FU9B1Y+qU5LFfo+QxhjXyBjmJ+oKmZsWF4tgV7RRhzID4\npnZEsAiIiC895Yr7u5CUJYSDnH2yQtd7FTBKgD3tc8O7Ps3ZDMlCTD44vfKc\nFKlT5HC5l70Qne5mHJPox8elXGjmB2yEuNM9WPEayYmW1dlVNp2xDpfV4TUb\nmvo0AG1LIxIPw4VBeK/LuOrwgahTTnsdp91eDMzy+54WbvQ61XjAgYvRMRhD\nZ9jt\r\n=mqRM\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDfLgkMRBQsoInjGlL+AW56cTBlA5FTTyASZKIs2WbiEAiEAwXmmv8vhPpnunbK1aStpkIjAsDrRFq2vOmmhLhx1grU="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-atomist.npm-update-main.20210205150724_1612538188624_0.0032503007897213276"},"_hasShrinkwrap":false},"2.1.1-main.20210205151101":{"name":"@atomist/sdm","version":"2.1.1-main.20210205151101","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.838.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"41eb3b3f63fbceff1a7d28af97632954e82a4b85","_id":"@atomist/sdm@2.1.1-main.20210205151101","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Xbj65UHfJ3ix0DtIQux0nPyhEXMSsz5ECedYYdfTOvh6D+9I4wZ1iIXLMu2TJr15KaMkFIh7VaTU4kdpVSQFbA==","shasum":"1ef61c77c93f5d26c6647f1f7dc5a45cd68f3f97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210205151101.tgz","fileCount":2425,"unpackedSize":10000518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgHWIGCRA9TVsSAnZWagAA4xoP/0e79xE1nwLpG7ScoKMS\nojwXDTR7JNMUA2GraD7zcLX84CFNAYgtP89JmUp/l+RksK1NPGmjvNLU7yF0\n8teNjJ9QcVN4r4QFabv7nxl0nW0873RS6MgmLV9CtSWPmb6hkDnuEwSRSpyl\nA7tyl5oU/2qGodjb3t6jts1Xp9QmJh/9B5xUIp8+tOlmW3biRG2fPz7Rvp1V\nXarpgobbQ7aSn8j5COAb5cPRkuzAkc7YTJX2Thq6eaPdEj21sVU7W2tQpWPu\ncMB2My+LiOxTLofV2XckwdZoo61AtOaVjA6kB7Keat9SC/yr6Y0n/hKyXYef\nuPjIMa2d9qXMZpa+P7l1ynp2Tpudl8eS+gj9RghRm+ANZQ+dK0ToEYUys7zZ\nf/EuUzIX5J2PwQGzpDHTTPvfSSmy1b2aQpHiygfOESgaHnepHkp/bdOE0qwe\njr9ED5qd2ShG9mgLUixh4mmtJfL9Ldc8gDxfHd80yAA9oj8DV7yip9rNLcQT\njcK3LqSKlfWsRVHaSKqFSDB/Hf8ziOJPEvIk8bcrr8lRnXk49YM7poF1WTgl\nsnNyWBcjQVmtejKnF/8g83+/oaVcvf6tP6FyxUWsLfDDtKy4zvPTUtM55HHQ\ntrQFTinFtlUiIz6Xiyba6e+XsVW9VT4g1sB9P36PGpWtobY2VYm3lRCEPSRN\n7UPp\r\n=ZmrI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCEXncnP7lG0OBmHLQ4W7sZ/jOXJG2genBrXToVIanyvQIgdArzT22Gw9dg9F8iKV/hNRfeBabB+s/UFeuBp4yEWZI="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210205151101_1612538374082_0.12172740066362664"},"_hasShrinkwrap":false},"2.1.1-atomist.npm-update-main.20210209150624":{"name":"@atomist/sdm","version":"2.1.1-atomist.npm-update-main.20210209150624","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.840.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"6b014ad000879002278169b2cd18577e9b589656","_id":"@atomist/sdm@2.1.1-atomist.npm-update-main.20210209150624","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-vKkPb2fvcmUNp9wco4tgXjzeUEzRuiPx8REEEeiInpO6Q7msYv44FHmGtS/SGlnJS3z1DRhyL5latC6M/Qwy4Q==","shasum":"27ba9b6e208fc69e7006f4d9ced6a6e24cbe70ff","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-atomist.npm-update-main.20210209150624.tgz","fileCount":2425,"unpackedSize":10000537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIqckCRA9TVsSAnZWagAACFkP/RFAuab8C+Tf7SSc8+d1\nSRIdfZp3VLYwc7wwMbF8UD1ckYrzXmqgmW1hQhMu+GAzk7DS5Xh4I1ppITXV\nnPedsE2j47lP4auL4gWz8Aj8DXFKv+Mo22CAVt91wL44veTOAm9akUUN0n/9\nsaHBSqXy3huYYXi4Nzn56eQDwSeIhrwHxp6yxkKEGAW7XBApVMupEmtwkrKf\ni0gbMclZ4cdF3Wym6URrORxb5jsCdNbH6fAGyzyY1CBX9/9FVxSM0No78ivv\ngIZOjnN/QMXCboK2qHApA+4R5EHLn7ijRPVvga5RBG1EURAJjlNPZWLiuRtx\nMxZVKjK0tmR6BazK8uuzXFba2ISCywed5phwR24y+BV8pDrbsfILCCBNQDj0\nII47EDwo9be6dBwWJcfjRMqHGC14PlRsUVBLapQOZlmH62C5NyG7D2LUConV\nyuEi2SZFeTdzyYf4JO1hfWGb4jygh83QDtj0eAyROMaEMqlLbGAttelo8AZI\nmyi48umlyWHPGPapBXBXGIGeHfaC9Ql7gD164n9HHY08kXnfiBGnRIODGVxa\nhLvoIOglPOWrLWLP80hbS0fQ+K++beYENb3wGa8eP8fEXVI+BYWWzUjUbK8K\ndodj9hiA+tKC403vIvfZRU1Fk7FzaHkq/GSHv57iKJfLt4Ln8FzCcqBFjdE4\njMUj\r\n=585W\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCTmhFTPmrzCK8NnGEvtH2NmQkrzAis7XYME1sfz/rM4QIhAOwlwWODwjHtoRCU0Nu2fXKfMbvGIj42L09VEggcgCIE"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-atomist.npm-update-main.20210209150624_1612883747444_0.5214047551943444"},"_hasShrinkwrap":false},"2.1.1-main.20210209152429":{"name":"@atomist/sdm","version":"2.1.1-main.20210209152429","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.840.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.5.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"cf0367ce828ea33af7d0bd7ba5ddf207a1bd2fca","_id":"@atomist/sdm@2.1.1-main.20210209152429","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-q8B5sYZaUO+Wf9vUdjMe2egPISpCWz7UL4Q1UNKkYboZWTutk9y9aJfB9XYijUBUqqr+ee2ky7DXA5kl3rBHQA==","shasum":"9df4f958bdbd3b09ce7ad57e5630430326d3c904","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210209152429.tgz","fileCount":2425,"unpackedSize":10000518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIqtoCRA9TVsSAnZWagAAnFYP/AwmBF7lUBRKEvnXuCv9\nqDaLWNNXJUTchwc2fmhSqZen81xOcoi8IwxFieNFKqzfZMQ7ccSnJO0cl6Sw\n4eC279646o5Vkc6HJId+u3Q8LvzmO71fRyPMATA54L5CoD3mdNW0ME0mr1PS\nsz65hNCLoj8nvglLSuKDedCY4wWhFd3yt0w49hzBNEr74JGCe7nSkZCUjSEu\nA3u34xEwu7zinOPR5gcVqcJGaGQMvI2xXkVDUqflT8QlmjHc7NgGQk4qkYas\nw6XVOn/lWsILTlqwOBIbrR31O+um4I9mfIcOfwIjOiAJDdHCh5f3dyckrKfO\nngINjbwYNjWMJZMYEtMXkx1fUucRLXo4RxWXCCX1nIisGBdEr+UvxgUb6xyU\nrsL3Z2dYvCCKcCWNWTPSmltiZx8WOaXaxxJAhBm26mPs/vCGJdeSK7MTuSAZ\nN3TVrrkHJB2jtjV01B/ps+kSoVOfaEuiMrBKvhtYXTm5wxnda0MJddbR6JG2\nxY3DfIvLRUZuuYW86fbTMS77clF8nT0dkOSZ5NsS+JzidA3tFVyb72d7UPpN\nJb4JGEJdUOabQgVnz/VYihV2oZLsBsCBjI7GoGMNyrAu5s1HzMo4FXU08SDt\nTHVMWgv94sD2IftrB61Iz+IQ7p+Obq3AsESxQHN04TbWG6QBJkd1jrkXwHPd\niXMa\r\n=iif8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD6eZVZf8AG33rceotRs3En503PKf84PRGQpRYuaj6mtQIgZTAs1s8y5xuJJN7wSCjdsqnjh2fdeRkjVIH4wgQqmDA="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210209152429_1612884839756_0.16454143954867595"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20210209224939":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20210209224939","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.19.0","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.0","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.1-1.9-fixes.20200311123135","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"6c95cafc5325399633ae512eb507b0b92395b73a","_id":"@atomist/sdm@1.9.1-1.9-fixes.20210209224939","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-gfbcnvCLn7k3ylslZsIy0cV4sjG35kHXZrtj0QQfBLWsaYjtVYQeyHiXfSXEAc58XgOmNte3jJJk1IrbZuuRcQ==","shasum":"006fa9ccd3d27c6a0d2c14c4c8eda337b30bade6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20210209224939.tgz","fileCount":1238,"unpackedSize":5795164,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgIxI6CRA9TVsSAnZWagAA3zQP/RpeZ6N/KttXc1Nz6/aG\nCmll7DtGcIrRj/o9myJkGlHLonDlnM5GM/bY7uE+rv6hLczEDpOyIM+CcuA9\nQzigb+HCa3EILE7VsABjeK84koh/C5JaLRe47DUqJXK+A4ceHzN/vlXNmqBY\nIQ6Y9YxPVkxZUDbUxgUHRJjFfT/nZ6V9FqYlVm7OzTkrRyR6J3z8b9UdA3ZY\nqXS3VQRz8SXM0OSU6Q9UlMelU03SdAwKLC/ZqnevZNlpSm/E7fax7GJMHk06\n65rFKEpxpcqQHj3ummFsC2rnqAo7TtU7sYgItR6gS6Ti2VQNlbgQcr+0Oa/Z\nIDmYkkZXyGDj6ZKz2GJ3odmW+DyXUFxKOnFllCAHuyXeU1soVnd4bVQpLaxP\nDPv/o18MKmzLDgOknr8BmxVQEvcRsjWOUI772wk8SRuMYpR0BxBGrbYB4i3/\nr0eWcTMp0+vSYSz96MzSeLooGRERCiGmv538941i0L9Kg51eM5sHluyS8aFd\nS2VbSBiUmWm/bvKBZ15Krjm/Z7yhGQqUaishrPeL1Ui5q0/s64ny6ywee3Gq\n8nsnFacIphgcp+IMszS7ZLAgvUJuKUEsdw84PM/Vvz8yCNRxp3bHE8BpiiJZ\n/tFpPAb2y8oSeHEZTwY3r8YEHGQ3m56nK3XAP7bshxHlPL3psqqex2gPeAMZ\nVnbl\r\n=iKTh\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3EXsaizLgNbkJcidfSP90yyK6PhP6D5zrdenkIGyS/AIhAOnNyNejyYeAEApf0/vw+CByeCCWFJMcVyiwg5m2cSk+"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20210209224939_1612911160671_0.03947609064447466"},"_hasShrinkwrap":false},"2.1.1-atomist.npm-update-main.20210210150604":{"name":"@atomist/sdm","version":"2.1.1-atomist.npm-update-main.20210210150604","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.841.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"aef0091560196a7a2d12f6b85778f8e5eebac545","_id":"@atomist/sdm@2.1.1-atomist.npm-update-main.20210210150604","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-sbtvQ6/vEDVTqoysYqRMO9tSBRG/DQgcScRABK2YY8edW90ktK7Tg0VWJypvgyU6G++QV+jhSLVfrP3J5XheZw==","shasum":"2b0f809ecd8b0b009472585c79ec67a1bfea1986","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-atomist.npm-update-main.20210210150604.tgz","fileCount":2425,"unpackedSize":10000537,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgI/g6CRA9TVsSAnZWagAAFdUP/0C0RvOhL3AUBUgDTU5e\nOMEjdsh8/RcfCNoFDeuuI4Zi3682BmfIGb3wjD4evBoi/giNI3NMfurwLDl/\n4JTuwf2/xBS3l1FtnBQblpzljYStRXVQsgTVVCErfoAQZlRV9fNbqzYdcUhN\nd749beQ/t2EOyv/OWx0ndW8JNTXJvBH6/uJilSQCLndLe1dZTQOhUh1uhru7\nZk68UtpWDANY9u/whM3yWfep60NsdUk84x77XBDDSxYm3LBGvMygMrH1Vo8s\nzrNQpi7bIFwi47LgI2CuQSiSZELKzDosNj5Pn4Uyuol6cO4fa2dpjwTYtqMR\nCyOTYA8nSMel9q2LuTq2VRDCDP1jfKtb95QRXJrBdLenPfnx2Bw2F2FN1Vi8\nLaEvLPqm6/eU84yzRiShr5VAOLSqjcQbcIoqij1BuUtELOTgF85QDrtqiTmQ\nbBMtxdgkaoS1IIjz8lsaqlR0viGI9+e8EXXjGHB8yV0qBnVskn5Whc+Lo9oX\nMY/0MwlXUNiHT27XMsk3zmdop2bDzK+O7PJb5CI2zHeVxL3/avrUiqqZNnmW\nR+sbddXo0k7n/IOnZEwJ1IcNtBPaHkP0qqXHMXXlwHpNe1b3HHpdDJjkEWXC\nxYSvx/2uV86pLWoQOSeRATaHHJjyE1diEC/vEU7uaP1Rct4Hp46gT5ItXESh\nWHgR\r\n=T+nB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAm26rx9CWYRTMuMehSsUzh7AjTWypsx0uMrKQ3Kh84/AiBlwKuzQMfAPlwJBr/CEaXYfPse2Udw8Gj/eVyvz/rQOA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-atomist.npm-update-main.20210210150604_1612970041216_0.825784112043124"},"_hasShrinkwrap":false},"2.1.1-main.20210210152001":{"name":"@atomist/sdm","version":"2.1.1-main.20210210152001","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.0.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.841.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"64098ea1ad93cd547c1fb39baeb00bff66027ea0","_id":"@atomist/sdm@2.1.1-main.20210210152001","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-X3cYfqbwiOZ8rnD+hWLS12NjUUQnI332ZKAhGfZ9qqjd1WNjc0xuKH0tASG/Dl2M2y402Fhac9OgPGUgia52Kw==","shasum":"afd29e1783b45f1264ae17e7c1576b5072a5ba31","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210210152001.tgz","fileCount":2425,"unpackedSize":10000518,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgI/uOCRA9TVsSAnZWagAAmmUQAI3KjICTJjz9A3nhe1AJ\nECVg+D/8Wd7gOM/fkaErAwz6knq63oZyF94Y0nJfGeH3wjucjMGzAuOo0kYo\ngnS8Xekx8/fZzeDXVrwhHJK8AIW8A20M8cjsDlLgcljy6+HmhhgbRsh1JweB\ntf3MdphXgFCbvn925NpdzpnA8OhAUSephDU2/NVM36cFM5+Isu7cEuUS3scZ\nMSaAv0OPR3f/zbI0YX11uKWbVX0fOTz19a3N5tbdO7InjNP9iY18MBzp/rXO\nqmUEI6RMOPo/vIaixhkoUXy9QuGMh5+MUm1AWFiys5ccQwJbc0JGOd/AVhtz\nHKrFl4MhMAZ7ypgthaHN/ssYAAQtVT3Yno57pw4gC+/wbIUq+B3NCIxvFoXg\n6Ie7XflJHxldZifdk3tDMcwHtuojdLlfgnta7Jgnx9DHUzuEAJe1kmAKTryY\nHyL/zBba0Yi6h2RRitVFBTCWq3Wldcxqj68ZFmMKDDIUBAc8qSWm0njEeUwa\nU+sy5YW8wb9RhgfGELsA0cUYhQkTqmvQ5CMAzw+efZYayLU2NqYU5S4Bxx4Y\nv6ginXEB+AE6hLq0J7E8u5GrNneruwCh03r7zPFymI2kwH8NXq4JuvCackxD\nYfmLrUPPvd3Rzkm+vcWeKvAPE7IKiyYjgVVn55o0cP9nU5Rce0rfFa9RV/Qe\n6gln\r\n=DTZC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKAd3zzayWnP5BcwP52B42/TFAkdiHUpUa+hLblD1N0AIgcPutYMECCqhBis8OarqyHhwB3QEr8DGmg8yX1aS39n0="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210210152001_1612970893578_0.2642742631536463"},"_hasShrinkwrap":false},"1.9.1-1.9-fixes.20210210162852":{"name":"@atomist/sdm","version":"1.9.1-1.9-fixes.20210210162852","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.21.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.2","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"e30ae96c3a9bfbb9a8aa9c5a2bd8c426129a2d7d","_id":"@atomist/sdm@1.9.1-1.9-fixes.20210210162852","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-U61AzLBY/aJ6dNm5cxbjKzCO/Gst/6qV8ut78xQTicFmxMjcgEimIJxJdx1YAFj61WyPZe735jFpgoiYACzdYg==","shasum":"c3f7f1ed5cf1fa833babcece7354aac6c9f9f55f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1-1.9-fixes.20210210162852.tgz","fileCount":1238,"unpackedSize":5795139,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJApxCRA9TVsSAnZWagAA6OsP/iQGF+etb+tASgKv0xBE\nT/F18hPFUxgosyHEMLdcaS49jSlcT8mEX9MdwSEVTwBmzo2LzFRkx+xyYxD+\nWid4yOJLTLEmJ7wz2/5zL67vHgIXBi/g8+0cIFsb2mZbV/2SoSmlFOUXAGEb\nvcnYihQGQ6h1qPiQLbX0tcBIn2S2WwwzUyq3v/BKCw4F50GSiAUyXhrhI/Y4\n5PwjmGf6eCasHlL1U+d0Tc4L7DXnMwmrMNOZs/pjcpbFWKcmUnQ2zYdpCqpT\nzTjmCIOcL65wAp08TTU7mkCEct6YIr8P70xk6X8QGa2LZJ9vvHAtv0N4KYQe\nlK8KOuDEcryuC3uvkCGbsl6yTFqTwPfa++EvMoRaPtfxoIMeTVvdKOfWx1Zz\nVPzBKAN1RoPV6DvFnOESBFvY50CMRVWrainpUi2dm4L9ZwiSGYS5XLvrb7Nr\nl6mr2SeE+6o6RLGkG4bUhHQO4fzplVbDccNFUTaCzlUU1D7tfnNM+d4aUDZi\naZDvaT0fMeP+U7CWkltsIxdsU7msA7Msw7/VlOveK1ibwEEn8suNESh/E5g3\nUC7+t2+RN836jzA1qvimAuo5C22/51lC4QVWlIHEpNNZkWfEDKP/g9yuvhcd\ndpZOMwGmwU0elI0RtPENx4sRGUGotUxPsapj6Q4q2jQh32fxxkyl7KQBxoji\nohQA\r\n=TjCR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDfjDoIuvEMzGZ2kuHoNzcjQMfmD59jmba8DQ0Qx0jIJgIhAIVwZlTHpi/qQrSiscZnjdPZlMdWXpU4e63ILQz/4CWo"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1-1.9-fixes.20210210162852_1612974704253_0.5594574365120748"},"_hasShrinkwrap":false},"1.9.1":{"name":"@atomist/sdm","version":"1.9.1","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.21.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.2","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","_id":"@atomist/sdm@1.9.1","_nodeVersion":"15.4.0","_npmVersion":"7.0.15","dist":{"integrity":"sha512-mVxTaRhr4yWT+3Sm88CiO/zw+sA3wg5URze+nEJLgtJiNi4i7UZ0ykRQelWmEypUr2ySjNABWhq4faxLvjzy3w==","shasum":"87f54fa8e9a871fba00e6736da1649af34e71628","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.1.tgz","fileCount":1238,"unpackedSize":5795114,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJAt0CRA9TVsSAnZWagAAq7MP/0MkR342d4hL0XHFhFHH\nSpQzvFyG1ut2xNetVxOtnHUqXftQEryJ2FMdH3y1xfFvJkxsdHhNuBTuKEJ4\necpsk6AthyuKW+atmgGbQiawp27pXqYOFOmyyiyPHtzUGjL5p/Eu3T+JTIhm\nmkWbykG2GULyV5JcXzEylv0zPkkTaoabfzyU8juylHaGuIqPxjpDALOrpJcO\nYI8iPeXHFdNnJZy37tLmZ2c5dLqxK7LvtC/I67eid9542x3sO1cOv6kwRgos\nfbvMktpGfCRU9WS+LD9O5QXyrWzlaC4GCVFuKavapzlmH9TrkO4lLqBSpLH+\nsbWNRdo5H1rIxBY5UBiLf46jMTHCLCT847snVYhTR8RrPpvbWKqsrzjlIyOD\nv6ryybzt9dycTB3+wuYQkJZrmywFMfx/GO7zat5ZFPWtX45QbQvT3swrzpf6\nNTUr7BNaeSWb/dE0X/HqaZvDlLe0uVqGpvqSAL7R817W3YEdC8y1CfsU+US7\nsVNEOC4UKbkNMJXo7p6t/FD/IccNX4DRfTWwqgSlmlmQJHmvWsx4USFZvOIV\npMezfLlCBbBGzAKr3eZLp5tgL9XrOGeeH4bGV4MCvfQnowDRVbXVeM+H+0gU\n0G4Zly8COf3RkLGGfpDFSkBrfEAMB7cY3MklUi3eOsvW16xP9iOieJxwQ4F1\nXlVW\r\n=Dk48\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGwCZRScJeUn8C4wpAYUJUXm6hkUqM/dPfCRNcSmgMxVAiAhC5ZXtUOKW3jyvXXwmPdbDULTFz9MFwMWxgiZnFITlw=="}]},"_npmUser":{"name":"ddatomist","email":"david@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.1_1612974964093_0.9887401261525022"},"_hasShrinkwrap":false},"1.9.2-1.9-fixes.20210210164117":{"name":"@atomist/sdm","version":"1.9.2-1.9-fixes.20210210164117","description":"Atomist Software Delivery Machine API","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@types/cron":"^1.7.1","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/fs-extra":"^7.0.0","@types/json-stringify-safe":"^5.0.0","@types/lodash":"^4.14.139","@types/minimatch":"^3.0.3","@types/node":"^12.7.8","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","axios":"^0.21.1","base64-js":"^1.3.0","cron":"^1.7.1","dateformat":"^3.0.3","find-up":"^4.0.0","fs-extra":"^8.0.1","globby":"^10.0.1","js-yaml":"^3.13.1","json-stringify-safe":"^5.0.1","lodash":"^4.17.15","minimatch":"^3.0.4","omit-empty":"^1.0.0","sha-regex":"^1.0.4","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0"},"peerDependencies":{"@atomist/automation-client":"^1.9.2","@atomist/slack-messages":"^1.1.1"},"devDependencies":{"@atomist/automation-client":"1.9.2","@atomist/slack-messages":"^1.1.1","@types/mocha":"^5.2.7","@types/power-assert":"^1.5.0","barrelsby":"^1.1.0","espower-typescript":"^9.0.2","handlebars":"^4.5.3","mocha":"^6.2.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^1.19.1","rimraf":"^3.0.0","supervisor":"^0.12.0","ts-node":"^8.5.4","tslint":"^5.20.1","typedoc":"^0.15.6","typescript":"^3.7.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run clean:barrels","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.{d.ts,js}{,.map}\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc index.ts lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","typedoc":"npm run doc"},"engines":{"node":">=8.2.0","npm":">=5.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n\n# Software Delivery Machine - `@atomist/sdm`\n\n[![atomist sdm goals](http://badge.atomist.com/T29E48P34/atomist/sdm/64ac86ca-3c46-4742-9e41-a42c14560af9)](https://app.atomist.com/workspace/T29E48P34)\n[![npm version](https://img.shields.io/npm/v/@atomist/sdm.svg)](https://www.npmjs.com/package/@atomist/sdm)\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode.  Think of it as an API for your software delivery.  See the\n[Atomist documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ (Atomist Documentation)\n\n## Getting started\n\nSee the [Developer Quick Start][atomist-quick] to jump straight to\ncreating an SDM.\n\n[atomist-quick]: https://docs.atomist.com/quick-start/ (Atomist - Developer Quick Start)\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ (Atomist Documentation - SDM Developer)\n[typedoc]: https://atomist.github.io/sdm/ (@atomist/sdm TypeDoc)\n[licenses]: legal/THIRD_PARTY.md (@atomist/sdm Third-Party Licenses)\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist (Atomist on Twitter)\n[atomist-blog]: https://the-composition.com/ (The Composition - The Official Atomist Blog)\n\n## Support\n\nGeneral support questions should be discussed in the `#support`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ (Node.js)\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm].  Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm (Atomist Software Delivery Machine)\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help?  [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ (Atomist - How Teams Deliver Software)\n[slack]: https://join.atomist.com/ (Atomist Community Slack)\n","readmeFilename":"README.md","gitHead":"f00cd60ea61f35559ead46f79d8323708bde7ce2","_id":"@atomist/sdm@1.9.2-1.9-fixes.20210210164117","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-mzatT79ml5wa6LX+FaGaBHEZHlyJRBGLPtoDH1e7bH5vyAytqjjFgPfZc9NrHP8l+gbePBi8T4wcYGE53exVOw==","shasum":"c3c783fa7e5b7095fc4e36f47aa498a93746d453","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-1.9.2-1.9-fixes.20210210164117.tgz","fileCount":1238,"unpackedSize":5795297,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJA1bCRA9TVsSAnZWagAACtQQAI45tvOOZPdpEz+YjTI5\nY2/t1+/A0YBk7c1o+TcpCA5Q+QHFAheymA53MVamlOJt/16jooBiB9duCP6Q\nZL5DfOAM0XzWfWJEOj2cX3c+5Qydr3mszQ0n668kB3UJLAAUDyKf7MErgv7J\nIJmulVXeHcOYpJZYSeYx6iwOoZF1n7Z7Opwz7eQhXC6JIY/JiKWyvN4JYrsf\nDYxZQK6NeDq3BGHLkvOLpPbn+RGrj9HyxXu4b9utYJFIYTdx4sjzaHi8CBA7\n7/cZxJp84oSukMUezkumR7cv43CNbXGi4WKuUFyNbjaySN4GznG0rpHq/qUW\nn2cb4pfxYjJwDOqwIF7I4B6w9xnt7IrzF2cq5kvjMQJTEBMfNsFJZ841Uftj\nYiyoEgum/96f6/mT7lYYkVdf4kRdYh9x+nonpt+mVROa+qqTh2hYuA5fzBHT\n0Ym6lYPk2Mye/hhQOL6L70xqbegxh+xI5Q18EjxKO+yFMDa/0AE3pt1dMFgu\ncJwlfZg+zMFPbaE9xjnhH5diYOFyJolaRpBZAdOWzNHNkQnusQ3r7pbas8Sc\n2LN030W7iR+4RnN4eo0FM20ft4rO7hqk6GWK9EWK4qH3vUC/1l69336xXl7y\nE5czGuEWo9hqqRD1DIBfVBpIHRLWN7qxro3wW6ZG6lk+vm299dn9U5bwawNm\nSCrw\r\n=RMry\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD9n8cyrFO6NR9cD7GGhd94axqOaC2ufuXIE3JU5EsqMwIgS40SkFp7yya+8zmYUyfSEW1q+Ab1r8u0yIJvMnugQPo="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_1.9.2-1.9-fixes.20210210164117_1612975450972_0.9412528667642561"},"_hasShrinkwrap":false},"2.1.1-main.20210210192104":{"name":"@atomist/sdm","version":"2.1.1-main.20210210192104","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.841.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"d2daab33a0a6b743280f0d33359c846258e2a3d0","_id":"@atomist/sdm@2.1.1-main.20210210192104","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-eEWwYVtQbX/obAmPHeP9h4SWz2WNA5SPTI0VUz4FVqzatNK9DR851wrhTFGuUhi19adgwG7j4SNQGZGXrUPdOw==","shasum":"492ae235f162cca7b3548274c2baf74bb979f4f4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210210192104.tgz","fileCount":2425,"unpackedSize":10006754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJDQZCRA9TVsSAnZWagAA+xoP/iyXg96xmqgNWDBEMovC\nlBWMlpODDMSHzKaHNvwRsu18PMfOAn0wE13vi/QjlvD9tQVo5NJg7eQMmyb2\nWnxwfBYGIpuwLM+QP18nXX1sfyp299cYlVnCxcPP/7MzKnpKwznMFHUGSxUz\nYelPBh3BYIBMoea+IhnlBb0bSFCZZiGpmELwmMg6StCrQ6r5omh09mnfg6bE\neDFi3g8krVfHIMzXNZ8Pl11FA9zWHNVXrlwQYZrHn1aUp2R5r7NXk30W/Vaw\njXTLnpSBlaw2TJ0YxxufnW6yis6ErJDkQlVvonUFZ0atXgDD9VTw9O2oeGqx\neQtliXDgOXsVOsxXDK6lfFGkz9qrwzvjf3XXQpPqmCUpRin8AQ68RKSo61R8\nG8L9z1MZqvY9F4O66pQmk3F+lfyRdIpFiMNC9LlExRKiyJ0ikp0Zec8bl34r\nN0W6cleNbch5WZ+IwYyLRayWRmSjTyS8UKe61VPyGNA0tIwZQR6/KboGykVS\nVc9BXMsftZHM2KgzdY7ArXWThGboC9T9e6pVSeWDJtW06wHLUsBfgaNJvbE3\ndQfilxkp28pCNw6La5hy23sBDZ4U6dkZjitG2Va4rQ1eeRD2fMjNX1viKBu2\nAGdPKdCdY90ct/xAFkVrRAY7hpbgHG/Azcodxlgt76OkQFA9kNx0lZwnDIi0\np6E4\r\n=ZQZ3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFmV8o/R2gII5Jqkkfa/qCOVTdNobsJMMfKiItXRHr7GAiEAx+139oMRj921H25x5RpkIKRPzcbVhLV145VnNCggbPc="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210210192104_1612985369154_0.2296873054873585"},"_hasShrinkwrap":false},"2.1.1-main.20210210192540":{"name":"@atomist/sdm","version":"2.1.1-main.20210210192540","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.841.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"d2daab33a0a6b743280f0d33359c846258e2a3d0","_id":"@atomist/sdm@2.1.1-main.20210210192540","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-+nJaSxXaEKj5Y/U6qoBLSa3Lt7aIt1V6BScukaRBnBXtiwL/eT6CLMW+FMkWimPPlsEoErJwzKQ8XB7290KxUg==","shasum":"caac3f5c98fd61b20da33f7bfd2550fa87b5d13c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1-main.20210210192540.tgz","fileCount":2425,"unpackedSize":10006754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJDUDCRA9TVsSAnZWagAASQMP/jh8FPA9C8ChDuoeJMKa\n57tv9LNZpeGOYEzoyY+hw0j3qDaqxBzj0cCmkJ5pYawnXkj5rku7Mux3BGX9\nUmTwhM88l0IAkDJytCUqlhjCM+DxjluzIBJ0kMwS8HBos7yYG9XfjU0FgEk4\nswnTO/wnSQ4rMR8wx3S7nSBhpry65Ir17GVvW+ubyxPMahPNKXRmmLpAQ1bW\nKS7HQchDoz7eyYsB4JbH+xFihYdtoL+/lotQyagI8q0avUqApzKelsqzql4c\nDDocOHzVHvzM3HnjMRcOwQIDxL3vV6pWQrQd20ah1GZjtvMtRVOHg3trfuYz\nQQ6ygqYLdVNMpD3ji9ZUocOVFrafj7Quk/koOTV/5h9VFs1SfvJU62qWEfLh\nTxUwDg5Q9vqIyMr3F2NRCM9/UcRmpN4ZO7xVLng9YqIUtAEoGRtYuWt6R/NJ\nc5fD0irFKJMULPK7bGqKYoxzjH5PIPGlKKMfF3LQfccwOOVtcQiIGivvvEsA\n3V3ux+1GkKYRnAgu24s4qkTYHajbjGwOZnQopUMl6DePUjpImgXk9V+X6Kgf\nzYArMWGmPqXtHQOIqp4AWYDuL9AID2C856jP4ULJGs5Q6UsJVoaZ/Lp2O5eQ\nTQqGRldNYmV+S3iaRXJNSZFAGz2u12ZvPpLdfRXjEUgZqJi6S/vSvQdocUrx\nacC4\r\n=ILtH\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICMNZ8Ztrum3CmY/S3zRcaQwbKpMYHUIH5ABaw5R9wO0AiAvLtU74FATM+IU0giQ10idgeEy2ya2J5bLQZBXeIMcLQ=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1-main.20210210192540_1612985602496_0.34964521527184167"},"_hasShrinkwrap":false},"2.1.1":{"name":"@atomist/sdm","version":"2.1.1","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.25","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.841.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.2.1","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"d2daab33a0a6b743280f0d33359c846258e2a3d0","_id":"@atomist/sdm@2.1.1","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-cbdeRzizyjfmKYlIFB4AdbQMR2ycteM7SK9BPkdld79OKOtEjWc/7LcHWDf/IX4qJilW1FDCHkQrZ6pV4M4Kuw==","shasum":"91a87b0e84ec907ed53091c8306a5259a3374f19","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.1.tgz","fileCount":2425,"unpackedSize":10006734,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJDfhCRA9TVsSAnZWagAAYQoQAI3ShVMSkDQZ+0tIEUMQ\nbOXZ+N90eBr9XCP66IetPhI1KJkHPQ9uVc+m32Rmq4exJ9OSDP0b8OouILXA\ndPyGtx1CikWepztbqbnytU3BsUAkN/X6OycmRtnbAfFrsQLDdHG8Vkl2rRE5\noFeGbK44kETDS1GdPS3dQCYM6up3bzGOiriuw9cCWwx4shacELcTbUbUdXzn\nLHKgw1bnE8BDHAO0yBTToIh6pY6Ni4hWp17hSkMv4zTkbF+xuU/p6IOkG34i\n7oitnLQEu1eYn024TLTWWbNBotXNe2fg6wQN/0No0gVDcEC+UBsphdzk/CUz\nD6gDf5crte/dPfm+3wsSChn9HZ93EJswnuD0UKzhr0ejbZuQ4WVTR7HC924q\nCSf6QPvZhfHgIepiijVDT+qtatmpp//fw2efPLTYwkLQiX0MgxrXiR4J++vM\n5EbblZhg9DG0ykEM+WBnhoblKXj5c518orQMoIeXiW0vmJXNvEqYvehkh+L7\nBXlalv62pGpCAxD7tK1YyKmf4XG+5d7zKxLyv/KggipM52kIn+EP30FFRppQ\nIrHWP6HxiaA022Sy9Njz7mArhpQY7nEQMoDZkIJSrsxOL/7kNdqzppMvHAKX\nJvP2nUVY++iTljuS3uoQzSjPhapOtgIoZbW3fuWkhcFx5r8CiSwEtH9AbVfF\n5OVb\r\n=Eksi\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHAnZkrZ1RLmYTwkr2+wrCGSO8wHAAtiXqVf5tnUrC9BAiAAz+wdI+G0n+hVIR2BPkvtsMAtNnj/nL0G9Ywia7sl1A=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.1_1612986337074_0.4181633616361362"},"_hasShrinkwrap":false},"2.1.2-main.20210211214218":{"name":"@atomist/sdm","version":"2.1.2-main.20210211214218","description":"Atomist Software Delivery Machine SDK","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"license":"Apache-2.0","repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"519c74b32aa7060bd11e0ab4e68426c7787be567","_id":"@atomist/sdm@2.1.2-main.20210211214218","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-/hum3SIe1fXvOyu7cPhwPb0BoGEvVh0PmEcl/TSupnRrqP00UaiYyovvO5j/gzU1w261o3oyPho0rjhKJ2uYfQ==","shasum":"ecdf93343f05c72d1a865fbdbe8395f20d1da655","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.2-main.20210211214218.tgz","fileCount":2425,"unpackedSize":10006708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJac3CRA9TVsSAnZWagAAHnUQAJq01KOeHJ1deLPZBGg/\n9O6TssgY26RVhoUCNlD8HGq+in6M/vS0gynuZZKFIII5H89LvoijWL+FL1aC\nxJ8J6Yse8h9MoYdsWbvD8qEBs0xbIymP8ssRmSUZ5HdbkTuYNdhokuEHVZWw\np1J+grj4MXbAfQdHVQMnmM0rJwBwGEiWMmqaBSiu0LS+ypyvQtbraot1fyx5\nPPGq5oyD8P5ZBwsq6C+DXrXhjjFfqDi+Prf2Juu5EHGpQS/LH3F0KSO+1hPr\nG42VfOpC1kzk/n5Tw7wGX4/aHW5bz4UixaXQCrCOXIBtbTZK2ercf/DU3e31\nxMnkT+vNF3uzQb9gTuxeRj2F5dHbJm3f8hw2LSWP6KyBByPQgJfnrGpibp08\nxWi7KoFkltV720gMSHTfgK10abBe319A2nRzSCvZHSLddyXytNKvuLjmB5j6\njqjJT27v7jRvWS6GwQoLhMG+aIjsjQmS6juK8AMrXUUeVK7zgeT+TsP+m53v\nLqJBiNeOzS9RcDP9n/lzHRtO3GnYAbv/b7Qox5zF/rnTo2HWIL8o64XqXxPZ\nhSuLbAT5hGZ2zZ1WIoi9ksTh9bwP46B4nzjqbhzfhAtKoXpyBi9AzaIPCeS/\nXAOMZg3odJSFMNybDGUarXfd6ChL+gb4xo4YfEdJc8Q5xt0aXaaxh7lI4APx\n8bSn\r\n=JgCp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKlnkXoo45F5Sx9WVKIcoFca7onNQRuEPjSBLNRdcSagIgUB6stSrz90N3OmTOhq03RGaqUJCS3/i8a6VQs2nwZAo="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.2-main.20210211214218_1613080374322_0.3752856870638519"},"_hasShrinkwrap":false},"2.1.2-main.20210212133024":{"name":"@atomist/sdm","version":"2.1.2-main.20210212133024","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"47c097f0b1698838f508baa2b3d4da2c7e352837","_id":"@atomist/sdm@2.1.2-main.20210212133024","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-XB42gdBwxFnjbK7Cji5eIY/gaRNICON7zMDGhF6Bt1/lbokwiqBXjbMVowIzHsbAEmbOMKfS2F+7fNNbTkjMZg==","shasum":"f9502e3a464c0c71965b02e754dd0bf9434e4f8b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.2-main.20210212133024.tgz","fileCount":2425,"unpackedSize":10006708,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJoVbCRA9TVsSAnZWagAA4/4P+wQ/zTSK5vHyPuTFkt3B\ndWGxODHVAb1V4RYnQKzP+tLM5CJzrBY3jkKcoEBh37YsU4POJFKvAib58Zc9\nG9bAOal59CsksjcjBiXixO0M6HFLhs3UhCGp0OaPm5cNox44IUU3qkYcvEFB\n2TmwN2Vf6ejQD3gPItOs9tkkVJSVxIn4pji58nJIek5sT5UnuvnP3U3yp69F\n3rugb3Vp2hrBWhjIAG52fPA/61g4ogPVawu+EutKyOdMX84nQdKFlkhM3gBM\nYc18GCyfnOtjB9nAvCXBmQGIyUvA//LkrFK7tpxVlD6oiaX8fb4w6k0ZOytD\n0+AQJS5HhUtQ9TabF5sg1SNhAWosfgBe+jrZYgHoFtxwY6ffOgBTfXk124rH\nzjOBiWe21epdoeVpdDCpkfugJePGZJmKu9gOFIKv4PSIBnzM6zLN9JsL4MBC\n8EyTud3f3EGE6XfnReC0C6xkqfw3U5RyTtMyLBQ19k24AsKfNlWRhbEwgJpI\ncxrzZadt+Vkq8ohJtimu1Jj8b6jsvMvW0LZKM63PsegZ4sF/D2RYDqOLBMFO\nXw8N6h+VAmMJxdU1AxHphe52aPLd3Vw8OK0KXaN4EKnRWFZioenVW7syTwkl\ndEFSNlpbVqjM8orK6B5cGVWHhIz6eQXhj5HLQDjJMdxkeSJGBi10WPap6ISF\nlN82\r\n=vNFO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDDYBxmUynrwuO1O3Ft4uU0j2hjLC1VgM6x+zaHtChlTAIgEb/l0DULlqwpEnQl7D0xlTpcQ3MhKCrIsN84i03t7p8="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.2-main.20210212133024_1613137242125_0.3075067263323046"},"_hasShrinkwrap":false},"2.1.2-atomist.npm-update-main.20210212133312":{"name":"@atomist/sdm","version":"2.1.2-atomist.npm-update-main.20210212133312","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.7","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.27","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"ce30f4eead616af90fb782ddeba61f5e5d61c128","_id":"@atomist/sdm@2.1.2-atomist.npm-update-main.20210212133312","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-BFqQIKCpZpYF7BLDB1vnKyJCsTAWe+yGTT2DIG63l7p8xnBFykLDWdNXSwps7gXdOvhJVac45ybBOCvebLge7Q==","shasum":"414d072f66bcb5e0bb3885f70f5519ca5f73cf90","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.2-atomist.npm-update-main.20210212133312.tgz","fileCount":2425,"unpackedSize":10006727,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJoXRCRA9TVsSAnZWagAAPsUP/1Wx/OUWIs/TfwFXab1A\nu0ZkJAjuxE1GlzMpyUmizGHnpXEKYu8wLpMfKOx8he29Rs8Ka4vUj0E5OF/B\nbkifyJkQ5swCjxZhBgwWDEkL+pIEtz8G1v6BEYfcSzna4VeKOVNbq5Y9TOUg\nj7bZCuTFIhBKj9zuHWhcz1VT6lH8dD35XB8PAw21CLFICbTWNckCJEDbRfhC\nimv7s7HDaU2LnI0Ck9JFBLBI98ZWeZuZTZrfZS0EZ734uEbMr1C91xFMLBx3\nzcNGW8j6iv7Amz1401MMxWVHFR8kzN1sbO6kio7WO+DjPYbZCjWZpg57ZS3s\nE5swlwMQZWc/UTA4gEbE1sxl78iPSvVPZUlwRk25E8WfQ9enWQ4+nPRhX9Ua\nZrQIa4S8IdiN+E1sCsvF7L2srwgU7JpWwrHAPjstizXu01DjTkjf1AO1ZLPM\n+UBa2zGsWakmYZIIHhiz5ZrdC5KX49VgXrV7KQ3ecdL6M9LWUPr+D1a//GvO\n5DnrSDfAiCqahRY5NmXC7Mp/8jqmGudUQNbOTzOYjhaUMOwdQgDn6v/G2i65\n8/9QeIb6GDxgNXgfv+vuAeCgm9NnaVp852x9c3+VVOw0iW/UlvnDm+uBekyi\noOeHD5/ivNxO6+N9s6wWwyb4bo7SUzi+Pk7Y6ejvQhTRMW9aSgL/52KBmZKi\n9wz5\r\n=vFWp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD0bQX1qnBgZ8PGPbVt0QgDgpx24ekV++Pul4bc2pfz3wIgP3L678QDicpV/rs5ZB0VhCJC8dVNTpKKkbH2Ug8MNhE="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.2-atomist.npm-update-main.20210212133312_1613137360450_0.19665521336895608"},"_hasShrinkwrap":false},"2.1.2":{"name":"@atomist/sdm","version":"2.1.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.19.2","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"9968260c4bc978426c10e46af23fa82122c65e20","_id":"@atomist/sdm@2.1.2","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-FVRZKVkQ4aJ1+Kt/rXDwVClIjhmsM6qwcUMxW9ynQ2IdYHyZoSUglOIhdtvJg4C/kSCzrIE7RkEO2+L9n56/Ww==","shasum":"925a8793d9ce196a902c6dd3491df5bc13e2e38c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.2.tgz","fileCount":2425,"unpackedSize":10006688,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgJoaNCRA9TVsSAnZWagAAf6UP/RcYN89ID2OwdIMQURdx\nqSGSPo959VR8zQ4JF8LhhUXO5CE0fw2dr2orTwhXp2TNvBK3/QqndyN4+YU3\nyTez5rUfTYjYiLGUY2S2o1800Gj53BrJPt5402MVGDWyUNwGt/mMXCrUznJC\nrL7IvskTxx/69P1OQXlm6W5tccN3H1r76PQOPGWsyiy2Ys2PwYzfdBJjZPEE\nD/odMu/9sDI1mWLUV6/rPnYcLBaFN+yuRhDckOZMWteKqnU/VYfosITbiTVS\nn53J3EiwUCe1dUeqWsVug2+cRhfb/iRVul3poKGx2Zm+TqR0bgzNkHPqQ9ON\n/YPRsfHQgTIHDHFC89jXaTenxyFq2IVs5wUhZIYYEMPfbzwaBE3Hn6TWngPc\n8RDlJ21PBOOaMw1YxkVG57nifRU3gCWmTXm9L/GteKofzlmQZZ9yuCHIQo6b\nGBnHO95oLT0LX6V3NYuGbS47/66FWGzUsHY0/28Nn1YyZ6b80CJfamhs4MiR\n07aaKAct07VZCOdKqa2eWmJLj2NASUBeS0L4L0gW6h67fUwVwl8+A7GqLeop\nxgpd46Envi9jkRoiLvIX5nTAFrFV5tE9ONFoHLXlBRocOUXBCzhYGSxXVLic\n86nspxLepTQu3ihOfiiTBzQgDDrE376LVEbmPclcqztvDRhaCo3k/ylQnxTv\njszk\r\n=kwTo\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICQsTPejd9G9Sr0l8nM/GnYiywdRU25f13H43XpFi3h0AiANW6Reg7xoSemtWOHBhSOO8YYfYRE8k1Yc8vOE+8UeRg=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.2_1613137548709_0.8319343645470265"},"_hasShrinkwrap":false},"2.1.3-atomist.npm-audit-main.20210224150617":{"name":"@atomist/sdm","version":"2.1.3-atomist.npm-audit-main.20210224150617","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.28","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b2099ae92b2d6daf4240a46d7026d51c08b64090","_id":"@atomist/sdm@2.1.3-atomist.npm-audit-main.20210224150617","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ALWCGqg/ZiWS0LHPCVeNgNC5Hk9PRUUswUMZrhSOEfyf4I8x1mwIfxjSFV6Apg6n1ZpKgAVlKk3UubyeRSh8qQ==","shasum":"740e80f4d38311fe4b62b8b13264edf72c1fc5f1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3-atomist.npm-audit-main.20210224150617.tgz","fileCount":2425,"unpackedSize":10006806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgNm4eCRA9TVsSAnZWagAA/PMP/1pML9RpE5v6WqgxoszH\nWIqNq5cDvxEhgPYDN+lyvmtCwemjt27CudRjycigGIAW2EOCXKRXEkyOsJFy\nybtgU/AlFdDK7Jd5z1E5zGKR5syoBpvA23EhAUNcYjBrwmNQwGhHMveAP5AF\nOz8LpLi8IOLInw5lgkpGkkT9aLnIZISK7h0Q+r9SEdVbYBUvAHDWQRuZQ7K7\nI5IZSwkLuo+FHkuagDeLeE1qYMaYaJZPd9IzqnnxXDsFpBQ4dObYEE5UKtys\ngXzt3NJkanoFSfrojUC7m69tl5zcq+oV+xFveizRJxn5tLPKY12UPlM/QcnB\nScugM/QpRehbCS/BQPrVzQERmxOuSEz7eygaCFmu5F5G/JaBuc9Ua5rRr9Hw\n2KPW/C9F96vgolYMEDILA6F7d0grRtQqXa46RkM7n2/1QdS9vULU1JMLln0e\ne2Ipd4/EE8rwltPr60lzmzesyF3y3q/Ii078FihR+nRUeWam6lRgbQP+jzdr\n7ljET2gLkZdmFlpx/wcNdJVKbuZ+BMS0R5JFaigvCUxVOhIRFYfPrbLjejPG\nfDbiHLIvTo7q3wB4xV578wuWjBw7nPn5x2qTzb33sF2H/mhM+NkOKIIO03CM\nZqTj1HW7YNVvh6ir5hC+7ufYiD92ra0rcVKMTQRWt/TvoKdoAVhkkCcrTFPB\n3rhy\r\n=U0RB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDPwJofHPtMYgU2ihPpAdPIEC01sS/y/OMqzKyb+xabvQIgZ73Z9NHuTcfmY6r0/ifAqPVwZFu/XUmDLYnjOJD391Q="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3-atomist.npm-audit-main.20210224150617_1614179870030_0.8577726506061647"},"_hasShrinkwrap":false},"2.1.3-atomist.npm-audit-main.20210311130556":{"name":"@atomist/sdm","version":"2.1.3-atomist.npm-audit-main.20210311130556","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.30","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"5ff10359d56ef2c2b0b3c713675a306a86ab3bdb","_id":"@atomist/sdm@2.1.3-atomist.npm-audit-main.20210311130556","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-7J71wRqvnz9RntpLswdJPma33ZLljDlfLImUqdbSbYrNZ6ATqun+0tFyUdxFHlGxbVS4W0rzXgh/JLKrOCVGRQ==","shasum":"a76e711e875ba26723fd5d01d7fc0e6c7e247f69","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3-atomist.npm-audit-main.20210311130556.tgz","fileCount":2425,"unpackedSize":10006806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgShfiCRA9TVsSAnZWagAAFU0P/338i7FDvqOMrXdhOiRn\nlgLeJzokKLTL/NrZiMRZJeKu2IKxx9BzI2JxbLx9cW8IHux0kP64lhI3tD/i\nSpNj7m5WzHiYYevizK0cvMt6KHwR0maAzuIz7B5zyZ3TOjmGNeQ92dx+y4jv\nECeao1gUwJaT+9Lo65i8zCRDNWugQWErQc95CL3tiOji+EIMqRx+exKqyuOZ\nAkNlJIJWJJIrpvWCLN1+QacAfg17khWla4vk67Hk3mf+/prWv/G48ZEI2oge\nsoItclmq8ihcFzOGjGm4WvRACHfBfFhYLAvVOw7n3HFr2jvX6g46ZwyNJgbj\nCKe1SZqTHVJaUQ599t7mSpajkonl+Op7/yLxQfzDRlVxXu8Cq0kGQBvOlYuF\nI4BCkGB4vHU2k9igIEYxbVhPUaKV2J18cnl+7u9JgeCSHvRvQ0Uo+KpDqZhY\nYeac67NV6nbjTZFndcDcSPPIE4ezeAVyZnCCgdKKAQh1I6Q/PIwxkSjSvech\nA1d7l61UqlGXxQQKIcGAAH+AXZlgZMuios+vtMyIFiUFfI/KiMclUSBlWytl\nPXnV2AySHSOZ1zDP68sb8IvW8mNYukvh0LC0JydeTiJ6RPW1Y93CxDvFe8up\nrxG9NaY574vnaamMZkKRUP54+yERwzevtphQPl4xn0n+s19KTWmsPd1mxO3s\ne+Ke\r\n=v3Fs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEbb1kEPQ0de9NLWb+Pf4fYMk8UMQyttE83SZyeG9EVMAiEAuzC1TwdRRpOfGn9Bcufn2YTtn6SQYZQNm8c/vm4qb/I="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3-atomist.npm-audit-main.20210311130556_1615468513660_0.33966172957400187"},"_hasShrinkwrap":false},"2.1.3-atomist.npm-audit-main.20210312130621":{"name":"@atomist/sdm","version":"2.1.3-atomist.npm-audit-main.20210312130621","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.7.4","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.1.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.6","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.26","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.842.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.20","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.0","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.0","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.30","typescript":"^4.1.5"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"97dbe3c2f74faf68ff2495cf7e241153f751b1b0","_id":"@atomist/sdm@2.1.3-atomist.npm-audit-main.20210312130621","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-jDrjrVdkcdXVTzlCq8vrk4qd3FYYBwjxg6KrEd8GNEd1zB33b7oaQl63bGkPFvqIv+WFzCNbDOAdnKp/m6hDbQ==","shasum":"c732fd4ae586c78c03817b434a78db733980a4de","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3-atomist.npm-audit-main.20210312130621.tgz","fileCount":2425,"unpackedSize":10006806,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgS2l2CRA9TVsSAnZWagAATdMQAIQR8Kd/epNU1NDdIp8g\nCX+wmsby0B69Au+4QOVIGDTd4xlbtUP1kGIsWcS0ZvdEkbHIzthKfgbf3Ada\nmrUKs7Se/Hf3X1kTtMdjLnbPVYi3CkrS0CQHwETz7A6E+k8Acet/GFIo2t6P\nwZ8A4kpvGdZIXngt6FDDpEDuz6fY0KpQXaT+W8b2kBc0r6j5/nnpZhxuGnIT\nxFnN3/+vPsw6Xiq5e/AS4GKp4m0YlAoW0pfEmlfFKImMOVDiukDNkltX9kmr\n9MguPzcaJsXI6+EaPr7rfcod62OwLuVYQhpLn2pF49iLN5rFDSpkE2bW8Qs4\nnPOAL2UHRfLAikDJOrYudhEJ71/sZqLZscCilcj1WBPC9KIQHxe5StRAl/MI\ni6Rt4BnK4gigIFDkDE4Wu0AMar6hNSk5jt+sVsWw+CC3pel6nXHpVE7lYM9a\nsvYOPtZr7wvE8p+aaKK2q2rXJospUtOcR1UdQvuRCXU8pJ+xpv46vXI/M3AY\n7b2uGjx5gg2OiwZt2Mcl6CGyEgzwtioEJqZ4pv/vI1osm+PuukbTYk+cVBa6\n3CqJFK/CMvZcj0Zkvicu/msxDmDswzhuRascjyMjQxEDCsT8pXom9+yJ2vZC\noyFkhxjrv2v6VU11z5EJ3Ib8vN7n1DLKMuJ1u0PM1l/Xd66nJ67wAlKvwQPX\nioKo\r\n=++0J\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC4l/MCStHIaQWoRPMsQRtSa6DZZfsPYn2dL4bcWysDkwIgR9jvJy62VeXQmH1hWFnxkE1BuS6S5Wf1ebFAtUhXob8="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3-atomist.npm-audit-main.20210312130621_1615554933496_0.2997423613607795"},"_hasShrinkwrap":false},"2.1.3-main.20210314130540":{"name":"@atomist/sdm","version":"2.1.3-main.20210314130540","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b34b8db7ab55648a28982f1d56d9dc835381c8dd","_id":"@atomist/sdm@2.1.3-main.20210314130540","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-zwyhzUeAK49/k2CbG/MQ54gj0iLk32exOpEkL9++mgd64xic0QComcDa12Sh4u0T6MYAliDgZNY+FJsvxZ48Fw==","shasum":"fbcc1b8864084dce658c88733bdf8848e1a0c0aa","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3-main.20210314130540.tgz","fileCount":2425,"unpackedSize":10007035,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgTgxfCRA9TVsSAnZWagAADV0P/0o1W+8gCgdewH2wuEKJ\n1cZlHR60mMzrspeWVItBaB4XR3pAOxlh+CuIYjvmXjGMxoGIzNWfk/szAFZt\nazOislkauTB6Wb+ooGwSLoS9bjpz+hWS9IuXg5szWQhg70ulZoX0UfAMlc7k\nhPkFyNGdw7sByze9MWLcqAfEdwNbZQlK2TYGJRs7Lf6svNCIJjv5Ce9KOtM8\nwn3Ktfqwf9JQjlegkc8tToyDjZmcbWkzJkPZ0nTPpyfi4cRK8FjjVfHbhdb2\nNexfEIUpWA78oPbEVPSmZ0dgTdNW7IY9WMhq0+hSTkfBTsptNM3mc4+N8d49\nXM9Ow7KtWwpSI00l+WWsqHAGKHbt5uAtXj9qRewzv8XxGT1b/8VocdAwLkDT\n6Rl71sT959HNgtlRGx40fApvVh5N708DLg7oNlEoyVkdaDKLWM+LOCEhAbUJ\nbRgnDZC0pgZZFO0VIGURK0KpG71GIeJJg1ob3fcScI4mSTbLdKNHv0yT1JOz\n3mwCqRehPxVEgjt9PXuI+QYb666eGywLywB3MlJwCwAf09AmHp/bElgb4yj9\nLh4uOdkNnwcU5xMkYlb0KiODjmf4MIxDffRjVp2EGLAJ5sEoQfecFWmqbPUY\nGWz2dfB0F/DmldOOoxGdYyBq1znOPIs+iK7jNkRhdY6DZ+PGNgbhxLm4TBgv\n5QRZ\r\n=/mjt\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDk9yBzzMlZju0X5919gISASYRgtPYmpyaQ9dwilgV48AiAO8ABMeAGa6Ldm4xH3EMzAYOANSOop+kjkcjiXItgQmw=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3-main.20210314130540_1615727710536_0.41462529050053165"},"_hasShrinkwrap":false},"2.1.3-main.20210316141625":{"name":"@atomist/sdm","version":"2.1.3-main.20210316141625","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"a91712fbec5c95181e7e6f74bc4911e0b4736fc8","_id":"@atomist/sdm@2.1.3-main.20210316141625","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-HM85wVBlrN6pcYDeyVZsnF1WrITFuoiMdiV/yZK+joGOzRsoNIAJGcRYoRJVJFHurGuBWuElrmI8MMZNlbf5Yg==","shasum":"86a1ee56c01882655673923657f8384669945b87","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3-main.20210316141625.tgz","fileCount":2425,"unpackedSize":10009237,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUL9YCRA9TVsSAnZWagAAAJcQAIWJUg9ms3nYYx7XeVZN\ngH+vAJaUQMEZ97fztKIR1ov0RB5+ugjzs8EMwvrZNQI02FfnJiFZm9mS2LZv\n1RuHIrZEnm/Ztzg3zj1tl1GFgAfB2x9HaE0CdKt6mzigPwJxPuOwwWfZbGnD\nL25vwI1qngBHXQWN+1NouobqxryMwVN811TgzsjUDheC9Opb1DOCIb+w27WL\nxq2+U/O9DeotADUO4Zr4UYZSAHNremPbjy8+xusl2NFjD3P1ws1ak+7kCyhr\n9JGA898DSCPVOaVQtT3VlbWnZg0O8eGe+/heOVJlTC7EqmYocKizkfX2ENJY\nTjy7bCqJyDl0EU6P10weoXFOF4s1f9BRn0JgYdBYapdRi8IPZfHIB7tdRq5/\ntNE8497iML8eLFg+hLTjf8gwfFKAc97KuXt6LNgXi0mEJgVEabADtsQdIn3E\nXSoHTe3u4pTKjGKNXbmKsQA0lQU3o7bVscGJIH05wMZ+809rf+c9NUG+jSer\npOG+akb+yFdG/w3wmRwxRSB0rB2BNaHoGbhKUl/Y+9j8+DX3p+EP6ic0wP2Y\nCCLEooSjYJVnmwzPXOoOBtDgJsr2gXnblVzyYPZ3PYhSfZWUFkSF24Pn6DAt\nS60/H7SkSf7mlyFx+PnM2bj6g+RjTjxwMkMX9pZH4KJ1ybVdWbLUrJODKYYP\nDrdl\r\n=FFr1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG+J89KRd1lUrTRdmbQcXTlw1/n+kmARfi7pCFy9ENRBAiApA28zJK22nax2PR+2dWW7S2SFlr5kGSvqwhNeqWHUew=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3-main.20210316141625_1615904599598_0.603934847953159"},"_hasShrinkwrap":false},"2.1.3":{"name":"@atomist/sdm","version":"2.1.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"a1b27f0cd14064088219c22807160178a1d10142","_id":"@atomist/sdm@2.1.3","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-hVVaic2+IV1+wPDwj7zPUqx9xe9uknlpHV4AFrX6AKjZJPV8AHCLZ1rC4O0DVNpXI1hMkYRM4zNbqns5KgkK/g==","shasum":"6aa5d60788e875eb823e0ff0b62743eea4a08f91","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.3.tgz","fileCount":2425,"unpackedSize":10009217,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUTmmCRA9TVsSAnZWagAA0jQQAJVMXsLxwT0aL9N5af33\n8pwUTgB84ppM0+kBqbX/Q1GfsIXlf/+i+zUDdB5iCpboEYL6qAX5fPgewVz6\nZvvoz2iCyeMKZfBn9BloF4ls5zcYb0bLO4/Of7CNWzMB4RJwgjojDXB2YEle\nH7F0tKZk/5ZGxgHJ+eS+M2aB3u3aICHqQ1iKApzxA2E7G9UhUIcoUnguVk4U\n7OBwWjFcZbFfjw4jbw1aK6OcWR5fKtWO1kNV/kq9ims+mzmqJ8CWOT9tlPDo\nOEWvEZyOq25Zkemz9TdqKDS3ck2mhFM2hS/NU6vTEatsumPJRcCbcCdEzf3E\nieXuaBqXXFXDrhMWbc9WQm0N2RpWytyQizdHIoX8YGcvpo+wraTHFiHIoZd5\nNniI3qPypX9YlY0Pb/BYXh6Q2coi+r4lzkvrBhXmbQvxFMil7Aav4d3jKLG/\n5yQmCXo0QK6ppQeaU/yhKGoPQ1/GxbA7oPXEDINF+ysLviZjuuvvjPiK56Jl\nBZNifezniaaXLaS38baCD6w0Wol3GvfhTrmxMEhY1441DGRgbFgp7t1VbiYd\n2vIRlU98LLwBFtOUf2ZCpEYGSj2g2FnzoTcyEpCwXnOD7h7JSCx58ijEsh1E\nUFpCq6Vk2mVZonlydbWF5Ggf9DdO/i9O+7hSPJO043W5CN5ydFLGqxPVxTlO\ni7ZB\r\n=/m6R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDZfEZdUSrYPzbhf0tOxTeQASlcsOww40Ydd2Ea2uGhoQIhAMs9uHOzT9tTS/10y2Am8+QU1ngffTdRTejdTvRs2Ity"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.3_1615935909521_0.7276057950068833"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210316230825":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210316230825","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.865.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.32","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"41dbcf2a020075def1217934e22866632767fe57","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210316230825","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-OgTwn4UajDIsBu6YJYWgiiUfopAN6dTFRUrMed1acML/pGoCidO4B4H2/CkhFIGzmTW0RHr7Vmt6QLaKQQoVgw==","shasum":"aa2a45ba4a9ca7f0302dbe468a53c234012cf75b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210316230825.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgUTw2CRA9TVsSAnZWagAA4TQP/RRr/Mt/fwOfHarXutDJ\nkjPssGkZHSYmbDEhu/Tmi90zTlDrSuB3LHI99dfFUpY+wbo14aR/cZSj++Yp\ngya4IVGfUP9raSbU5JaRcMtHhrmvZZbne0/p/P2S5uJT9DeLZo+HCpTJNrh9\nI60D8c/hsc+jRZrf5jfPOWHNviJBZJ5rDCWsDpaIQ8bbk7k7Ok/wDO/jJUYp\nDLKnO/aSlmw0e4gFzfJt51CvrPeXQ721RVzplNN1YOOO+6qDxN4KNLxX+U3z\nqHdrQb2muwL2GgQ9Opk/zqkslbSaGATmHbj+SPlSdIDOTrIalJMErLt6bKBQ\nlqYlmkAWGu9nUIy/3gtgPJ/tfXZohxKkiEg/RdXjS3L+kW054hiGHIHKhnvV\nqHRCbGUVWGvSOEWrMwEg7S+/uogTmPxwgcB4NAcDlLiA+MvKouVBRbyKwidq\niFFxu0wjSAccnjDJjuWCGxEq+h+F6pHHfcsKonkhXLRIZj0ectRMAcPD8t8l\nEzz/zl0piwUA4aCWx+nPS4De5iV4m3vrJFYGAqArC2A4/HZ9oCrexxgD7+jF\n4+Y6+DZlYYkvFw/k0Z/yXT0q/ZrontxT4zt+c9VKVp8y+fMI8F83wtMHsyWm\ncqgxpJ4RJDBvrzvFiCLodqsX1aTeTi0cXdnGFl28omnBMW9NuZVLVIgkMcbb\nB87A\r\n=IzPr\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDGXP+TCorur72/0kaVLaXSw3TgapmApvgWzPMM+w62MgIhAJTUnfxbrxPylV8X3L01JJexuWD7V8NOSLxEhXTiJZeM"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210316230825_1615936565382_0.25261966199367514"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210318130516":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210318130516","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.866.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.32","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"adec9e25c45d14fc25fb983be7e1d749f20ac72f","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210318130516","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-nHqGye6zZrkFkcmrWvBc8uPMh4btYabgmBr863kwXfNMo9w4AVbSendK7qVvWX4lAiEAw46nmPawSJbtkfGb7Q==","shasum":"59f59221451270e00bd60e6876b071e9ab581d97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210318130516.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgU1HYCRA9TVsSAnZWagAA7OkP/jbfqngLIOuII4a18BO0\nyDDeBlbm8E7+d9XD5BlWsQaTgH5bUkV+0uDI5ahpCVI/m06I/bI7EnNl9rkV\nTNo0rW9giOpIcF4A+h07bfDBbDG3KavrkIv6POLiauwGpYCbwBsdSf6h7t2i\nzZnN2hb22fh43+eDQ1zoyqoBq1qKJAOg5Byj3ulfchsR5FFCj5IsDQv8NXRI\nE3BT2/90prONbg96zlhou4K6CBjqBcc/7YMN9OZv3sRkAmT2pBwPzaICJ+x5\nFc9T+m/wZ8emqPvXTOTPukNXZPbpE+hrxpxbFhVkReEx9H3kw5OW2NQWVAjT\nuUqDo+QlneY92x47CrnvD+oOk9rm5W8aXXZwy81IwzIvbcwiaHAr3f6vEBA5\nRC2jkTGyiOABEpygpFmDB8MsFZLcXqkE33faSccty8XYj73RAIoQzb/nGaV8\nyy2q1O6/Jb7ZD8iLN9kHmRVccacQMsW21+XtIc/p1jjaDTrWTiQKW+GrElHv\naNthLKfkwGWHwcuneJpYYlIYXGi4BCKUqrZ3tIkaSRmhdiqYdnkiz0y79sK/\neqnSF3OaCazhrOaSG/PgWTBmkTsm5J3exe9pXwQkvvJYekAeeHIHE+n9e8g2\n0tU8fCksiU2xICYopCTnRM6lSsSzCVjTHTgzwmLtODusbWLrnMP01r5KAvu0\nmn8q\r\n=R3YO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCnGB02WRjpTkCgU4PGCH9+fjzTZvByjjQM0xGNcoactwIgYQ711DK0MrJ+mW4rplSJlRGZj+KY5dlRfZ/GBml55QY="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210318130516_1616073175695_0.6807580695228543"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210319130416":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210319130416","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.867.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.32","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"82ed43bac2641d7adba7cd4abfd6825824734f4d","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210319130416","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-8pDBGlE2KwExsiddS2ulM+lq5CUHOrynAA6NEUqthFohY7I3xzWDLSjokpAghDSi6XQIKxoaIM7FOQakM1t0cQ==","shasum":"578d184a0f86ccc0e81920b62170debe3b09a86a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210319130416.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVKMSCRA9TVsSAnZWagAAa6AP/1xMCSYhke/LaUaxPAtK\nsFSZMyhfPjdvRfXQrkUx8rYD7/BUovIDw726/DW6tdV7uxXo+EJQ96B/oCPR\npQfK/r8QBhLUyXYKG9iJt3UcS7M9d/A2GgWWXq6rWs6DDBi//f6mf10HPAbG\nOczQ6iasDYhXxYDt46hJSbsb+QLEIxgcEqW4yRVfRrWj9x23B94jpzocyIU6\nEE1g6gwHgUs778nth8A5aikl7/VCFVZkRrAampB3W20WX+HziWw42+kcKhGY\nWoB3BmSLp1O760JPmV+twQw+Dl1PPYZJNbTfjX9815HQ2+HyuXOgaxnvoFIX\nTKfwl3KAUY3gJbbjCD7x5YH22kLdR+m48EocknH4ZrmSKfCxCXFK58ti1aCX\n0h5kPVW4+C21/ZZU0iZ87ATndu5rk/sqOiEi634dBjMT+sUurPYwplw+i+gC\nmPjojw/XIfyX7Ky2gFe28myXGEZW5u71HGCUSGz4CCgyOGquyS3a/DF63UF2\nA9jJX4HoeBVIBv20v6991EdW0WkWGB0+NXZ09y8HFUrERi07MIxEm7VQzdAB\nwPQgeQl5M73oArqU2dpVdcLw3fIzOi9ub/9Uq57y1PW6ehKQ7gbhfzsI0ZIg\nAjzBfBXVL/d2Cgy51KxhjTsRKVhDGGDRFZ695QrNQA7PWx9FPFAMdKoWndQ0\nVEld\r\n=7d0Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDUIhuv29z35QbRKWVnBqpApEbpNOJSBSY/w8b6eQFOeQIgAlZs17YI+Q6udXhNjGv75uaT86YiztQIEfXeUSiczhY="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210319130416_1616159506119_0.24664929380358624"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210320130356":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210320130356","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.868.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.32","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"d015a0e6a84e658bec3f631826c615515f3a2727","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210320130356","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-YZAGlx1tdxHd9J58Svg3beQMBKsxByvrDF5SlKBH02JfalRG5QgOQA7DWbk4jQYXEXKuzrqMaxXZWB+sIpk6Dw==","shasum":"34511cf0c50a151f148213c7a0d2b6d396ab40a4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210320130356.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgVfSGCRA9TVsSAnZWagAASOYP/Aj90EqqEu5leUTfrWuL\nEJJUzppJZX3favLFCOM5No+NvwCC323kTx3pcBrenx6PPsWhTBODymFPZ9lo\n08YVtwq/rNS+qNUHQ7n5lEluyDRFUiA1QdPMihnwzzEUZGrdh2e/lrTscxz7\nbzbFXcryDSq7rwGY+qm2qhSZeX1OX3r6VBf8ruM4PFWKK93CMT1cDcfFPZFb\n5y0Wb4eHAwd6XIw9kcWAUbPyRaZOlAsELc0S339OtQwMP2ExyL6SDpdDMzMP\nVd4wvnzjxEb6FhWT6976Ntlx84iYEMchbMETxqt2l9WJBwYc1rw7iEOJl4Zc\nKGeGhq8DEfAeeAiOSUsvSaLUAfLQkALAlGm9QcS78CSSuZ3TyG+hL2fDNxBu\nRHsT2n9KgywC7wlkhMBXL+UrYAWLbElZH9MR942fyQvZn3OJA1MZl1Znaghy\n7gDD8xniyRcUJxSzcATumOOLcTUc97O4s3zN7Mo87JPWJ4jdsno6I8NFyhYG\ndc7D+mQdSfhbGIZpBgmzn/Vt5tt9dZndkMk39JFocwYzOk0oUPvNooZAeQ/G\n2oNG7sMqsYN4Nl6+UjrIX9tRrRAL8yQwE5PyEG+dKhqezr/DHolxhMkgw3lq\ndRE9KXT94LXRkg556p/aOe9jtmFD7kVmY1S7BtekYDE17s97PETtzYeVQ66a\nbCZj\r\n=E3Up\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBMfj1RfUDGkRO++YYFzdGLw9FBzswoZilrGHI6Iop6EAiALSutevMXZaEMe9r9EbPJ6V6emN/ZDUIHpaTTphQxe4w=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210320130356_1616245893623_0.15828086964028332"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210322130348":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210322130348","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.868.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.33","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"5d4554df565e1d7fcc6d11ca63d81c21dc5d3e42","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210322130348","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-LxTSV+00bS+DTlkDB3hViSj6Afbozjx7perguqqKZTfSKK6qHu9npYtNdYdPI5bnSRqscpMJgsnRw9WILUbFpw==","shasum":"d942d5d578ee828027fc92e3f12aecb7673a8bda","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210322130348.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWJeTCRA9TVsSAnZWagAAue4P/Rd8fqSo6p1DpaFscX2p\nxpHxH7cQo9VRkpH8h8MinErObu2fWYvLoXkArLuzsmKDh7oU4kfV8+dffKBz\ntviYx26BT6cPDJfJ5ZZlPzJtuaq2rkE7IQropKb1gIyMqtP8WCDNnqmvBb8Y\n+9na3U06UVlGJTeK1+k47GfuR0XT3q/VpqVz1buCj8YSz2qqF1eoGpkQGMiD\noxONGpxos2ApW4pmW3Gg7SSDNK3EB5jt69KaA6Lt7yfEhWP4ePxVYwgYpQi+\nB715Qa8iuUsUPOlz6kurwm7pOpk0k4OpG2rYoKbN4j+1Hi997g0ikkI9NyDV\nsW/S8RpsUtni3zIhBrCGwNXEakzQecR98/WZSFJQqX3zbzvGGMKpJ8ihj4ki\n9a6ppIg+Dlu51HGYFHS01VMvIS+mc7XvvbMKm+p9lb4tLS3RLMZbALn7ISMG\nMmdpSnOUVQaqwG7T0gKVLcUkBvjDFnwZkn02g4PNdHvp+oYAZk7aU7QXkr0J\nqKkwZIwUcxu/smtFMDyWiG/2dnmwy1c5W3cxPQqyLRLtHUOWBk7XQTt83S60\n5/FuUvbiObqRaNpUiqiP5LYgkpcLgj+fXBOo5iSBGSYHeaxk97K3UZTDI8B7\nfnX9KM3D6w8ymGf+xtoUAm1H03/4zyXE0PD9R25erKamyta200J9mVTvaZyA\nskAS\r\n=DMJd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxWyFZw2WyoOsMo00zjrHLr1T5a/6krOh0axGHMops5gIgH9oorwaKQckskkjNLMm2A0yMENaXoODXZ6a5Mj7IRMY="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210322130348_1616418707154_0.6630646974543553"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210323130602":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210323130602","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.869.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.33","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"19b109b8699074fe6426acd308a667ec45179390","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210323130602","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Zw5l7kbezWbsjUV50VY0cyesLkwQVAc96oLzIUPxxjqvdIDAT+k5DkGV6+eboXC/gR6KbaszgRgUpiXieiR1tQ==","shasum":"cd1f782927b2f14afc8941c7d8bf5fde7b65fa63","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210323130602.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWelsCRA9TVsSAnZWagAAfc0P/jFYDWHBxJ7RHO6d/ZHy\nUydBgMfFxjBJpUr9zi4q8hhheaCPNz1M3aXFm+HzA4/Ep+zs5scuBzBROk18\n9sXu/xwkPNwM6DvrrzFXQwb0Et4a7RWIrbVK07j11/LXXENm/u/OeAb/cDps\nIal9+vdaBc9QsP0p8YxXF9wsJ+6CObdxKLooUuz3jCOGDqeAuWaltdy/k10v\n70UYuCfwtrNNp+P8T9nnIA/7vMrru4H3R3SnR+ZmVg+LiRqtkHjAp9O+LaHM\nFXDPWbpWyWli+Pu1oFwiFwpwPZtUQTFrYAhYJ7i6mIRhamX5r4GlKoFaUu7M\nwVyGq3iC9fOKwF9gR/gF7Bq3k6BTh81HAoP0Z5lZ91wWtI17N9TkuBFmbEqk\nSE5bPg1Fb44Sqg3UJAyMkaRs1ezO2uqpuucboCEzMU+ehpgv+etBxzbzb+od\n5fQ45gw8ftkraRRFTpfdagDtiQFXiPoJvMx+RR50y74brmFLo0+nlEgRvbsT\ncp2KA+pE+5ARmHtJUHu4ccl7efUA8bJbK7Qv6t5INPA8cXDZnDp2elMZfhTN\n9pNS7uSSuzNNCzLyN4sEan7ed8immv+ro7IoA6AAJzr4H5yqNDLV4pFe3BLX\neCwmJyqmdHfPUtQMJ5jy+vdlRnUPYs6tEPQTDaZCB/H+sdOcC+zuB5wMp3/3\nXulr\r\n=w8rx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHpcp7fv25pBA58wCMMr4qhjnChm9YpOv7KJp2IdzjO5AiB/TEb4qSY36f1Qr9eoh4YQvuv5dNVIzktqH7ooS8a5FA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210323130602_1616505196285_0.0776539856492533"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210324130516":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210324130516","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.2","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.35","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.870.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.33","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"020be16ffcb04a4c2771685988f10c3ea18f7ae0","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210324130516","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-3AQaYS/x65u+BYhsZbD5KwdFx7ML9UrHbbzMCx0wyde+tP+41b2PlAc58H546fyh4bW+kBZV0i6WTaODQPFwIA==","shasum":"d3d592b10db603b1ea6244102dd0ad6e9c408085","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210324130516.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgWzrLCRA9TVsSAnZWagAAI5UP/iJ0uKQuVnXXZ9trPo0q\nEmGocYjxbUwUPJIMzYJGr2b8alpkTQiIqvcQc6NX7SKgTrIW4XqaEDIoQNP5\nY/z54wE2ZhoZDdibAWC1AfB9WLyiSJ8kLvr4k0csppNW1r3Ak+FXPoirzBBp\nUeMNlnX5EBxr16rjqfIA5C4FX6WHZbmvSdlc5WE01qKyoFshpMewDcmM6R3+\n9byUpneWLjUCr7xYHGMi4sXJN4AFAjgLvNcotxMqjeA5ZqzlB3MWgmg+sg6r\ntUW3VqW6+4iegV/u2/pVCsJUjz0r3PjzhQAyvufvEY9sgGMk0oMEBG2Xx4fK\nSAgH+p89R2U1Uwe4cI6NHuhyZKbjHXoPSEcJIfKx2Jl47qx39muH8/M+NC9d\n+vq4m13BSWguY37vCnUppgow8LXy/euXskfw1tiAWuyWggIuMNzjYRo79X49\nRoHBXsKEIZFeGCmUXKL/B/gFUWHRYxsLvN09PmahUtolXI70PF1f0Om9+p+Y\nirNOL8ENeM1GAE7vuRN/8Xav4E0AdCFV6oFJc16GKr8neXZJciv2qMAyo5KJ\n7Eas3voTtHwTmPQyE2qMfaiJryEP9vOigcyT8eBdeqhJGCWYXSipVOKFxeZY\nYZTKjEHX3cx+tLtplwdeCIrBlTsmOSYCvMMr7c1lPO887dWj/9mCcEHkIa1P\nlvTg\r\n=g2MP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICAHX571R59PRlAwHdWrKrA90cuEUqhYFehoifUGTfuNAiARXraK+VqXrbiW4AkanJspOgV/5FhzeAJ83wlTkV/RLQ=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210324130516_1616591562464_0.42223321955231996"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210326130643":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210326130643","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.2","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.0","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.36","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.873.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"f3b64c679fbb2741b713734d8c09a12907de4118","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210326130643","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-uPhcto7M33EgnlJLUzDuE6Zr3O1S8Rz485xdfpBdz9twoQMFuYeT8jDeECvCzgh5HNNfvN+BqXoO3YA3+6ctRA==","shasum":"2bf15197b913e5a3e346f6951e389d120aac2a3c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210326130643.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgXd40CRA9TVsSAnZWagAAdAcP/AuuEZ+NKNbeDnmqDl0o\nlrw0xyfoMAwEeRkWkCCIx8/LmuAp55PAbIqHPHsfxCEJb6MpzTXIqABnZHhU\npCPYeqCTguHsg5Ld7W9aVFsAXbwJ6Z4NhhLVE77qvR4WFmym0vVltCJ+/ODw\nofusSwCfHq/qJHEO6oRi29jBWXWKWBnJIfhkccEVlzqgeZM9g6hnvB+yS9iU\nmGUlS6EQdoXqJZPNHIZjakqa7nIAgIpQtQYpXuD/yK+CuwqQXjGM2hJ1BxyT\nQ6uwTHENOHFK8tUN7C4gMQIg+HCv18I+s5LyTTbIvWLcTxTrgfAioZbEX2ED\n9YgYdpV5/MYIBkgggP0ohWRzkU9spF9Biii96tTzIvkAIPcRJvPIInL6VJZn\n+NF1qOzIS9SKobpQ4nQWCdoJqb/1OUP8DJNlE/DpHHn+C61DSwSsDbkyywq8\nz81Of9n/ITzlO074kqXDoxHO0yedE+fmREoDDLRpEHbv+3IqjAYEsR9Z7NOS\n2BiF0HVAR1HWgMokkh6Q09Z2xVaIJYpmK7MLRnIqRKEvgGduIoeNkZi2wbFR\nyE88ThdG5lTeO2mH6cg92kZf6V5cjlbKWjvLItuXh8MmrybRlZZZb622/pmk\n2Ma2yAKS/cCOeVsYRLUTxC2N4UB/rl8EKV7JXSlljMiKsD172IQM0ZGKDzL7\nX6XL\r\n=chbF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDEeLkSoHvEYc40jjiUGPGPIVImkUN96MFFoq5tR+PocAiEAsiwhK7DgD7Fc3Tr1FVMDjQeWind5LEu+QzQ45xbig/s="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210326130643_1616764467338_0.14172452727866247"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210327130533":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210327130533","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.2","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.874.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"ecea66c0bcf06c6d200612e20c45f4c08e7a77ac","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210327130533","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-tAkDRcBQyaiB54NvwOCk1AQoebepKGGvJR03fJiQ3LiVqxJl4DPMDypOKyiMw7nTZDfhMh1x081VM8/r3oduEA==","shasum":"9c21f430e3882fe6d64bcbdea6e33920edae4ff1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210327130533.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgXy9jCRA9TVsSAnZWagAAZa0P+wQmUQoGqalV4M/AuY4G\neQrRcA8DQjVbMYoqQvwiTWkfPy8lAN4IC5yZ0JaswNnlXHDY9VzfYkkP+4YE\n5MBM1bOxnKbp21cfmtGuRRvlzfcftYob2fBpt0XM2cLfcGVDL//NyE1Yw1vI\ndiO74lNqc99ZK2TOlbllaPzs264b6pT9PoxqVCIj590T5kJv0UhksJZM/Qym\nyykTLb2JQ52x7PEamB5Bc6d0gq/5s4LWjmye3mT/2a5zrT106yS6+fzadxDW\n+obgbYP9YtF+x5/q1WMG2FASwqsmAi2Fc9Bqv3BpbLZaY54pdv0h7Wj6DQa4\nx8JeGyabTl8RhiQh79Y4/cVvZIifDzFnzhL8m9McaeSc1zKo5QYMwnyqrxcf\nYnbmo4oVjJj6D4AH9qPiHbXFNYUBiouj0n3qTZiUXvjklJhVE5nM4xg4478c\ne1aqEdg2Ghq8cngvfrPyr9CAz2ai/g8cS4x1ycyWhzB2RPD6gd2UhXaF5c/O\n+9ds0CD5DE+tPtDeAYgiOc9e0K7qG3vzhKdu/A+KuxRFik8/GJ1qjq43MovO\nwQTgOQf1dc0LyTSKTfHTLSCiJ08sbhIv59KuYVmVzi+kps51f44eZwbtxeX1\nJNoVuWpdhui45sWTofcaGl58e9mFEJyyjf9ywpFFDCPaswmFO/LpCUxf1cMs\nlmX0\r\n=EJLF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDpwbW+fCCh9cNiZCAzi38njyswbgzR+oIvZvWi/4gSgAIgb+UYkN+QN2G73EI39oJSDc+MG0exgs2KIhHFpo680lk="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210327130533_1616850786567_0.5046413693338365"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210329130721":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210329130721","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.2","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.874.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"9ef810c993d6e6ba7e7befe9bd1088e87ffa3f7e","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210329130721","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-LKykgoX+XtE/0nMHaW1oNDRQJivHPqHpL5w5klqPnuhoZjE5XkN7b1t89YBVlFsbcaxMEldUkgTOlXXl6D93hg==","shasum":"e3d6dadbbd68b2808061ce1f2a0000e12433453e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210329130721.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgYdM/CRA9TVsSAnZWagAABQoP/2CrWnGIjKQ4nk2P9HKn\ne5Ktja4xVXN+JGifLDfASreef3jEEbHIJcgHhqL896UXz7HyzUyZTskKOdaE\nU1Pc/HGZJaGXukBubKQ1n9Dde9aPS8fIBsqVl56lopKl4q9mM9zsloDlfnri\nA8G51rGJsv76dNLTDDPaCMy6+nzqLjdp3fkyFXcZspZeJtEaTt1lwaBqCT5i\nXiUuosO5epL3VnOcQNlbAjOl9Ao321EBLNXAGA5Ai6LcRKJmIitvvhNKmdg9\n4hArBurPy6qkfRfyKx5dsLVU5NbgFjaBOpIWgRF4ajOeo98ZWU4WcS3UGjmn\nRV9QPrpD3wuYhOuCQf11vsN2aXAaZKo5SbpvrR5gDVoebASXwGvf4Jdo63f3\nxpbLCgamVzUOt0vpNZQc45iPNr15JHQEfFCAgHBmcHkyaes4L/dbAiKKxfrR\nhkkgVGlrIH5AgNhddLmGHFHBZViJdrMwEHiCVdU1BHO8dSZma76m9/VWmqPK\nxuGPkAGu28KFepT+eyN4MYRi6SWbdlHPHpAjKdX3l9Dcydkp/qtx/HzLSRu2\nUCCZaUYkKOqdQgHkriTD9wLOhR2os7YsphP4z2YiBEGWv9ThFeNOqM5vjFN2\nWVNRabfF9ZDuYUoCNZ/RcHUi1xanfPJArOUCLYVsNlHijTN37ebQCg1O6CAz\n+v22\r\n=EXFX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFnFgLotwqXJ9DExPdgYs1vp66VEch9q+9MbBqpZwMeaAiEAjtvrpjVEeJFHYi4Bry+F5m016iJZrAHoEvAr0LOUOvM="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210329130721_1617023806252_0.9002661340406468"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210330130415":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210330130415","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.875.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"167a95f2a53faf432c711f82b2fc21a6b579daa7","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210330130415","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-1NTnjX2fryf2y6N5nMLbIXHus5zGVl/0J7/0bCQ6bO/eBj8r02aR1lkEPYTUzd99HYi+6GOH1aJ3ePxUKSAtrw==","shasum":"16983832d338fb966ce53d3ee14d0fd1c02a348d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210330130415.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgYyOsCRA9TVsSAnZWagAAaXMP/28c5ENJ8GlsAKfj4hZZ\nsUxbt+gu+bv0Cwo60sBiSygKFaasa+jOoab2Vy4+8IHmpuvnVJAPuLri/l1s\nOA6VqKLLUEjf4XsSzTkowjUzSr4ZMuu0o2rIcoCOCr/HpN4M0I1S1uFhhmzc\nbaD0O+JqvPuscBEe41XOW1u2zCwCAPEWyOEZJyTf3KyLq//Vqb2O3ppPzmak\n4/4yHkoyI1p9vGCU6GaDsoMy3K5985/lMYu1iTJ/bg9WFxUWRPZKLYTVOoyu\n5CHCDqJGlCqwIHJK4LYjyMJEuiKs2QhaP+VJ8rCIeg/8EWyc/YbXPQIy6Uj5\nHq1FHL9ObPc/AqPD4ig/KVju2Am/OHtewWVrJq1yCjYyxXlkN91po7VYZLza\nm55zoaUHh4wXOKs6ZZkBvMvE96+YDnZR/urVGeVEtLdd2ak6GdUBqoYosfYB\n9TuDbuQ8aIlicBiGhxvOVXVZ/L122DR3s4hzbEBBYhKdWXTG9VBDsesinLAl\nkwHLcIWec0Wt0YVxPulwtmfYkHabGYU6o5yBEbDYdy+LhI1+xs9xqcYPKFpH\nE1esaQm0WTxg6Hg2fuSr4fKbaMQZFQ16c2altKAXYnFmsfQddXSZ8yOGF5J1\n4NZaYe7iO04PTgO02Ho7D/hFmgaZ4ZeZ3WU2TOmpA3mxnCNzi1Zn5NcGyFCI\nwZi+\r\n=QZ4D\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIFrxtWJH6DiLNBXQ+7JkzYO/ANtAHcHNM7W9uCqsSDzQAiAkISnTkFWBMINSnDj/m5JT/lxqWGBcp6KIKu3drKYe2w=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210330130415_1617109931983_0.5913624683782059"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210331130354":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210331130354","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.876.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"83264cebb487a795e19a9f3ab779edf6bf68a0f9","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210331130354","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Tgalk6fPeyGOKEw3JyHrb8R9yBqh5si72PGYUs+8CIyvJkleATEkexQGyqofVEqVed4FBDzXNv4kMklirAN6QQ==","shasum":"d033f4c9c8fbac23431405328dda561802b1e948","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210331130354.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZHUlCRA9TVsSAnZWagAAhWMP/iWQGjknzKtPHkQa2I7n\nzN7uNt/ICFli53KQRqsZPcdKJvzKv5QRnxnlAlxwoj47RTZV0iNkCiK7Jg/P\nmnJgYzUZMgsB9Yz+isp1QCIPIQOX8HZtg2zHdHPCFyL4AmLXr+V1unNCF2Ro\nE9wBK4TNZPQVq+lgJGITFerz8V9rc4EVblcM2ACFDnnj7xLDqc0nBCagUHe5\nFr8P9FeLVvAwdYQ1ybhFkjSBl66nXBSwUuPKq54JhlKMUsK3vIKJsDU+uVrb\ny9MAFhxPPOeWbuqaF1z0aPnbF2UZA4ITIN3zR5F71bpU85ChdbeKR09FjE3W\nNrPYGV4BZcnDDG/BWAABIgOFmj6IoQrPXDTZXv/4SR8PsRPR6acRGehVZ+CY\nJsAFNdbTMIzkd0l1FszWZKuX8a6LcB23s07PWA4KpQS3NzJc29UzB0AK7yc2\n7uDgfmuhT6gSOmMzOfpRCeE6jsap9BTrM52lvMxBKCTSPV0rfsZ26Mv74wdU\nOdw5zh0N1zzAUr5jhVa0+5c1F6+Z1/CQx7dMeFT6w0igPuwBdVIFJwmH3/sm\nHStxDUsBT3NW5LAr6M9IfDRJmo4zHabfILwh5bKvPdNNNVLgX4OtcoX8R8l8\nMRRsZvPLv3sdMHVEICZ+4XBeXUMrMr8W9TfrmSHHAroxiU3D22QIGEKXSdeU\ncPeo\r\n=pVej\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHfR5oF8mXqTu60kgmncSjXOP4u/XJWcY4yGYYtBvv9eAiB1U8BE+MsjYuUMEbCtJO09ALwc6/jnT54gcosQdluDvg=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210331130354_1617196324770_0.8952522814516646"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210401121241":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210401121241","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.877.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"f0d52537dccac118a16f1d74dcd01aa4f640be3d","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210401121241","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-8lTeZZbD93pBBIiz5oRw3x1ZQq+xkMGy1zxt+/v9cUHptQagS67+NNFApqNNiW1XwjQwfCtV+QLqDcW31W8rvA==","shasum":"7ac13c0ef3d6746e173cab85ec7fee27d017aafe","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210401121241.tgz","fileCount":2425,"unpackedSize":10009256,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZbp1CRA9TVsSAnZWagAAQIUP/3GsmmENReBKsmkobaqH\n1TfPDxZChk54Ba134XmXAE7G+MaNujhIPOLgWkk9+caAIqZe3fIYl8qkHW5Q\nXxAWemxrUYNDNjq4OuXOms4xVFCJTcPWskQDBkymYx+7gdAmFsOiQeboVbvA\ndEoZfoI5C2YU9WtPVYLkROTHy33KuD7oRiR5gKM/Goyif4RlId1aQ4roMyOu\nIHsVST6IDSLFeBejkmJfEovJoxipXKdC8S7cdprOHH6dnWJHLccktvi71MMp\nDZimQ4uWXWrhkysiKIxq1sGuaUjd8ghKi52QkpApYgqsZQ+yyMMyMA0o2C/M\ntbKHv0A2uMBXhsPAdyl+wLDKO4Xlj7+OGKzQb3n9NLrjn+paVc7l0/XwgxKW\nUGdbHeENC1FJMGGuD4F0s0bZSpCmPppUD0AapV5Kp2J/CtBUKqyc4yI+IVUT\ntQSYGgEe0I7K9ED3RF4vNopcqy/Q0rIz013KwPJB6SC2ArH1M2/UMNUumnO2\nQGbQnVKEktomFkQ4WWspH2tF/PsRR6UJOGjqnVd1TjHFyyYd+Y0pwWQKIo4s\nvzk2cmWKvKCiE+sKBX3uNZdhSYQBctF5BDJ36td7q9BeVxoLIj+XYn9l/y84\nABIh1Akg3MdnZFeueDAEYSaPYHBHGG6z1dXxmeq2ht1z27Eh2waW4G+zwprX\nCo7n\r\n=glnY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICC9vaDokjqSB3zYZhK/E2R+53Ij7HXbjt5ms8YFXQddAiEAqUxAWpFvR1J+KZisooUX4V7cDasAJblepU9mIeiwAd4="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210401121241_1617279604450_0.7507176731892222"},"_hasShrinkwrap":false},"2.1.4-main.20210401205512":{"name":"@atomist/sdm","version":"2.1.4-main.20210401205512","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.1-main.20210401113224","@atomist/slack-messages":"^1.2.1","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"877d46260268f0b702b6938a638b0f01202bc4d7","_id":"@atomist/sdm@2.1.4-main.20210401205512","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-mTnGI+lOrgJKwQjD5Vukg2qp68+kV0RspjwgXiEtkXXRtDLtjgeTzMsPkUw2/jVXrdyVchgDFCM8V12X9lNENQ==","shasum":"7c9c94891754ab4f93f408571ce8bc93262ac58e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-main.20210401205512.tgz","fileCount":2425,"unpackedSize":10009257,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZjT9CRA9TVsSAnZWagAAvP4P/2fpCC4a43LaitH0wlFI\n4WvSu9R1K2scq26ls/tsM61cHiRoKCAq+3pUtWvGyvmUpqK+mIrbO1FQyfmH\nC4OZHTTksFL2tceV1V9Wh5fG/DD8Wk/cACac8oE49JokKcVAy0y5v5nX43LZ\nd6NZbKNQd54XSr01BoERQYfEG3jxpQIDhUbEGd12sjsx5J322Cu+HKxa7YuI\nckal/uXY7Y4RYNyBJ038p6NYfVyRSKBJEasXn0p/GGqcxHYb/dVSx0w8HWGI\nWYU4JRvjEws32kzTrL55hF5NWcY0WewAja1x5/cOOC4DWLM52EWc1tX+T2oi\nsJdWfvJ4ZGVlBqPMEISERB8t13h2Lt8v/FkBjQgQSAS0Eh+ukrhxr0XUOtpJ\nR/TB3zHHv21BBMU3If3yi36MxsOmUe+Rb0RxeCIPCgD8RDecbhXCUPeIdpBX\nQe4xKSoJIiJww5YI1N31+4QaHr42eLP3EtCuHGMIcqubiEtNPaq61mF5nMXI\nCeYQCVYaJThTfLOzjDQpotfGhMijP0esoiqBw0IhzBJRd+Kaw+EgvGgkgXiB\njG/zBwoc0CNov2vvci7rMxl4jL5zoY6XN863z/LSfwrj1KEFOdpWRVn+iwnS\nrRGPIAk9co8UQmymkBGR1AHLw9vzVpoJ5Ax2XRvxF+60krhcDWchQ49eSYx1\nTJUU\r\n=wXVj\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDxpfBuuOHT10FsO9q+soDQgurUwDYeV8Q8P4kqtajHcAIhAK3tiFaQAC2tW9a6EybpuRCqG0cwd2p+cmU4yUyzHGwJ"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-main.20210401205512_1617310973147_0.46097272963040736"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210401205528":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210401205528","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.1-main.20210401113224","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.878.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"cf186c0a8f3672d58448b6fb5701c65b266e7b22","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210401205528","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-zSLJfLOITMT1XA+O+OvgDChfmdSdnt6HqPN6wARZ4jCQyYYQnCSb0xKKxqs6ger1JWkbn+B0MHl0sr8LpFUVVw==","shasum":"3d0b2f15d4b2f42ac10f551f99f01453aa74fe4a","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210401205528.tgz","fileCount":2425,"unpackedSize":10009276,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZjUvCRA9TVsSAnZWagAARIYP/2x7vxlE3nlxjZimyjv/\nW72AB9byEUMoX4pyIlB0lll2zS7UZAQNCaPUOVxT8tkr/vt21uYZkKgA9qtE\nGeXu2xt7bqDswXzk5pNk1RDOF0/n8+DS0AMC5d/ZUaq+5kBQ2DRQ3238hqks\nhnBVZypY1w4L0wh59mfjjvQVfag3DA5bn9qK69p6zYKKc98wSpLz1aokvak8\nlv5+GJAr8JzWO30f2Oq94GimyP+l8+IY8GE1z8GTsdKKC7ow1tSJDQV9quBR\ntPxchh84AlKq0ueBAdRY9lnZyV2O2sA8ashIbqnW0VKtfOMiGsakDEIxYOvJ\nvvJQvaPb7ZngfGnP4LX4NN9yCJsp2/JuQseV+DQ1dUpf7nktU2grFjbXeTM+\nW8bvCKpaw6LYgMigWZBCE7URlDmIySDgOT7IOJBuBDw663YQceeg/9PUyZjG\nJ9pinVcM4dzuiS+9tXMOXEpvMnTgLHYEkL7fZClCfmqxj/OKnKwQEuB1nHhg\nY+K/jV6pqoFWd6iPw+nrA+3x2kP1pqT/M2xUtoNt2zYHdJeF1vvoKEdh+8Kt\n5ssZ+2eJGxcu+61s7oG+FnDKs8i6l+Pab3lOroXyymnnxDqLXL24rQ99vXEf\nWSjhU7715BWjEpgBeDyx3i347xu41xMCjVh1B/2Uphr/zF7c87RLMBv/9tbj\nQRGk\r\n=7n8z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCm4HUBMTrwcNki7Ww9dw5yarCoc5+iNm1SlJufaGSIvwIhAISN00JQwubmolNHwuZBy3osvIiYXkiAqJgMr2UHUHMi"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210401205528_1617311023136_0.13973450012693234"},"_hasShrinkwrap":false},"2.1.4-main.20210402085638":{"name":"@atomist/sdm","version":"2.1.4-main.20210402085638","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"8e9b2b41189dd3389aa84231c9de7b79d38e437c","_id":"@atomist/sdm@2.1.4-main.20210402085638","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-3B65NoiYColXiDtG6IgJ7jXlRx67W5A7l5vLHHTEC04aHU8u+XmTGwcDxg/l8r85E30Ys2lM18rPuynBe7N1mg==","shasum":"171747e7690a9a4caa6bca2568856f2f60650ec4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-main.20210402085638.tgz","fileCount":2425,"unpackedSize":10011811,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZt4KCRA9TVsSAnZWagAAW3oP/Rr1oFeqaSx0f/MLgLBJ\nbwB9q12W6Igv5G8V0bgq05xOpb6+RfVZBAu3D07ovgk+TByr+v4B1lHhXGDq\nYh1o7cVIqWdKihTJRHlIYy63MLTBxJ7mSHxxWYqrf/bCv2LX1H+LDLZUpzpD\n/tDNaIT2jodHCw8cgYGmMWw5HMKqOTVtvCSp5NHiZtVfNHzJKV2apaNhx+Xl\n6hge+yQvacauUxOtjrrDFhWMNNE1fGVjey+8xFb8NQwwS09jfpHFCeycFiC2\nZrJnUUCv2Hja5sZDzL4KCWWUOFL1R3GXV82IhuBcdwqqG3fbOOgqJBjYkJd9\nu9SYm/CsKBiRGzmLzCZQgAAKVnA9YtIvDjJrNRrtyN6tmZhldDb+fJvTsZ/S\nsFCG+Pfg0hAUuv8tffAHI+wrQfF24SW8k1rxjOncQWyoygn89WRr0NfJFO05\niZLzEaS8lmwGySVi1dMsT/jigUP7riFCwrQtk1yjBZ9DX7axh4vVQNazEKuH\nl9PIa1MHEW47Auk0pKCbX4QmcT/VD8zaia1hqAHGC8Og7h+wpYFatt/iBxkh\nYP5smLqdK0Jh/eiNhf48VNeC9ifK8iJ7oA/opEJh3vjbi44ROlqbi1yn+4iR\nwqjhnek0+k6Fl8fwgAHYxpYhbfAZjbaV2ugdpCH4gY64AVzcwuxX1WsPHDmX\nCWZy\r\n=+L/Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGmv3U+tZjCLLvhpC1o9UvteofSrluySY9FViCcgPayDAiA4S6ygXIOiksDyS6JOVioNGxFNH1h89ZhPdGT57j0uww=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-main.20210402085638_1617354249956_0.4301575842763923"},"_hasShrinkwrap":false},"2.1.4-atomist.npm-update-main.20210402085721":{"name":"@atomist/sdm","version":"2.1.4-atomist.npm-update-main.20210402085721","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.878.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"39595005dacb816b5d6b9c37f86caf23d7794fb5","_id":"@atomist/sdm@2.1.4-atomist.npm-update-main.20210402085721","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-fHpiB3/EHT88JI1bNkVIFZfsd+dhEa0zlbo5HCZ+LImBlxCIjELrOqTsIZmvLmXaBMk3vw8HL6B+IFe1adVDwA==","shasum":"bc6db64813463e4ceba63984f83a73b7f0bfb1ad","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4-atomist.npm-update-main.20210402085721.tgz","fileCount":2425,"unpackedSize":10009395,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZt42CRA9TVsSAnZWagAAmJAP/j2+efhDu+wedKL9V3yE\n/A5uGclGNIB+yoKuW7Fpao1gzZt3sL9D5pUP9kJDqwH7LS99/1tIofGh/ras\n9m8/Pqrss7KWB46qylERSWSv55iOPx37ogT0/if5RN2PuYfZDx15csLCO0Yf\nNv2fiw90MEwvtkOYr1iRXQ7WxdLmBgBx78x3E9OSXiUMEootWWlkrpYycURk\nv4s0OCRIHIF6OQpHXc8U3CEtzOX3FI3leqkSDRDCk1y/dikpzxB2uTIl5aOw\n4zOX5L49nizLaNhB5Zxz3XYY8Olk13iVvq/cLZtHK6523FzY33y1Ze17SRwO\nEq/hj0/0C7ZuxlW29IOpi/1X76Lnc6WGVGiodOt/X+88cqZEczTRrkVHoDzL\nekaoOXH/Cz/2qery88KovmHc5I2wX4DYqtKLwWzJxkPmvivGhSRIxxWZiEsO\nuQbq0o6VEgRLd0KdnHp4YfMXm7gsWlgrCXKRxVXkbbtjnGsQJI8IEg+XeXkE\nAFaLQM7cCaGbq88awO24DGVvPbZwNHuQUhZf/zeNd+TPScudcT8T1hID4yjN\nRlQNOqdIUsAQQg3l0nK8kBUUVB2cp51Fv2S4GPbpx0N+Pg766AbAxVF1kn7a\npQ2WfggDueivLMyLIx+6YPRqLY6PEBZVJywa1WfLeyQ6Je+qu13N8AY/Z30D\n+yzJ\r\n=hb7q\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAYuSbfbyOp2JcOT7FceQzgICAMiBTmgI0OMlhMlx3qsAiBEOftNaF49U/ci2LjMVREdWkCdrLWXyKA17ryRZdCoUA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4-atomist.npm-update-main.20210402085721_1617354293815_0.11935471317150825"},"_hasShrinkwrap":false},"2.1.4":{"name":"@atomist/sdm","version":"2.1.4","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"11895133fbd29b8436bc3bee3cf55ca70ba1781c","_id":"@atomist/sdm@2.1.4","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-0UNeZnXKt0zWCnD8zRnK7BbJuYSf8qGwVVzDUv2xVJ0RQo9QSR+JcfIHSoSxJOWpsP7zzVJHwTOsSaE0iSuW2Q==","shasum":"4a8f34f49405354142c18bb9b7d54e3d94d76dfd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.4.tgz","fileCount":2425,"unpackedSize":10011791,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZt5LCRA9TVsSAnZWagAA9rYP/0behUz8FlQNWSaOIjmT\n45t9GZ/iTx82FCAuLRZLE31jeCJCERz2BwqCTKQYl/s0h1j5sDil8wllRnQW\nnAj38smkdkUWpf4f0A7Phb58zXypmA/ZU/0U6QpZNwbvTAtNi4Ig20Zq9r2w\nLxHeyYl27gYBfrVMLJRGcenRxOL3EhVBosBUy5a4TMB8UaIkyRRLvwS9C+zA\nHqvxNy6fFr8bUXMD9EjKo1/VhofhqiBOvlPbOsZ5CYQ/XLGgHrOvTpGNOdMv\nvcqL1EEcSbpiqtm5jzuXDtBiN9AEKk0TeJJX1ftWMJXmow4mJaLVTUMyOHtu\nDvuXuNX1qmNsDiF+xHhtnCHfX5YoPAV7T77uctazbvpM/jpGWdG4BN9d34f6\nN4yH+8OYb0+augeomg7CICVuWtRRcHJIdS5C/ewA1vTX6NujmRQV3x2WjDfA\nCcp+IuJ9RGeIcRWUW3bzVlzzD2zIkuCA0Uk4kMgokD3TwoZdQxdNFDhHsfxh\noBMTEylQ6hlc8qXL7tfcf8oupDlP1EIGri3CuRUQHR7+OXktFiDGnUxWW3dO\nH8PvYLcGK1Y6e9/Khtvo/gYAwtcog6xCcJWJikUlrBFyOY8QdJSjGZwcNe4W\nVu9psZxAdWX+k4zrB0hlt5k9HyoZKHHrzuZNIVIDsn6vlwasw6z2tLNyHxTi\nZxHU\r\n=BLwb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCZMe0dl9ffh3Q+EYrhzL8LO6aQ/XyvKWBAyZJLvwmD5gIhAOqoDmrKxTIYZ6QWR3o4OzVjzP7LZ4y7tgZUS1CxOf0j"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.4_1617354314748_0.34771808260498704"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210402090906":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210402090906","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.9","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.878.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"4f9ce18cdea41020d54c6115b0ed7b62e641e63f","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210402090906","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-SJN4b9g2ThcLbb0ps+k7gddpEWZWp+BOoA+N7L1N/OQq08svlv1ZoMzRwmiAvZt2m9+0G8lNGpnSKXlHuSNH6A==","shasum":"76deabdfd80fcb343f2ca6319638ec575fe1fb29","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210402090906.tgz","fileCount":2425,"unpackedSize":10011830,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgZuDvCRA9TVsSAnZWagAAfHgP/38bS+yJNCAancSJbUkH\nMjqs24sL4W225tf0ty8SUANnnYNkK12r7IB2Fz0Uet7YsO0eiwFVzOfeVFx/\niqKdrHvLKGymqfiFgfi9eN2NXBugVnYCeTHOUCiGBW4OSgTRJ3kUHvPINncE\nv/0YNKsCupIqO6e0Dr26I1bP1BE7uXwhv2f6M1+4px/IhJ2ROcYBLYWr9GVM\n9gEodTsH/n2imEaa52191+b9m98lNQ/PQ6V0Bcg1X7GPn0MyhfNjtzoka1jz\nF7zeiLsE1ea2roduCGysjaco+vFZMCblPqLuLO8eDjZau2VMwCR3++6GX32d\nUdty5tl5QcTcMWdcWVTv9CHNrJ3kvH2MXWy1OvdVahq/ZyEHMCYjHTRGdN1+\nzb/ZcmtK5u9xhoe0qFIfB/hM8TZgIv8DFaKMrzYwByc6Ah7Nc23fvldXeKoR\naJsPFLSWXn0LXP1qLt0lOTuQY7k4rWAzCwH1PbTxYBqzuC26bVRTvUFNghfD\nDv7xwXcFI7woUuvVyRw7kBFjXApZ/fVuZ05l6EYcdjZGPyq/9fOrzB+jc1YG\nsbMVYXOpABzNGPxJY+UFIdWudg4si13NqJiZSyn/pFLln0gH3Ky+LUdwCBdM\n7O45VF5L3JPiRpZbDE3BHZA4pFCLlrGowr7YrbBh4alYQs/CrgotosvgGl/6\nZFXE\r\n=25BG\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCY8ZDRpigtfHLa21H8QKBuQDG2mQ+ONM53NmcU/SJhPwIhAPA3sLqp09kc2K7aNicrJ8N3va9Sxc0ZFun8rRA7KBm4"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210402090906_1617354990476_0.1303255419331817"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210403120522":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210403120522","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.879.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.34","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"bf36d6a0a108c1c037a07f6d0d0ba9e86eb6ac8a","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210403120522","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-bEEwoocC7fsi+9doQZ3HmDaAF5UsChZia61s/jeudKQaz8OUeHhAvcFmZxDER+IiEFeSNna7TM0D2mRKrGZIhw==","shasum":"a2a1c00462e8b505f216fb14fc184265866c7764","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210403120522.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaFu7CRA9TVsSAnZWagAA5nYQAJ5WAUmJUi2LeF50SVym\nthrSKqC7rEtLZYrcqxNwXEr9e5JwdFLqPtuAqulLWq4dQiYwceFuH9x6OxVE\nJTr+1sPcy9KmqPsV6uR4l8M9i/eGgiocZJK6TA4wC09nYC4CaihvVg+LAa2s\n/Ew/n2eGFtcw69YjK/IJX6oyfJggApT2Q9A0i1Mf+KdCPFpR8oGPnklVTJYh\nMgWugYUEnxhIbL/bxOKjHdgxuJIanRcj6dc6tfg7KBdjQKojHtNNDp5Icfxr\nfuGYc0Hme0TsREhXDjnPagsHgXKakddAmlGlvIKs/D8Z0S3f4Sd+qyuWsri8\nz2SEkgu1IDgwHfwHBcjJVslJ/p4ZX9ZGcZ//sFAnuIF8YZaskJtSeL7td3ur\naHcoIOY94G7NxZfeRllpOP/pSmx7LBtP1be0993yqtZu9eBjzELLz8FZow9H\nHqDaO10SE4L0aaMURPTWIllW1p0vtyH++RLVRPTGlHbZtFtBdwajt0iiXfnS\nalCwJbYiueuQ5Y50MzC7YoxxTy9+HgePAmN15lChYi8LEPp3aVc5UxxGFDKM\nTorq6gbaB6AvxrmrGnrQ0v1Es+pPTciIynUka8sBSgZDsRO0TdMwXa7AvsA1\nhj3IGZkIoKjPSnqyraXF7TgDRgOw6YaL1NCd3bVS5j3VtFvYg1sbKo/0OkKY\nLh8R\r\n=MMV9\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHXDfVlnC1lp8JfEadMIWjlgSRNfxLV8ktlx9FBhk9YhAiEA0at/60+85f3TeJ7lkltXJ7M9FLLLwm9C9cl+U7PNEUk="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210403120522_1617451962435_0.7889056278554882"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210404120531":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210404120531","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.879.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"25b8e95d61f327ae5489f0d500f79ba2dcb90b17","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210404120531","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-z5sBS5FHRQF9N516shupjexConbh3R4jFbDslHyWCcZ1I5zHyE3/OCFWfi8L/0RFdif0I1ple2KVk9AnP9CX9Q==","shasum":"c89a60023d0865ced1db8aed505f9aefd704a96b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210404120531.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgaa1PCRA9TVsSAnZWagAAHdQQAIcB7PhLkn30SKaBQx42\ngjGWdTl09dH61M2S76sGDJkWsBa2okhtqtDprP/HpXCwjzd1MRUJyV/L704+\nGZ+qcXMSnpXG/mO3TrEG67DkedK/BCPOgq2NK3edLpKd/4Us98zT5C/HyK28\nVP6nA624gk12y8xSGIhRIJU86TJETLUtTUypaAHvzwj2ZYncISmfQ4TQASKc\nYV39TV6IZwMfyl4RHPeQ8uMbcAz+7fzavDirRg6Q0C8mFrJCXzCJSWgllkuC\nMj9yfjPt7oHsef+VpqgYWlnMhtUmFkCle2BcIPMI5rXqeqYNXEQXvvaSXs7X\nwewuALRuA4JGuKQWt1OGoMHdTXi5woeH1HV3Gn/12PCYsbRgKf3IOFR2gv2b\ng2G1chH5/oSBRH78qtvXTiBC2QQguUwsZmCUHqudNCZRpocIR7iYRH4c6mxj\nwOqdWIGu/qy+1pWmMK1wA9XbxY8j4qC9/HXnRQ7RuULlpfuuau9tV6aarzSQ\nzLhEzrDKIhnaTjA92c6POqd7vioBZkJ7VxosIyLYyxtBsu90Eqa7zZtd70ur\nIfN9B9V3pNSWmXabVx+y6MUqFdIS9EAAKwb+V+mIVNK01rCy2YmC64srZg8L\nfp2U7RRr8KPj3anwCngHefW8J/erWfBMb/lgTzpIApxvEI7h0Co7/fXKQC7z\nJMlp\r\n=MESP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCB1P2c1IerfTbKXPfIxtTDI4jQo6cABiIJGg1bQOoaMAIgA7n0o+EpVHeqnDmOXk2jk2bzJbViWFS7pzs3/YTn8+o="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210404120531_1617538382993_0.6118118815413687"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210406120659":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210406120659","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.880.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b54be3ca73816847c3c1158857545e37fecd2885","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210406120659","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-iSvG3iZZZt8dfcY6ZSPsVULi6J1MsMUb+j9Vrw67qtcGD6Wf5Po4nczii20bqHyYgQiEorOfT/52IpjB3Z4JOA==","shasum":"e38909bd3a32b9ddfbaf57925921dc4409ada9b9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210406120659.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbFC2CRA9TVsSAnZWagAACcsP/A2MhnNy9b2iUhylY4j4\naCdYpaIbSsyxZjLBPjFa8+RhsAO8hNU6b61OP4LQbujBYH+H4PTPSRFyg4rk\np1Glv9azKDZWomMHE13rXj5ISiDD7ZcyARPbEB3Q5ZzZek8j5CmjYIQXnaXN\nARi7oCMZ56v7HikhXW6Ek5QdJ8vKhjYjIvbGP888RFPaN31JMkyQPfXIIyWd\nuDsazwdtqvy1Mn9rtZYVMm5XuODtArm2rmv38aL+VGVCmNsHRwbDRxm5T9M5\napk7PzZMGajFesD+URyTMdDZ1tGbHz7h2pEEnQ8iMdmSb6gkVNY01zvMULvx\nkqt/UAg2i75iGLMqBsePbLBJmucGvb/n0nGjVCX/JgPIxMJQrNKxfwoDnGVn\nqdCkhe/NhQw96E/9UT8cyGZfypjw1Y0x/Dg4J+h68gXFmX+lKZe6dHob0Z+u\nCUEs0xlLJxb4ue1ntlnijB7523w/cTCC9EMQ8RIKJzorOot8oUg/WVNWOJqe\nozW+kFciDrXbD+oyWRHI6QBX4h+vgCoohmO2c1p9CulZdVOnMly9XbmlI3W+\nkX1zkVSmMrQ1g4PX0Gd3CET5vCSPebd54ZCgNHjSzt9/ZDfr7yJXlOS9/8NR\n+pJAUulgwE5d0MgqAyRxi+Mnu4IkSEPYrotNuGhFH9rYRiMSYEuEY9EVgxZ4\n7RIR\r\n=4Lyb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCI2YuvuQFQUaNSMwb1sY5xcP6HAKm7HjATD7Go2nNJEQIhANUUuur9mHQZNH6QI0iu3fhRwnzKPh9zd2ytLPNU1z61"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210406120659_1617711285250_0.36588482364408015"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210407120703":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210407120703","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.881.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"1700bea1a75e08ee6384c77d28f7204786031554","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210407120703","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-Fn4HWerN5EsXWV328EzLohOezSlvs6L/ICPXlSCdtaiHDsxWKq6jWHW5Y3UyhxrGGb7M7im5lTm1dzecyAi97w==","shasum":"197f2bc973d3d7bf65de6fa15ab12b44ff88796c","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210407120703.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbaIvCRA9TVsSAnZWagAAznsP/15d982cZB5BNLxaVjlb\nn85TYbZ8sJbS6IQC/r6XC8/QVDUf6EJbGQceTFgoLgTA46u8wtzs0DAl5ZEb\nEre8HrbGfL8/1moXYbjSy6tlN8OUdMJqOO8CvU0A+AAojm4drhVu9l7i/hK9\nczW9ZMtenkkyBjWVX9LDXSmBIttO7+lPQIfd66j6oLcQ62LPDNRQ6x5uKNy9\no4fnbPz2P9StPCB0ghbufR2cH6brYMRd346j136tteNS8QDb1a8NRhFmlPF7\nwdpXGBUKm+R8+6MPZ/45akpS16isn5GuFMo/FOGjD2kgFPNBrwMnyEQ7QxZD\n4GwbyhNYAngnreBwFY0cwYq7cq3BFoHHWoJwYzAjX+MQIs48mcJec4+p3Ssm\nMNgUVZwm0sQmp12L6InpxR/utHqvGYlqydya6Ta4f4U2tF4knjEkNue1ftcC\n6T24emNFfF6pLogOQ2/UP6qqDsee3X5zvEKF30z+9OBPh0yUc9zO6bxt98Dl\nv5yNNn9DMuQOsiypo/5O+cDw99Gp0wL8mwugHFS5VCmesd1elGcjZRoF5Hav\nH1u6hUDLWkUR2yYzMDmltOFz1kY4slP/hZwehJx2mVWB8zX2rxZIhRUfE34k\nKvfInFGiV8COS5vjIw+X4tdP1zDo5KsgdKV+FDB6guvfBG3Vyh1SWlUb3ndS\nSDdW\r\n=kc8C\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAS0PqhGrjtg16WS0L+yylkikVc1gQxdhU/gOrrgi7j/AiEAh1OQkoYGobxZj0gLO4fqHeTNpAoEguNGeI+rJgAi61Q="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210407120703_1617797679045_0.009487495054704143"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210408120517":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210408120517","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.882.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"b41027b90878aedc51d105e96a74602e03f2a987","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210408120517","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ry0O+JBqR81EoB8c0nOfF6LyqHkFb5Rel/E5zoAOMcaUkRp9LMmgoV9fjfLAnYCcfIy402FZJG2RLVxKtoZfdg==","shasum":"6024bc171b25bf4620e75d7916be5f84fc03ac67","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210408120517.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgbvNJCRA9TVsSAnZWagAA7roP/3n37I1aEPD7s5v7X+Px\nD679CspLnEgPQM7Ekl8rc54FdpjO7hBW6DtAE+PD5NJ3HW7lyNmvK3Dfsq+9\nw16yA0WVwTQ/K3QLIERkMUjXnw1BtYYSuXe5rqh2scbFGw4yj12NpgR0mFtI\ndkeA2M5hULL9xmckucBbiyrieDHxsk18bgnkNsDp2mdx+DDPupKTvg7+0NfH\nd0pKr+ozkbnK1OkJ2sYBWAXk2zuwu6KR9VLaxXbBup/DWh1XGU9qdvzpMWf9\nV6hLlu5cL3CMn+5Xtr+Yfcs51kGs0CUYpQyQijevtWlZc3CHb8ruQ7n6Zs0+\nceqgqfLn807xheNEyBOKhj5ZM4dHHzFbHYKe6thAMz8nl6cGhmVw+BiET8L0\nf+6inziGZCWvrfEYygzA5z3s3d21jAoXqo+fdAj0pKIXmAoNAyZb1O6jCu8M\nYQXo41Tvc84xL4Eh2CuQtk+LllimDAAokoCvRywm/yNC4Hz2pvU3XSmV/PI2\ns9BgXrMXIP1I48dzN74BEKSKY947A1ErO1dSJA21H4DpIcNFYJek/JoDWGyJ\nqMFYi7KCDE2yA94YNwtN1hprJRHn5Q7bJjgvaK9lq00RUF/171TB842KzF7U\nsoHTZfHilgZ0RMwEmQaFZaf8IYEsg2aKyauQCPpIrZ0av38bVLR9TM+OeGXb\nQ60o\r\n=S9JD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBK/OFyTJADgNh3SGk4bGn2v9oztFrBluGFIZm8CGvobAiAnPy2sNVxLFwZ7eeizIZRmS570bwh3lhr4Svdn+MyWcw=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210408120517_1617883975800_0.8920287394495867"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210409120526":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210409120526","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.883.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"56369a369913430b18e9ca2d5146f0a558fa1551","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210409120526","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-V2t6yhDzRkkT8ONNgEXoHFxxLNc0rWfssrd2pZcMWICIc58ZqKNd42KufW6MPo2Eo9lXCgFirfuCS25fTm00tA==","shasum":"68696f0e8d8870a1ede6a9b37b399576c3dafbdd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210409120526.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcETGCRA9TVsSAnZWagAAD+YP/jEP0iIQM3rWpgWGMqba\nSycS32arwkATKB9GoVSwqPQg2Ec12CQTJ2cwtcRvamL1qILRfml1ZmnnROvD\nPyunXKfJJQO/+1W8CzRjfv/8eHreaYYnJSEThAQj9GjSxq7HoO6+cWTsPvLi\n+DW9Y0x4JowBh8N0jD6DRM7JbP1/dU9A1e56o9SFVVTycW6UuuHu5hHYculH\n6XfKfLALPK0j9L5ZozuDriQYFUMyR0lKDjwqojs+DfxoxxxGzmhPxu+beghV\nYW2vd32sUXtN9k6Aozi12Es1PF+gkjDvH38fyu1Gjf8sJuAiOG5NrkSfQ3Ei\n1EuAMHRppNWxzc7KwmxUA1qNkRFk0ESSaeJHTy43S39gcMuOJ5kFAjtC+RQD\ns0RmZ7SpOKvg1QAaW46D0W8b4O8dp4wsvPkKzho9iK+nih6mU422++MeuCQy\n+8owvilw8osRA9VGCpf+70pQWGiOqraIcti17m7DEpcQ8o43Am6SpbUB2NV3\nZ6Y4InY8d3AfiJv3lvppmUT+yzzs/z/keD190RL4+nMMQ4ajW2Lm3Qk6me47\nRlv1yeas7TKoG4DnfqMK45kJkGqwUq/JLPl6n27m7SSwdyRWKINzKG5hpMOs\nFbzD9kM+g6sjYYDvr3VH1cqKVa/27PIk1DCABq5JstBoyutOBbdKzSQWJhGf\nV24I\r\n=IRBS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD4vsaR30es1Zshu+qwBi6oLAiw2HBxLsZZlY6XBFrupwIgeuEZP+Ogq+sKNKJtTtGXugPiWtQqOHbYD3cXfnuZ0Vo="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210409120526_1617970373515_0.36443011798536107"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210410120519":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210410120519","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.884.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"7745be3dbb2f83d211df0481dd0068f65668a130","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210410120519","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-43oAvEz3clz4g04Y1CGGBn37s8HD4iPGJ9zJpFkOShgg+xpHSgLzEwNYJz5QdeEMUPWKHKzYWg77ERwNZzOYBg==","shasum":"1ba5636bbdae4552fe5ab9e263dba7a304cec2dd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210410120519.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcZY+CRA9TVsSAnZWagAASUMP/jSZrrMP7cU+vI2JvsKb\n7V1xYMjS1nL/UC3LXQ/Xnj2hXRc0I/ewOYYRAlvjssktQ26QvfDhU+mN2aYH\nrVaazlWLl8pyWD3DriHtZ4tFeCpWR1KnMMTrS3m4FY8kTGr+444Qak8dbo3g\nl2hOTMmUjz9nMkrETGIRBy+nBjL3ZEwX8RbpyEBimStqtelzOcsWTYfZijsc\nnckDfPJ9JgJAfmsFAwM88gTKGa02q1huNnXW9LGl/SXcYZWYrAdOXRRl1eoN\n+p7NybLdOfGeCYkn2q+2wefKUslv5MeS6urm0QUUvtGD5VgctAdLm194TKXa\nNu/8/fxXzw1xrbWmEUEWcFDFAQhfw/J/q4PuruhyOB81ZZEOOnaG0LU7aSFN\nqFBz3G4kOfzSb8NbbaIcFuoMmQaPpmkoQlc7fuNDYSNVIm1LZu49ZQh3VZsH\nY5Ty/pxoG6p09gw2w24eXeTM+Lv7aTPB5WLntGRIGm1v13C4Lh4XL+24Siu/\n4C6lph9qkOSCAfA4Inn3a6ZGljx4j9/KZFGGzcJ3a5Huwsaf1LsoXcxHpFDO\naSApiVZPHkue5gRtmpOCpA9nDDMkkzkvryVJaLr8RWqATZijVBNRp5USmpbq\nvIiur/KJ7ndHfr5YzJGrfyoz0u6CmGskl6AgjqVkqZihTbXwwqmXEYap5d5E\n0CAf\r\n=J52Z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIFkFuH0j39tJWbrgEaDi55XcT7zoNUUcsmL2j3mtQrmsAiEAhoqSj4vdn6mU9IHpJQXYiGgqj4dz3ftDYUjB3J9nkuc="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210410120519_1618056766364_0.4531740469098824"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210411120526":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210411120526","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.10","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.884.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"cecb49036be5b24965d4d5a032722e7f5e5ac7ce","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210411120526","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-0GVOk+WpdbMPPG4QS9ZhjECqI+/P6RFdYh0mQAcAdK1piexCUj5TAy9Hw8pYOcY/1AhiTHftWWqSPZadzHGlLA==","shasum":"e43abdfb70aeff1d10014bac767ddefc0ac5acc7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210411120526.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgcuerCRA9TVsSAnZWagAARPQP/2Mo1+0GpGsyVGfYyD+V\nwef1E+Sl0JzEufLAY7flhsNLX9ZLQnRKDqdj9DAFn0Ja5EtWWQJFSjoY6pPr\nJ8U3RZf1RhZoNSIWb/b807eTyyo5hOUzKteT/chx3kwf2sBrHDBa6zaYuUHS\ngypuq3Tw6hZQqZHarikUskOEmXX4IjRCLESoa9ejWZdoe31MjCWHG4T5miOW\n+UW1y+pg7qPbTyrc0WW7zIRrKRZxNbhNUEh5l+M3TFXpioYNkX2zvWr6eLHP\ncGxHVdd6uW9S5aVLnuqYUggNHX8cVpEthD9CowfTONiguPKiIQI1z5/w/2x+\ntarf4NWM9s7yy6ZfKlYwHOgnw+gGRSpQ4ImC8gwaPhCHuXvxJPZPyGkOPjxi\n5pibyU3VuCtaXhICju1YthfZEKt40ZaQPw0MM4G9tUUOdPbPEzJFAOvbqu37\nfYn3vMuIFPByOaX5NX5NqrMZ0fJptEnvIXktPfgDotHRk9PIrX8/dC0FLJ9Q\nPtAc99E7WiFVpOGjTKypf1ujreYqBpnGbLp7tnDM4odQIglQDW2CdrbvXH8N\nL7N87P3PQGbDNzuGaswLizv9uTanqb6yncmcKoxl2APBaxmH0fFcllz6YgC2\nn8NUdeJCk1bVqCwd8JEmM714TYhzRqT8iJhpMCzDbqVuwFuOsMjwggawAqqS\nLqQC\r\n=Z/2m\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDKrCFRZ11LKCsR+HO+w1VRRUux4AlMqdYAjOac42O1sgIhAOF9jFhmjtKZaSzHjSa9CJFoYItCa1TYPOqxHayhP5Aj"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210411120526_1618143146870_0.8197452967572465"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210412120854":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210412120854","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.11","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.884.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"ca9e20a124619b82b509455442063500f5366844","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210412120854","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-ebO6bTU/L6vdxYHbOyK94M5F41FzM7bNsJwChRLalLp3ycE+5iS8SO0WJYezJPhrvEplFpEpDG7+HF7IplBGtQ==","shasum":"39bc0f8520ca9d29122958f30010411a0a489584","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210412120854.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdDoICRA9TVsSAnZWagAABcIP/3uHmBFbeiJXrqDWVMr7\nmjSFohmx8OiFA2gEhkZVUGRd5GlR9Rz/DIY/6M57RiwyCFngbjSLXXoLugoi\nvc5yHBQoDoxg4rosFMa5d74wr4WqlsaUJDLrqMnC+p2P9hxUJ8yPv2C8mq18\nsW4JT2Gclv4YJriPN824b1K+AgbvJn33UJ9wedTT+KlE46y58kuVZ4RIeJgn\n8Xz+UKpESGOuppID5sjeIRFsFmRDhPKe8Dqq2jjRjPWUNQQVihqU5hHobCWv\nuQXi6ITZgqS3xewad98Z0STkc/XXY/78uVEf0RcgEsVlsmoMXhg+CpD2Quve\ndtyVTCq6K/y4RxDcXtO65bfydCQomjlyLOjJ7Z1HInVYeBE8hwlI09IUN+8q\nok6802bxYMFC3ZBbj5VLSKNbzn0gIsyDnNovAo+2hMbmnu7TALrhERX/z/XR\n9GrPK156GwxOR8A9GmTNliDgN1oObd3cD/w3X38FiFxWDoJBwKsILddPU3kz\noZ025SDOpCnmhuUHPpKTfdWnQhem95x5GdW/ibjF60rX9miUQOqtIxd3HzBI\nfCgqRX92KjWLKl4Z55tqCsxjL9F+mkx1AKtigRRIf8zsnnxXVuKpo3WUntji\nbE37ADUoIigbxd48AmbRpAhSPbe0ZenYclaOJ6xyTcRz2P563bX5hWPamLH2\ncUZh\r\n=l71e\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCICPdjIcaotIXG3DpCc3shwN4+teqZKjQUU5rbH1/qLDBAiBLv1NXaMHjkqEKmxPwA5h2whJMMh/GV4G0N/ZHWVZSkA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210412120854_1618229768269_0.36400928055243953"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210413120555":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210413120555","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.11","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.885.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"be5f05b4d9249e131b70834b66fcac14be9695b7","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210413120555","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-PTmA6UczpTboGOKwxKrMTFDaexDM0yAm0nOKsoAZpplioKb0kH0qrumvZvTTiULxXeApANMglh5s11x3PVImwQ==","shasum":"3779686ff2280d41d513ef6ac86ccc82ff2bfa3e","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210413120555.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdYr1CRA9TVsSAnZWagAACzgP/irGR9LdOLQ8SCcIO1zk\nkg0mxYIRo5hlbX2NXKHrgYbOfdweOdKLuDoQs+P6UZxexmlRQqJX+w30S+Pa\n/UH+avkRmpgJoQ6F8o9HrogYuB3Yz5f305KIzym8Wcz2dd0vpQf+/LXDF7Sv\nuC856d2et2qCvU7/+tbLl8PLoZV7yckW2we69yAaTRBiEEEXYBOC7kizhRJT\nYHDJ72vU2OEEAimg0hnGEKByenqlDJK4jTxTmfq4L/0Ff1DXY8CxvhrJqitv\nTCBgSSmmydU9PyTzjeLm3vC15ySFgGs29moM/7w9xAN72iZj5OM3wqa0raVG\n8AheBCvrQD0SGw9VZ8dawkYc0yjyhe18DDm9smMXvwezpUwQZADArd737GRQ\noGw9okx6NxXPVuZVP+EBUUB1saRyewWr/3nZoe3FEF4wFo59iM/LlaesZ6Tw\naX/vBikle+zMWMLnLccZkMj44TVGwM88SFrDgSI+mWQBI5eJnBoK9sb3DzxX\n5GS6ZnjESrw0D6kSq16EhRdvbHdN28xLkuQG2IAKWP9Vow8neVMrgmldS/gE\nuqkkGCiWgJXddzJQipKCLUsXOJLmvihrYQDawxoQYApeokUjuzKT3hyBRpyt\nCFmLiU3aeSrb8VoACysXFyayCKhe3cLUbsVG9L8cek53YdCUlPjLUJtgLvHV\ndkeU\r\n=2fVn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDZtZUnioLE6lfO+Xfx0/tMl4VfT4fBmIW84g09EKJzfgIgA/D8amgWkUYukQRUi+hJqHIRbv7PTyMO5Lf2cz4SLYQ="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210413120555_1618316020486_0.41608642205191027"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210414120503":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210414120503","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.11","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.37","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.886.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"02d2413199b418334f8b58ce366a85e5e98f83d7","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210414120503","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-SbC+umqR4m7k9MWAX61YHcc8a0Mu1q/5GpIm7vUJbi8P56/FarUjLS65+YbGAeKy58aDbiyCZly5z2BCRjwg9Q==","shasum":"9165a1957a2a92774666b6efea71c97979b33fd7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210414120503.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgdtwjCRA9TVsSAnZWagAABkMP/jj5dONETni1JqCocU5o\nzVn1h+QyDZyogMVSDN3CnUqPWpo8gst7c8JBjydk6CWKb7fAIRSFeGcd3fbp\nciD79uEDqQylUH5nYU/URjrJMdZq47eh3FhBpDiezpHDUJxyKdjmF7vRxnj4\nsPPFRiT7qgMMF7Gfqquz76RGAlh6NTOdYt5D5JGJtCWQEDp4NPQBz/GRyRd6\nPQJdRWhZq9w+PZJ942Q53ljhIOBrfLjLdt9QS1e6Jk82vIzpojR8StJj5GdL\n7SUh/7s2V0+uyOen6OWfnYLu8MTZ/8WR8WLv+CKMt/cdDuMI1y3KM4lFPe10\n3aqXpC4wTX4suYjKWX52Gzkb/0hgAwcTffwVlgNNa2LcOpwX+cQ3wodVw37c\nVbK8mTTcQMATH8tWe3CKymUngDTszs4hAJtgvgYpVv45h5CZ9Lbd3RaXXqvs\n993n+Q8BggMY5nNfsLUTH4QE9Rf+ZMuxCxqPUOYjRg081OjLehaCutbpV8QA\nI3qhhlR2zSN8IjT+yyMvl6iYmFKwVdIHrd1gJhxFA6XnL4MffzSeH6oDKRyn\n+sZ8TyYc6nHjsDTNZipnD+ggCqVpeapOQPbJxDxD+CaHmHViXkXiphwfWe9p\n2ioosuIhWBN7zQ3boWNQCcNIiCmGAmSzG4oIv85dt+cNss5v4e6yZ6lyV6q8\nBv/0\r\n=hOhY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDp/TQ3THvd+qdGC08cssH8e1xY09VwzNNCVbIa5WqpnAIgSdvRQEmhhZzk11fGuNK9HWSwXqmf8AEAA8947LTIpy8="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210414120503_1618402339042_0.1662361785008306"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210415120553":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210415120553","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.11","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.39","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.887.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"3f872e75e21e3ccde162dc06b06b28adcb175502","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210415120553","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-rVf/gtyvbKiHp+S/R+CZcyFcPPmPWyyUtsmMoHbzoqA8ezOr9ktb2fprW6eugoUj4wBxj1lM1lrQneaFl7CcyA==","shasum":"7851fc5f6516bc6eca103c5e14a90ccd4fd1d644","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210415120553.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeC4MCRA9TVsSAnZWagAAzQsP/2pZpTC6lxndGMqu4DyX\nJggU5aqDmvU/N0xobJDOn9sMs5+St4eIu6/IfWlSh2hLuOkiy6oNC7/o5qHv\nQ9osSAc97IbkiPl0IW0K+pJu9608k6V8UrJKfSp3BbLYjqLbNUjMLYIa0NbV\nSQDGj7YMAaPYIMJaS8tsS26Fjsi2Bl8DF5KoLm4bWFUh50z8Pf83vsFJdMNq\nC3cdNaN43BOogZNRPhBhOEl9eLv5AJoI7X/zZfUxQzx6NMiTSXO3u8k+zZ9b\noblpLtdhtyPe2tGugU+TWQck3zpPiP1nK+Pd54D0g6HjGljH9MhXP1ALEv7d\nInsa8L1awVD5Dz/hqoKEXnWN9j0wemmL/04+aG1wUV7Z4qJ4jnmL1JM+MXX5\neUqOjXfRn+kx6KnzAd/Fgev4lVFLa2kLrXiJiuFtcy2V1LqJOEX2ADQBAgpf\nfNtUSMcV0RT6avsnyRW/WZhJBAtM5Y+o6g/VE3NK0fNgOblCFizbSnM5YGb5\neCv2ZPf6w7RZaYLMSL53zviSXJjrjpPVFcLvTevbK6FnRiDRRWZW+dRSBf06\nt1ZD68myihsU6QINzxbe5gd5GrICZqOJmtBIPwMQF4ykp8Nr0MVykDao7lgJ\n4wz8eABfIAVv+uMEeIFqb7R3oV/Yp8ibDLqqaPpEdOskK9SdCW0+GqPcZ7Cf\nAjZy\r\n=weSU\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBU0sUuONb07THfJxLo+EgC0tPGgCmnT6dPviQda3PagIhAJ+3K5la8LQezCpL4np9T0nfWhJ5RbQPAT9ieR8Oriy1"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210415120553_1618488843604_0.06338552066161718"},"_hasShrinkwrap":false},"2.1.5-atomist.npm-update-main.20210416120811":{"name":"@atomist/sdm","version":"2.1.5-atomist.npm-update-main.20210416120811","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.3","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.5.2","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.11","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.41","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.888.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.4","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.5","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.2","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.35","typescript":"^4.2.4"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"207ca27994856291dc563418f1400e5146c63498","_id":"@atomist/sdm@2.1.5-atomist.npm-update-main.20210416120811","_nodeVersion":"13.7.0","_npmVersion":"6.13.6","dist":{"integrity":"sha512-78L894rI3+VxoKCwj+T3iHrSU7ybI8V324rHzofRnT0va5U0GDw4PtcGN1WGOzUEpF4nhUGamAUUeiVbxjadmg==","shasum":"762c04625a7f3bf18d69265fbe9bee5e87208d8f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-atomist.npm-update-main.20210416120811.tgz","fileCount":2425,"unpackedSize":10011910,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgeYASCRA9TVsSAnZWagAA7NwQAIK35+Tt+0JblWuqT0Ti\nWbi8B61bGViU2vHkrwLy4g2ysgRtshxpfu0Bszv7qZzZRDa58ccV9j6CdNnj\nsp9LnrRZf+mYgcNHtILCCdTKppKz6ljR/5oHs2wx5PR5jyx1obFmXp4rTBVl\nhPfPnbXMBXh1f7CEmW6c2TdAERZ3ZCAWNruTKqxSrCa9OG1U+weNfkBhwOqL\n8UiEr8hCocpVW8Efx89Pvlv4U1G4GTwZrz/3YldykbDib8PoNd9e9CIG1TtY\n6Omm1tODVR3bsqQYd15q9TBapyP3hp+AJM6Fl+mNuKOVMrIu0R1vNv/8beVE\nz/6lRzCKfAbzvn7KzoWCtdeELZS94CrnjMi4p9YJuuDOQevCk+bsp1tPGuRl\nbJ9RTf4KqG2JhMz/VwAIkd/X8pj89+gBnSS4apyq+k4gh4occILvUsIM/8aJ\navdDtNw1QZWdV/gYbuI9JKJ61zKt7QSruovxG+GH41+Z/HUXV3RORWQCwFmz\nvac/59QBJJgj8KHkY1sKRQARshHFtYdPZ2PSrhh+p6igsYRiVZozYPQLJ6Lc\n7SRLhqI6voCdEF4X5XRVdbQEnBk+o3SVXOK3w4Krpfd7tBQ/oGRxxLFz862K\nQMeVCEcCDWyHptUjbYmOn6kBmUAYFyrjmu0LbmS1U4XCyfptirtceWocOCUl\nwWRS\r\n=IsCP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCp76kxyv/AzD7Q2snJi72/3R1kzEXSI/5DrA4BsqPcggIhAOs53nB4GHZ/IWuMd9tIh+oJP3sPYRj0oAhIZz4AGn0i"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"ddatomist","email":"david@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-atomist.npm-update-main.20210416120811_1618575378244_0.7052708368441933"},"_hasShrinkwrap":false},"2.1.5-branch-event-signing.0":{"name":"@atomist/sdm","version":"2.1.5-branch-event-signing.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.2-main.20210504162439","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"0c3c1989245fc9db88c01af8ee3576e00e750cf2","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-event-signing.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-dZsf3FttLLcIkvCTRDUXV1eNHqePFBb/AOuVSo+2BGMHMhjs+zWstq4Mh9YGs36DifemU9W7b9mGbCTzVNdJlw==","shasum":"57163b9b8a45bf4be550784fd33138361ac127f0","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-event-signing.0.tgz","fileCount":2436,"unpackedSize":10014114,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkl7MCRA9TVsSAnZWagAAzVEP/3TLsTOgrhtulZxECSsN\n8qggNi7OhPK9hAUo8rmIPOlZRsqQ38PEmYqj1lCQS4T4S7mvtLIjp2D/atm4\nQfWuM2lkl/xsNY2vcNeIZIthYQpj9pLB4F33MaQskpEqhdluCGqog+LGMuFk\nWbnoXptwNMWlyoQyvH52ZGQ69cqAZnCYZ+qmxeuF8w94Y0jTStLQVumuRqWE\nwaBwo5ARivsxGSxcH9z2S2qkhpJ1+/JGU6P6NJJaZ4whyvJOZ3mCetv/usfS\n6Y7RzR4mhOWuUGSb9dprmPC7938EyQqI18eDEAUND3vw+7NQfHpAndKClCF3\nIr7jtXPRw8Iv9+ACAXrsQEFphKQSxZe02RpFr6W+CwWzRnDzGqgb1sDvoN4z\naL59fy90e8ERzzFUZevLEL7tVxSOJi1CQjsk/8GIE8CxQd7WkWvsNOKwDw0p\n+m6eX2noVR9C+RHRp3LXASjgCn8w4vHwmIn7jtv9Utext9+STuJsNXUkqkkj\n9m7IjiCmAaTRTiRXRCTvwvFO05XFS229X6WiLjOUpZP+RpICnzcfaNjozIPV\nS0ubuUlaH5ua2IHzWMhTnFR6WaS876AvIM0Hq8zGHWKAQkMyz11V+/kNQXDm\nOzLpRzTzV0J9jwVJJf6bj+eXXlQrkq2qjWCRGOcA603hRTHgtVNO2fpnX1Pr\n9uT2\r\n=746z\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDeEnkULgy5myX2fadyrP0Nh0o3ENBOv8iWMHaTjzOaXQIhAOSeCrBfVO7nDbwKB9BOSeRmH4KC5OYarZQt5WOGeIM3"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-event-signing.0_1620205259932_0.39300462686683657"},"_hasShrinkwrap":false},"2.1.5-branch-event-signing.1":{"name":"@atomist/sdm","version":"2.1.5-branch-event-signing.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.2-main.20210504162439","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"fa6232472dc5e24643fbd2388fa02a098efcb275","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-event-signing.1","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-x4YInVWJq23tURDWUwPiXI7tZn5UnaryKkVHDvgpbdhI048SRJfp9NNIWYTwZlxIsXhC4pHZYvDFsdRDpa/64g==","shasum":"6d2f6503f51730466091e709476bcbe479a768a9","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-event-signing.1.tgz","fileCount":2436,"unpackedSize":10049021,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkmoMCRA9TVsSAnZWagAA+usP/3rP3XBpeW5ti0Pjf9n3\n3FhGHj4uyOSTDQEIMep9lSGk2+Upz5a81mwTP4hbhpxz0tQisXGobCpoBJIY\nStk/SvLNydpZ5K43gmEUSNuSpN5IJJa9xdGUrhiZOrW62lgWlDAnotdQu0wG\nepvQ+G0DFCEU4lQclMlk8GDcOvupwBpdjyXlE0YyUGKaX3/lo50cZQVgC+Qn\natY54+qCSz/Flfw48u12NZ0dUZ36LzleKM2u/lcTZ6emSdOU8+f9i7LFiyTj\nCd0lPLUh9TmIJbuiI8B4k50TtdxIzdb3nSLWBRvvg+IqPSzzdfzo47JuG63O\nZrhFGwh2d9YU1Vmco1y/AgklaC3IBJ3I0MS4zXrKwbAyjo8a5g0WUPYIsInI\nUloaI6a8tEmonB9n9jQOaXi32PzpoCp2KhGMgt9yxwy6PxqzA/22DGDKdDTO\nD3Pg16bv6vz6hpxXau7h4UlXmZWgPMODNkkAaXZzM79DlQI1sI9+uAyts5GV\nz0IPK4o6Y3AlR09CobY/mib7hrbvPI0BzPcPAyQmMwbspP/ngIKCKapzmoa3\nOJaMTX5ZyvdbBI6hh4qrSA4VbF09fhxow/+FcbwbiQ+D0xtuSR3LZtbs7RNo\nKaYxn+J1wyYzRzLOSEvexHpK28VfEB4PoamKTwjRJ7Nc6aPotLO8PLUbfN2y\n4aT/\r\n=TTRB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDy1GxgxR12j4viNQ86B+PZUrB75IDJFoF1BvItqty6PAIgRmqpvYLHdB62IFkdhKTUeRuKh6IRivWQJxJaTRBVS0c="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-event-signing.1_1620208140262_0.1547642042098103"},"_hasShrinkwrap":false},"2.1.5-branch-event-signing.2":{"name":"@atomist/sdm","version":"2.1.5-branch-event-signing.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.2-main.20210504162439","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"643ed9bfe95118fb81e26b082611926615842c7d","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-event-signing.2","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-qy/Fp+QCJHG4nV4Rzs7/1nL9y+praTiW7GcQoTmS8AJkdLaPdVlumdVougWpTpahG6mZrDYVnvrVbpnF+Uqjsg==","shasum":"645fd5894ad84dfc17f17807b600fdc66e3da03d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-event-signing.2.tgz","fileCount":2436,"unpackedSize":10049173,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkmqnCRA9TVsSAnZWagAAuKoP/RJmKDBH4sSu4DhnXmoP\n+ZxsEl5P+RrW0bO73jjIL/mhSpmB/nXWf8N7Vb7VpvVvf45qEA+WAv8evcS8\np9219UZe9Uq/a5tScnxXzbf4FlXRvAnM/WySwSq+Oa1SJKqdrZIIRKOdVy+t\ncex2W2vK4pvk2qml3YY1i5/UypAGR/uOFGn27Be0sgI+gNDErWx5cETjRMGf\nzvVk6/zcRGSaGs0lcDQTeX34lQjzO5ST9AUFc5FS/Q/H9uXKacGteBRXyd66\nLbXK8PZ2SLqZlIlTA59+tzwmfrU/TSrZTRO8Grqb1ep2C8prmAmjI3RMWTbH\nwnjK4K4d/B7HwMZtBeH7VCgJAYR6kzqJ4inyXpmR8bBjUHr1klZLr9b1fsJ6\nLeZGYh/VmmVEqF5Niz+fxV1mZuiYtRnauvcDFNqB54PX42i3by9nZYxRZwUn\nBIFRs7sxBiFInSEfAlDkgjTNcb4qdy+xWn6zLHQbbJdQOxgbgVsDj+JJG+uZ\nHfoKBuDykei6QtoZUvqp98zjB/22+VK2/WzNffQqf3np5cb9Hr2FmbAF0Ayo\ntXqyQ2QuhK28vYXs1qyyAdOiYfSr7KI0m5irCV7LmMnmSVJ3/ML3axCU69gJ\nGe0952OVxF/2SOhhvVTWQ3Q3ZOcvCx93fZ4qDbhIBDESC5+XOZeM4DCAoefq\n/fPN\r\n=I6hu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCeAL0cJgPpKhUjKV4RIdvNnj9i8OYVdNi97T5xytEsNQIhAPCDzz/p61jmHEPZhuEhjxqX+i/M93mbAKsf7/2CZU4g"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-event-signing.2_1620208295269_0.04475876693989367"},"_hasShrinkwrap":false},"2.1.5-branch-event-signing.3":{"name":"@atomist/sdm","version":"2.1.5-branch-event-signing.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"2.1.2-main.20210504162439","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"e4d47f767c2e3b2a17f23309c46822f86e67c975","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-event-signing.3","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-iSpsBMXgv3AkVJLxBqMXkAmqk2WX+FkybD0KxnfG39L4L+Nr7igek8vs5C0YMfwVVlQK5WhQbpp5WFeafFN3Ew==","shasum":"507d7b102d236b703bfcc741d40c734a3500bce1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-event-signing.3.tgz","fileCount":2436,"unpackedSize":10049864,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgkm/JCRA9TVsSAnZWagAA/M4P/jw5/ceXZN4lMmaRblA1\nhHsWL0O8Q7vBTWxJHqCMlDhwD5IYIjRAzwRRWWVCbo86evfQweOPMavjXGZE\n+CCZnko/uu+P3yxSp6GpByzPoW3TPfSk4krJE5tbFVPF8FRRhwfy4FSQiThU\nXptgjW2r0bfLSW1gKmxham6uYdnfFak+RBcJ0Ta1QLug4ePhUuxMJVMPeYwU\n/+hCQfsP5hbWzM1/K/62tz34wZdUaG7K3UIEW1uzCAc7JdzfU0DjholqZzTQ\nZiakPigkKnD6XMBp7HirWsYlOMiS1TPAgiJ2tiZSaEWTKqdg01vAkUQapSEv\niVQoF3n3Acdjf6DxgHPryguJOhrykD/wKO1VkOoGqegz7Apz29u+9GHdzXKG\nAk6CKORxmfTZDFchw6zPWcVsvv3IEWR7sd9TkbSDaOhxYtpPBD87SzwPc1Xz\nDbNdbFGHoJeMFnSwMMHIeYl30DN+u/AZv52XO5rrVU4XLUTrTY+LGswPpmbe\ngvBa+ke19n7S1iiAKShTTYt2yTJ+SL0v11EXGdiQpl6vYdOoL+hwh+0baYoU\nSJVBSe3uyt1WAOdx47ptg9sNEGzvlqUSm/DnDk3zNrYYc8juQka6KQ5Uu3k6\nrZyH/l1p8WpToJdBYIwp65CZTlZUk6XVpeScQ4+doMTXMvMnFsPFbA80N+we\nqTAd\r\n=aRaD\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDEoWs6fhlHIeNE+32lXeR4PLDH++RLXbg0M9SL1MgBzQIhAOTwg2ikOztbYHMlw45jDDE/mwUZa1uuUcKrVFwvE0/h"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-event-signing.3_1620209609144_0.7008146118433636"},"_hasShrinkwrap":false},"2.1.5-main.0":{"name":"@atomist/sdm","version":"2.1.5-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"6b3c60738309a6f6dba48b47d79cf53c76fa47e7","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-main.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-uwBWPCHNwnMGT42quM0AtW2JFme8ebAn4DaFsIDZBw46CPYu1Sx0aICCz3I0m5XuW6HMcA55r6FFCeJQXMxw6A==","shasum":"129a684181a942ce06c88fd8d624ceb4a83c13ab","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-main.0.tgz","fileCount":2426,"unpackedSize":9990717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJglS6QCRA9TVsSAnZWagAAFLoP/3n0daSgR6R7FwDJEycn\nx0kO8amQun9g7CxREQVrez4gK9KwbnYZndzKwOCH6u1qx6eVVUH+8oWsX/2s\nBANKnncshTm3j7tryusLEelzKS7pZRuQL9CFB2lqRfgEaOQXuyjoHhqXm6dE\nVEXDc6+4w8Fma0XfdL6+N6OnGcIpKLrCjlXvxOQQFjeXJJ7/v2FgseNXXS3U\nFnSeKxpjAhJBiO8DQvHvnyo7ts+02NAT8Exk4tKQsPcZA+s71r1uzlEaDmaY\nQi2VIssRDaSMiwlEKPhFzq0OvuGr+xoTgTe5tdsfrOx8QgcP9MJllxf7dIFP\nWSkeWKTsqjT9a19oBxvIKqKta6qgmaUxOidba8fedhL4mifxUIEVuCUL4cXd\nVNzBlfdqlJi7hwItfpj7pdc7JUOIohVlzvb6L0/NuOPcVXLh/283HEx0ebLJ\ngiMMzPu3RgL2xd/kCkcM6m6L123v+a0ld/xBj/jXTs0XX4fdf+hPXMQzawnb\nm6GRwyJpbnkyTBo/Kd2oIx3Ks9bpkqofZI5y0nOsqp1IxspYUemn/0oPoXv5\n5PlaI54CQ92I16uDYGoOO9+dfnHRcW67YJmAhsOpNIJdDjZDe4scqrMf2Bcb\nZ6mn6K3QPHOw79Gw0iOliNdskp71++7P71Gawi3tkM3FE8hLaAP4q//wKt1C\n1Rug\r\n=YDtX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD68KjkdUjwSUJTUjkTKm25kG9Fv6BoqwEiLoQJ/OXezAIhAKCwi9aq1pvtG2sk10MqOhbNz1nkiWzlWMi+Gjpiunin"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-main.0_1620389519877_0.5327847736298315"},"_hasShrinkwrap":false},"2.1.5-branch-atomist-npm-audit-main.0":{"name":"@atomist/sdm","version":"2.1.5-branch-atomist-npm-audit-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"6d4158bbcdd622949be056de7726d6a40e04e4fd","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-atomist-npm-audit-main.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-NowCsoNzq0iXf2RXl7CGpiYb/o+Gj3zwp1opzmpM4UfgI+3SbHcsmWAbKpya5yp4q5NH/Bbva6Tg2AyNVanPVg==","shasum":"0a88c1517d2283359007070663607ac89eb8e9c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-atomist-npm-audit-main.0.tgz","fileCount":2426,"unpackedSize":9990742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJglS60CRA9TVsSAnZWagAAPKgP/3s0yWUH+YAOgTmI7d9r\ncrTE1SSzxsAZeHQR6GhyFws12/8twVMZvaIudQnR0SMrwlEbS+UtWULM9V4i\nFqKNBrEIaGXjFy6RxZvgv2flPYTqywgIvYMuOg6xo+7nYQemHsoUoAxuqHdq\nyaD8P3X/l20idcGQkztDcBEnBdRHeYvDEWtQrOpPCovz3jcKm4WQRpoccoQ6\naWPPQWCv67Y+sE5rJ4BFsLjo1A8yqq7HSi4p2w29DbB06Tp9C8n1a1ILoklo\nxDV+q9Fl5V1vDrttzJ5HoVz/vb33va11vVEHoQnCGisr/9ipjyCgnpeuuHxy\nxaYJ8WnLtkyJoi4JAS0gegf2+W2Rr6ASosZXaoFDNHLCosD/o4FnzcgywDep\neuneL3MjiEdhkycumbYfrBsy0dP6DJ2FnJcGLYOejUw5OaDmwdklJHmUFsFZ\ncewCnxaI44kVWenkzpeubrUCC698wpzO/A+Jz4gqrANNCTSfQanGvqXQgfYz\n35mYlCrTew4EH/PybGhH//4PyzNVXK61JQ8OB5YpkT8d0JreLUsUQEwM/c/I\nZ3AzffZ//urpRWb76th5XBcK1UuuoWD3s0HtGFjXlkJXS+EWLhG36B3Q1HLH\n6bfTqpxaz92DjpbU0w53QjjnIU7+kAoYZpMhX6JtPrIwilE9pck54fa3dcEq\noFpG\r\n=zG1b\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD1zq9C3oDb3oHw5Dsice+iR1pDXgwTJlhoE+MBaGfXvAIgaOyBSY1mbiU18kaeu1GhfhbT7AqZ6f8g7GL3IngTvAw="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-atomist-npm-audit-main.0_1620389555783_0.6761921679318612"},"_hasShrinkwrap":false},"2.1.5-branch-atomist-npm-audit-main.1":{"name":"@atomist/sdm","version":"2.1.5-branch-atomist-npm-audit-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"314dd20ef58b3c2634b920e8a5777a6245418366","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-atomist-npm-audit-main.1","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Gm8R0aptLMRYthj/5QTv0+CnfeR2KDTsUmKM0T/gC7UapbvhLBdutuBoEUC+3Dc7czkkhX7jGvAJ17nQXpFoxw==","shasum":"268823f875587ad1a7c6a2db0e65da94fc7bff5f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-atomist-npm-audit-main.1.tgz","fileCount":2426,"unpackedSize":9990742,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgloB9CRA9TVsSAnZWagAAONIP/0sJ386FVhhyJDBoqjMZ\nVgpU0jz/C545KQWCIAW1fXG3Ts/o9OCMQ/UD2qt9d625Neuc+6m/OM8tNw3t\nfkpJHn+TCecNabxae/85gFd2fYK2NuFlbif/3IBnrBwamWEFPfbnSIETNKFI\nFh7YyPvp5cF9WH36B5bZhEd+az+fTI9V69bmIJZys1mVeLJLF3F1Mn+S/MdX\n0GcEA+gxhg8CgbUcRjoj/JU6f8PoKj1AdoLRavTPaOTXnLkrAc1WfM3GAU2c\nwfzIeN3QVw+89reYZdba1Rr5kJ2O1OntQcGCNBfXdaRxRQcyPNLAsASkX+nH\n2Xw1toQCPqeQHvqy+OrG/pWQFm3kP8IEGkVB2U2fCEhPHU6R0CJePwssJ7t9\nR4G/uL/geXtvnWvqAb7JP0fOQM/9RUpDNM/CxGpBw7nmobUfANjd6qAicPja\nPdvVU6qt37+95b/Gm8Yz+k3TMUQjf0Y0yjWcQZtMeP4t0DSsDGwfvGf0ZNUv\nijyCIBaDK7ct+lSw49z9WltYmELS+ZZz07gOSHoUp7pFoLtt08mvJQbuMxpi\np6FM6TAis1d6r76E9LcBXnIXX9oHQAPZt+035TwCZVFfYOik7cLw6Pi/U4s9\noiZ+QTZwdZ19+4ClWYNT2vwSXlHDDDMHXEWMZgQRnkCvWtdHX0bs7eOvRNSM\nB7ZD\r\n=zozb\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCg6AHWVP+jqOaaAIjVYRjBhY+NOaDoMOBZUrpyXtpy+AIhAJiOCbPvnDZILMqJdMHd4FKfn+ucoqPmrX35UnVhluMx"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-atomist-npm-audit-main.1_1620476029077_0.5093135449431729"},"_hasShrinkwrap":false},"2.1.5-main.1":{"name":"@atomist/sdm","version":"2.1.5-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.1.1","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\""},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"14718cd7fee6b135d7da6bce01fedf5290cb86ee","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-main.1","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-wPWrbIFkcvgqQCB9TwSxV2lVXleAOsxPvlBR5VPwUp7Pj/XjaoipYKkV5m2mxc4JU1gcY9k8nzeoBspb+1iYMQ==","shasum":"c065ad9b0c7fca97d8b101dcfb1697d1efe8ef94","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-main.1.tgz","fileCount":2426,"unpackedSize":9990717,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgloDcCRA9TVsSAnZWagAAimMP/0uGskGWynGuTaUG4w8t\nqvUhSf/GqvOCMpGwcNzNPWvXXLd2MvAi0gTVeuRXeEnYuFcWp5xCV/DwoNXf\nu1Vz+Bd/GWw7Xsi62uqqyTH/ULb7HKXJ/S6uTUwXkX1c8AHvJ+7pap/s+2he\nOCsXbSy2ZGjIGJEtAbpavOGEJiMg9J894nFvjWBdrR3A4h9XCfbuUBpd+SAy\nyrSTYXcewFfREUu40W6iSo6VXe536EGvhiUkzcizjaj0i1UvEEut7buARcQL\nXJVxhY2vKr8vDkLF0QemCgJczWdHzhMHlUUUM95Lvtl6C4PP+Je44311fqQZ\nPUpiXM9jfzNvKRdhPM8wmgGuogxMZIDWhWBiCvTEyMr48WRz0L2QS3Y76V3+\nbrgln7sm3e4U4jq5VoWqo5sATAPPuSIjeQjUEAU7yOiYiShJ7EOkvb/W4Lbl\nqCDs2/MPRwunVHZ6REFNxgZ2nKXSsHND3g3aI/K9ZLAJPmp5TN8/SvkNydsr\nw15RPJjOdVRNApU9UpQdcja6f29wwg1nha1hC8V0Q3qdBChC7E8/TO9UVDQm\nekXhQEztB/9R73jBnEi/QTiqYQwQFtKLfPQJWTEDmrb+SCai9hlgVVEM8/0h\nvi8/nXn+D3YLy8rNYe6tWhjTf4yS8Bhqbx+6oqfeAm+BV98q7W05zUdR52co\n+kyA\r\n=3iaa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC87Vf6WVEFtUP4t5i4dzh7wzPjnAVuatrifC0EalUbuQIhAMbZ+JKr5KnnGKniwyCFBcZK81CHDMR0jKReBL7jovgF"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-main.1_1620476123881_0.02063661351072854"},"_hasShrinkwrap":false},"2.1.5-branch-event-signing.4":{"name":"@atomist/sdm","version":"2.1.5-branch-event-signing.4","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"9174e0bd61cb0411c21b26b396370a07987a8d34","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-event-signing.4","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-M5rubGOO9Z87fE3RKDpPOf2oYCUaH6S+kF49xRXmeFGUj+4nObx/7/OTC8VyMjm678fYrd1x+WJwgNHg564YoQ==","shasum":"d5b9dc1624c408a4ef278f8563d0dce6f402bf8d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-event-signing.4.tgz","fileCount":2436,"unpackedSize":10050754,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmOO3CRA9TVsSAnZWagAAhMMP/RIZLBqFuzEy/fighNDe\ndn+SYbu/jLmHw87c60YHMnx3P2i1zlh1lw/ee0Me11hrUWMKo/kZqZymaIJ4\nwnESLsIGRUfArqt5rGs3JrOJO0PwnjFD98ntI9JNwJumk9FtJ1Hy97BUTRMG\n8rYDa8JtnEQJadTZpbs+ybgG/hj5fWolgQZCEbIyenBAdlrkom0NxVKWdnIr\nI55+pg3Nzv/0O0aN+7dfQSMJPVuDQrWV0bvQv3FsiMPWBvilxtmAXELPTvBR\nL3d0svNmK8T0BxbtDHvbprh6V2xhpfqNzxNu0L0I578ysffuK6Jwp52XX71x\n9w6nBbQoH8yK9mvPSVuSOQqWpjzXig97+zhivgk78MjdN2pLhiQquUQdh1rj\nz+b4PeEE1dYIaNNcxvw7CEg24rfBK0YCWSevly67ZexdPAql7xDBybs04wMe\nDj57LlwUD4RQsMrdo9gwNW7r2kfve4GLHaYDiuTzIH6HvHmrQztHbN2jYMOi\nsMTXeauBho9TungKUHGYG1mwmVMhxKtbj8LPSB8q2pkI+JgvvY0yJKL9nWEm\nf9CdeJc9osg3ZiiVKFujI7ibM9MLiwhDPPp19LtiZQiSL+L2HTA7D53mujNS\nNkHqohVs1i/46ex3WGm0UweA+mrlF1tY4pa2YvEK+nS/BI9k9TsIttYtPXHI\nSUSk\r\n=zG9V\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDvO3oixVPWnGmXaK2Lj1BIrzfjrF4DleiNctpE9Kuo4gIgN9ZJTd5j2xkKrb3kaQyi8F0s5XRwJpOlenvn99dzJws="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-event-signing.4_1620632503041_0.21202251209842604"},"_hasShrinkwrap":false},"2.2.0-branch-event-signing.0":{"name":"@atomist/sdm","version":"2.2.0-branch-event-signing.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"e0bb8c65c86402c032153d17de5721aca0fe6923","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-branch-event-signing.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-wSqRBOCzCs47YqOsyY8+ekfdz5JweZ9p5IjjW+YJuJMSWc5+ubaKb74VHnyOQgY3lA8j6wmkkWUUMeOQI556Lw==","shasum":"6611416b237cd8717b46387544fd1a1e005718d6","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-branch-event-signing.0.tgz","fileCount":2436,"unpackedSize":10050925,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmO4uCRA9TVsSAnZWagAAub4P/10C56F6H5gJJe87/6tc\nv5DC4EYRMhuAjrKTRXalKaApZKH/N7BqWpfG8ccgoEiNibaSBh5LXM7GiAbE\nVxaO4ceZEFn+lnlbFROPpLvGY2y3ex+3EyknGetIHnBOkc8+enEdYHPnnX1F\nOB5yOcUzJFLMe+ZmHO6rIsJm8s1vdmeL70jcQvwEhzVNDIcWn9w6sQKxw/0k\nW+ftRN1/Ix/aGZp46e4Y86uZ+34WIsH7DoXmLydKPoX3Rmvzq6yIXrReCOxx\n2IiYyjTCjRaqHb5eYp7B2AivbAiO4EiyS10g9TN9kzElunFj+WBZk6DZE8ni\n6JxRcIkPG12TjUm3ZkG6OQEpmcfY3DA/1Q8GOLHfmrKGyZERBK0PhdvJiHe3\nbyZlkPK6EeEQA76/oI6DYz9izy4WfYp0izT2PTyiSMxqnmg0h7uL1Oxz+brq\n761VLeIy0MjqhSHuA2URzeRYSBO5Db7LHOKxQF42hjpy3xnU23Ybs27uJePZ\nKlVUz+vAYqm6cgrFkuMnSrx7R4tkvGY8p7wOO/9W4drvd9u+x9uTzpky2MhW\n9DWAAxniqV/LvFMmwQBWpS3vJaFM6Pp8u2I89d7mtVo1EoFVahyvXwCCkRw0\ngjw6Uk0n3W6XWrGhJ+UU/hZMKz0WBpihF7FURBW1e3AyYxa9Unva8RNmJAO7\nXDkP\r\n=44Uf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCiUYxyZXGGQcuc//RwjEF9kl3NUnwkl1vfqyyr/sNj+gIhANyu3eC/yfT/bZtaUUmHgLuax8Qg33epo87/0utVzkJY"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-branch-event-signing.0_1620635181843_0.8640195045960433"},"_hasShrinkwrap":false},"2.2.0-main.0":{"name":"@atomist/sdm","version":"2.2.0-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"11a12381bd31b30011aa6ab788c04a7d10abfa65","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-main.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-ztzeWKZE1ZslUTL7VohwCnADj1VxRQA8xnSrs5XKduodzSF2PYeCnNwA0yVjtFGwu8l54fbh6MuQRqdv3LCY3Q==","shasum":"851ef1d7abdef03034a4537ca2d4689eccf26139","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-main.0.tgz","fileCount":2436,"unpackedSize":10198580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmPF1CRA9TVsSAnZWagAADQ0P/jW+M10W+aW9QRg4B/4c\n845bpydVtGxIcaWPw8os8zPWglot5G8T+HbUVrUU06N6fYeJpG3JH0VRd1iB\nBiNNVcZE1AHoKjk9pvTf8C0r3RMtRYd3YQdA6p+sUN2QQo7Xwa8Y0khTRF7p\nxjBy7SlgLRPtCpggYVe2qhE0LY+B8Au6faIIALqj7bFp8niH6bIxubZHWu5r\nyp9ZNsg7ly3wdXt8S2pL1/NEHxAhbczABpp7SyNDv+mDKhol0APqIMaJ14pG\nqeV12jgUnHMELPXue3kPMnucpkN+wZYH+nL0VYqkYpPhAsAvJ2z7xtB8eXNQ\nJ2Xy8HOakW5NeAtlBXgC1DBA+1xIv71tZ8OnN4fJDt09flT/Oq4/n3ZqH+77\nJcyghpsfmQMRGlLl0Rk82B8oih/CjB17JjS8vKRU/cGkOFE5H6GCK9N1M1fO\nR14ipe8m4Lh6fthBT5dvvpj2YcIKozNSlKD8Q/Bkz3OuVzZ760YLW3xcTdp1\nWlVBn2njI1J0DCETa0o8W271pdQGUcFqg27Nz53RTYVoeMGqqr634WKS61Az\nmOoa/HpMGvOfYI3tovpjBOGrIc2PUIPbsnE9R1bCjK4y1/C5/Kisc6sDbgJv\n9r7LZ3KXlLIPL9v3XJMODt4VD/6AgcMnj006nVyhs5TOgWz9CYN45lJuH5H4\nlFku\r\n=+IiY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDAj3ggGUf33jRmVXBVXlbp+YmsGkLbOc2BAgT9J+B9dwIgXBHaIScz2kvWB7d70Rk31ztINiD/Tb+RVSvGx9+wow8="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-main.0_1620636021345_0.42246612516099624"},"_hasShrinkwrap":false},"2.2.0-branch-atomist-npm-audit-main.0":{"name":"@atomist/sdm","version":"2.2.0-branch-atomist-npm-audit-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"d3d361390ef4e8a8167e03ac6f83d1524c7bca01","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-branch-atomist-npm-audit-main.0","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-pWkaPnriVzcmOtb7w5ffFKlD2gh1aiNV7qMUT1tv1YgYr3z/JSFS/1eepVjYIo4mHDaZrVihR8qint5He9QA6w==","shasum":"8d77dc8d7ef48323248ad0f5331c4636417c16ba","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-branch-atomist-npm-audit-main.0.tgz","fileCount":2436,"unpackedSize":10198605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmPIHCRA9TVsSAnZWagAA01QP/3FERammQhDrq/WpfAaC\nt1MO7K8XGinfMQTgJ+/PyOlvd16V7QiDBMJemf3GGtGM148VhvY8xIfJRXMn\nh169IBRQ38VGxSBaLrFmrBi0i4m1ENhx/jAb1doV2+atX+FdcxMnvpvnOzv0\n0OAjuA8S3L729cHIA+VQTUeFEblaMIbBzjd9ofzJ8ekuZ9/0MDIAoxM3wLnU\nYu/ObmDm6bwSdRW2IlfvxYPwH6uLcrAHSpmt463REGuKKA+xkPK3QVSjBPJe\nFhad67VKHtCO5fMW23fJM6yT3P0CjAYZu956Hx0Y0YDN1DgkUHhhJUlpUIKT\nZSsBXG+R7lNJBmt2aSULEMVxAjUK/V/SHhJ8++M+ovGgqWwVdxgk2h0mnHTI\nS7nJLWebzM21p7V76zGJbW76cJj9QGSyzHlo2Rmu3SVNNo/w5h8OiXY3cieH\nqGU2TuvvuUhbhnBzfpvauXoq9YWszyzsQ5WgtLNVqIBJqjLHFbOuYyAMmcbp\nzAbqMJ10srbIfuJiG34tIaKjlgspWbxBK7tf7stWdGrv2QM35egpiwhxYzCP\n6+xXrbeLezlhza5quYI6A+ON8v4zuiA6NfB9JXuwhKyYQLEaLNisSJKEYahD\nTfh6gAszMLrjJHU/oOQCiK8crC2TbUtaiV/CM0+XyQHb8ffKQ7ewDljPxLh/\nOq7d\r\n=ghDA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC9GK49TwUT8cL6MTfA8fTMTAJiWOGAwmlocgwbl0eoggIgLQH9MXbebcO0lZy/vduD0WaILJr8dEFxWyNN37L4Jqs="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-branch-atomist-npm-audit-main.0_1620636166751_0.9828221467112286"},"_hasShrinkwrap":false},"2.2.0-main.1":{"name":"@atomist/sdm","version":"2.2.0-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"ede22b4e0206ebb57fdcf064fcf86344a3f0c3da","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-main.1","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-7jRpJWb9okGcgOJTnZmiMXfXCfjKW6MNd480r+3ZzgKFqQXsVsKK9ajFkUnMxvXn2DBBcoOsDIk9627eZZQSVQ==","shasum":"7024e26dd7045e4fd78adb432c2e242190b9c0c7","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-main.1.tgz","fileCount":2436,"unpackedSize":10198580,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgmPJECRA9TVsSAnZWagAA9FQP/3D1N3qjx8VTa3hzc+YZ\nnbMeXaPsM732sIb64Fl1jyLkBAUZmGX9wsb8ZM6XX/rTKAmJbCa0XFuIvgDs\nOoE2DgBxQume1T36WYgDZKiUAOlV1oSRusgWh0GLcZkPBD8KFlxIYowwVNoi\nmevtpIwiKFkn/grOOOn63HlW/Rw6fgvJKL2J46t1+zwCqW48aQeITpcSg2ds\naaLLkYQLhxeO7WmDisyRL5isefdUViTav8DIGJ2yGyuEF2gkHqgML+/LmL4b\n+Dj0PIVp5N8GM4DXwifiyF3VnM50tIbZ/VWu+BsdBBwBeXGrckYnL5tIzb+a\n6mAMul+TOjWEc6lZgpEkEr2jYoCnuvLg5dZFmEgpDP5bWYyHIf0ZLbFSi97h\nxKQy6DM2381xirKrDsk1sSg6L91cmQ/526bujrWxfRejDoo0t12iVoPnNiLP\nKiPvS4zxhA+L7+6e93bVKtiBPBWtMp1JcK17hKsX8g446B/sIYe0m98gGgmq\nYsfoa89ITMD8nLE/N66dVZpcpMWPljEwxy7PAOGwaTp0wzm2oqYTmbE/hMjW\nkLnmCgRzzLtXhgei2nMwbvZD9j1zZI4itDNIV79yPsQQ/d3QyetQU8aQ4YkM\nihIV05X20j5NHYdrm/yYrpZ3KmEekzkpLhH1dEVydtfV9jIb76F9SO8fkAv3\nizIj\r\n=xMmP\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC44+x1sm2tSUvHYbe542bKwr6I2HUk6QB0NalOMWCY5wIhAIwZN00L9ZDrkBzq80dStPkndzeRNQIaU+xGiMlRmK5a"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-main.1_1620636227844_0.8752455079094539"},"_hasShrinkwrap":false},"2.2.0-branch-atomist-npm-audit-main.1":{"name":"@atomist/sdm","version":"2.2.0-branch-atomist-npm-audit-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"4e40ddf33ce5513adbeeb7751bffb0d260378178","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-branch-atomist-npm-audit-main.1","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-JMaHa9tAyn5uU5EhchRFiY35ENAPadtHqFfM23BWqZZIlurcHievB+xbklGJHi+UiMau/5WTwRMfvanZvUyBlg==","shasum":"bb9155d5c3e6f600d50cb529059372c4721df7fd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-branch-atomist-npm-audit-main.1.tgz","fileCount":2436,"unpackedSize":10198605,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt1khCRA9TVsSAnZWagAA4qsP/jDvXsLm7Pz52t/xnq8J\njTpFz+/aTFw21nl/kzD7m/utMVYpD1PMHaPZaNyhl/sCgX6RvCaDKGktpGNP\nYjFVLvVDWWYuZ05JyujzOVwvcnnLQyilE07+fGhJ6a84JvJ+viFtIuuCPWJQ\nf17W+LftUszdr7hirYMNoyX5PWT6RwLNCXC6UTYElm9Gh7VsPwDHrioIYYTq\nt2gEVQLT+MVzzcgL/mHtAUHT3jXc7i6xtIL/Zw7Vqs9J+BiUSomRAK8uFNB+\nKsCW6SfSHBkwtuO485vQl3fX1PCHxP9+nDgIwHpJI7N4rYVFW5VqeGzwVtkH\nJAI+V34MzUAji/uBODCOo3sdu6sxK+0SiYMdQ0UVPX+7Er8MP61WMmm4ActZ\npjcN0puq4+5Jj6OlXyo3DtbRFySgT7dZ9vWJxU99NcPpNMalFRQiUTgU1h3P\n3eX0txvUt8js3sn8/x7+RCqbYzqlZYoQ19ppcW2hklToUByPgqBjaSFuXOFO\ne8QFNeLud++kTM30lqgOyX12iNW062DUpk7bHD7gUJPofRd3aKiloxjbPE3o\ny7Bf2L4nGXIdS2tWeSNKG2yDuWCitsLeItpl+5Fin0t/l22G2dzwwwnXx89n\n7nyplTEJSTGka/XXr/DrvsYHJGTJG+KvRpJIbDJGklQ7ztwa4FIxJoxi4p5c\nqPrk\r\n=A0eW\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICeYjZSq52hTj4fJRJse3FE05fFn2zxOKYttpf3Sz9sfAiEA+ikiM0LeSy7tw85tzRB7+J71O4lMDzF3W9kExk58jH0="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-branch-atomist-npm-audit-main.1_1622628640897_0.5796857995634928"},"_hasShrinkwrap":false},"2.2.0-main.2":{"name":"@atomist/sdm","version":"2.2.0-main.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"bb3724e5a6765124efbcb5e0fd1208f2d03381df","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-main.2","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-omujUbXhuwhch80xvfjjfpCFbcUFiiPiKWIvMOwN3GgIwsD1P3ToFpBQ55ybQnIkmtjfRDe4O+su68tnJOeipQ==","shasum":"bb71a3ea6ea4b4fe48f5e9da14eab15f6e3a7a33","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-main.2.tgz","fileCount":2436,"unpackedSize":10199199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+nQCRA9TVsSAnZWagAAmHkP/RijbgwVGzvo0JsfAyJ1\n8AwNAYGHnf5TY0olrmsVWQtpEQhEvUGNu2jSQxvovzP6TX+YwHhqvG74AQmw\njuwBmR1mfxuHHa2/5+XebBSzES55XuxPAljyQB9+giChL8E3KasHE8i0BFI3\nJcHP5yxUQpRn7x1IGbEkfogBo3xQ2zOneRvPmjR5hcpvQugobzLmQto5h72U\noB2wvhfyjRsa9TMf3t6cddFROdJmq81YajUNF/fPI9zennHoBJdmZwZSWYvw\n4PkG9ekgJkTzEnhjgazI0tBmgxQMQV/oFGFPplQnW4ODP+DPTAPrL8foabmK\nNGRJUjSPP+6P74+Tj1LvdVIYfDYWIWXZcHyV4DW02XIOy4a63QCYjtNPGLDx\n4JhgdFfdBHTlfMDq+QXOXsCbHtOkmpmI63zHXRAdfx3ZzMpu9kyjK99zTzv+\n760f6Hrr6OkY80OirY517LnAF3YdHRa59UgAzgqrNaWULL9/0+bfvDUBbqUF\n6k6jVQAcShHJ/vv/Cvtf9uX2eLQfNQTx8A9UlMIS1SGFOFMDLBy0HeLNEEBM\nYAG3fZoX+XsjbXWlbu88DYsynaPhumW4sdmAp0StWzWtlpkZRYesVr/moJ9/\niq5Hjb+b1+mK+gBfcoJLnu50NWMPhodo476P3XEle+vgX3KwSFDtkmTGP3Ht\ndCM1\r\n=Ot87\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGaJTAJUctqfutkvqzgxUh3AQNfyQ8mCxxpgHAzAVmhiAiEAufwpykMaxNuP45EJdjGqbOisTT67m+B+4j3qusdkgvM="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-main.2_1622665680369_0.3077770626894829"},"_hasShrinkwrap":false},"2.2.0-main.3":{"name":"@atomist/sdm","version":"2.2.0-main.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"dddfaba64da520b35abfae0bd4581ead0444ef2c","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.2.0-main.3","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-NItfWkX+JffRCHEblD1A17fdBu8mZtC6FEXiAP3y2CCYVnL/Pud/etL1u1AR6b0ix6fpOVzC1SSQdor6ordbbg==","shasum":"daacba1c55ef89322e178f5fdfd08f09ec84c3bd","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.2.0-main.3.tgz","fileCount":2436,"unpackedSize":10199031,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+okCRA9TVsSAnZWagAAQIUP+gJivFqKUQ//2wEYHkxZ\nliYJSKattZvU/CSltw6akFFFjspc6njSdEMf0DzWEaSacFtZSkueAJDxlguq\na8V3mih3gQz5E0OjCog23phvP6yCFX8Zs/dQRlkzr47AOuiT4HE/0RX1ppfy\nCqA+DgbfPxOTVijdjc/WnfNFauP5OsofW7ILUgmXkeKcR3brfUQFaGImdCIe\nJptH1FL5o5BH97afnyX7w+X/zQmkPLlhhff4C+OcwL0tj/xdnLA0AxdqM6fx\nU6LZvZ7lWk7CJbbbwa4/a+PBLZkzVT52UaB88ilMoQVHDQMikcoEaKVCdES3\nv4CZFCP3FgkAzySVTQZnI/s+qldKhGvSfXLq2n43r+noXMUIyFlKuns5m2w3\nYiqwB0AcGmigPpVKmpaD6mCqEp428cQ2f0KNCMReVTPxr7e1+i+bK3GS3tib\nIB2nsbAjA54PFFIVMFKra1OgL2ou5d3GjhMsrWGWlE4pDy7IusESxAss3mKt\nBnxFV7ZIAC6RN2iVDqaGd7H1M6qaIo9imvywkboDVqqEIopM2Kqv8c6Rgzcq\ncyUb1dpaRhtNsokzAxRBxvOQSCZ07ZqEzTH9T/2ihj4DeezuRO1HQSuD89fJ\nVlWgmZiQF3SUh0CP1HPw1f91SwKcwWbH343aBWKL3CTZSJnENNxYBOKDFljF\nSLkr\r\n=U7vK\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCO23sJ5FXppb9gOOnIfngcMslae6KYAKNMpUNnAU5Q/AIhAPu9XiSjWKtXyUhMnVNaFcNZgPL3yaj8A7/dk06CRAa9"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.2.0-main.3_1622665764098_0.5353796890633165"},"_hasShrinkwrap":false},"2.1.5-main.2":{"name":"@atomist/sdm","version":"2.1.5-main.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"0fe741f60a2267d40bbde077daa21ad6570c9e6a","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-main.2","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-zj2sdN65lkXC6/6bzZeBZBgXERYpLV7hvDc56xHoB/3TZPFx0Rax9Ri7Dk0+ll9l9gNyvEkclsMyyphv48Y2rw==","shasum":"f20e9747cb8394d7020b13e88f81710793832989","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-main.2.tgz","fileCount":2436,"unpackedSize":10199199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+wOCRA9TVsSAnZWagAAwxAP/iK4lybECBIZkeYgtIh8\nRXD/1boJ0vWbFRnBjm3MLgbgbBGLxJd9HlFezdNML3p80QMnuKBLXOLDBLGK\nBezs7kA1+BAeuPfdvxCNa9qDmuN612PDTC1oFhzx6GKPeNHqfXYALTY/6SBa\nwpYfRcULwHjzmjOyAvkmrIstxz6y2hBNMebgFlgMmo9KnyR7o3ptJ367+4Gz\nz4c99zbMjRG5bV9aBnMK0IckbNX1eFfZEs7MSy/4knQa8DtEgdHnt0s0mExZ\nhpE9MaRVQBCX1OKnAMq6+lBljGil2uFN/VgnA9VrqdcHwfPCQQHkheV26Ovk\nixLj7I7GwBsIAXc6DJLImeS2c7tzX3dQ6HBpjbqKW9Nil0wLq2DhAGHNHgLA\nxP2TS18fH2mrwest/cK6sZyk0vKRQRf1kbMhucIH5KGxu283nF3dEiinG0W1\nppnhfE1LhtJ3RteUWTKhxe8pt40dlI1DskXFL32Azju8sgurgm0bjWr02/M6\nhtysYQaiwHAPrcQPlvoiOS0uDYYCtz0cBGONN5WDYdqwkQ/OhPcso8FH4TOx\nCKn+HT+WGyTyatfnFg6jniz1mOkdKs0vUThjdSYXnz1gztzhYR1T7PIWThF3\nSOsMBrJ6S4uDk3QrtrV8PkduUekDZURHYySDCR1GhiAPCRT4VcK/wnm5813/\nHTkn\r\n=ZLEc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC5S/hSu4Eg3/9A7WnTeB2cRTC8i+/B8DpDi0jJ1mFWdQIhANITir0aYYvFMOTaQAN60bY82fMXaKnZ4rxLQWF8/Oy/"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-main.2_1622666254444_0.3740645556758124"},"_hasShrinkwrap":false},"2.1.5-branch-atomist-npm-audit-main.2":{"name":"@atomist/sdm","version":"2.1.5-branch-atomist-npm-audit-main.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"669898cba6af39b1f84b0b9d38379b04cc3fc5c2","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-branch-atomist-npm-audit-main.2","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-QyxTob2GzMfAM+yKluhUBmwPKw0vhROiNLYIomO85AqW/QfPadUuU3kEtLKmP4sx1ilTWembZi5syl+Pr1eQpg==","shasum":"63a381cff5dedbddba271f9e5fb44a3f21c2ed97","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-branch-atomist-npm-audit-main.2.tgz","fileCount":2436,"unpackedSize":10199224,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+xaCRA9TVsSAnZWagAAHZMP/2EQybioc+++E9KcZoJL\nzwBGLq3qqFQTkxp4WmmkyiDWaoMzahMjJcy8hYo7AHgsDKlFjl8KrsWaUpm+\n1vBST4NMYRt5SmenogGAp57RwXKv2SSKyO+/zz2u1hA7/sgdi3Ujd3zTV8Or\nCRdxjq0bM9+yGxq8pdGMovmHCZdU7L9tbiZl478kmhmy5pzNrEKL+4RpMPDP\neSP3FIkmbNthkiOTinAMudKy0OC1cI3q8Pp6yLTZtln4qnX8hm3FjxIZUIK+\nQC1fGzkbau3dHivn/Qjfejkz7ORPPB73+S8qnaI8a66XfifhwW09ZZkh7kVB\nD1oQlCOKvw8b51OzD2ZE+gPY9stRwp8IHJbKkfia7Y1/8inkMjpF/4bJGyOM\nsSmUFGxlOXx4RE3JXxTv6TTmHhsvQHipV6V+Jpj+25i37sweAlkS/jiRpoE/\ngV9WY6wVY+7ThNnj88uVA7jN8TySVeQMs8LZtuWJTA01D9OtQxp0i31oCJao\njP8qJ0pMvzxPN+ot4nm7Q95rr97RMaJ227hwYbt9znXgxlgOGIo+wfjPZ5mg\nQkLuxzho4Nq7CAILEXuxt9Y1rSNE0ZJROdqJCF4ljLLCaWNe1piVspvYK1jh\n8gYjqVWQi/R1kWITmysDHb7BgMMD+rHUph24/FVFCK/DD1zpGSwAZGyWGj/u\nkU/B\r\n=IZqS\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBxudmrk7DlNPa1i5EceBeRocZrub1haBPsio+SRKRucAiEA4kfi3cKHwTRekGB3AzteFjMDyGgO5JGlU70M1RulEdY="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-branch-atomist-npm-audit-main.2_1622666330728_0.38964400301163016"},"_hasShrinkwrap":false},"2.1.5-main.3":{"name":"@atomist/sdm","version":"2.1.5-main.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"6a2c529f06d099e45832675e5695c72f87708e32","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-main.3","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-o60OEiu4h9E7lHjuNMlDHbUH9hPJbW3bwmlpqgyKHN1WoPBTsG6QpgdBArTSv3TMqbYYj73BbbJjGVa09SvUTg==","shasum":"db6486affcbb71af1fea3fa86b7988f65b795589","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-main.3.tgz","fileCount":2436,"unpackedSize":10199199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+ycCRA9TVsSAnZWagAArVcQAJa45GNB+n2m87Q01rQE\nxU+2aaxorvE3zPHxpcW7fqPIc8KKc1bA/a/Igg5Xvlh91wAizD+da111eXId\ncTYgn1hwwrQuTdw984NsHv17YSxh8xc03NTrIKxZL3ZyaA/trBxHlbNAqGXk\nbLz3ZHBwwppS5IR6zyIA77d+Kcb+ML7PexjreTvlWiIS4xeOMNK4SPmyZhkn\nzlYOANRyj3bzIzB5UMuNFNbOtn3xt1b9WPYjznTX3ZPBD+L3/Pa2irJ1lpXK\nP0hyoRfp8ujaO5xEROFdFI9rPzek+AKArwj3fVe4N2MBFZhbs1twFxDO5qF8\nYwdcPhahGV8khvvT6ZyMDC9aa1BmyNaGSf+ROGdWcEWzwbX+A0Y9efZHhLPU\nKx9eh33sHrSFOHjJb+KgRglpACEMaJ4wum+YVzJ/rIIv7es8TYPM+eU686Kg\nC1K1m7+ydmEcuQxvqrn8BDEaxnlwJRkvToTJzNq2upipUqty4TEougG2KNk0\nI3GG5aIrE6mvdPY4nvUinZ0EpsWMEcG54E+drfSzFrzStfPH55DgSucHu8R+\nZjQfBKi9bguAo7/JhXtD+Z01bOEWQrUi0MXOiZZtjg+ZJgvvgc1v4Q7ABkWC\n0t62FkmLvIWxNJuhHgGaZD3xbCo0X01d+4/nzKqEcCnTZDMC8MK7mNm8oUnz\nIhDk\r\n=x5T1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQChsHe5eNEO3g3NHsijnghann/Ra8QgBt8EtHtCvUqyegIgSO0Os/3uRfZvLwbB++4lXBqKTNf8wSW7EUC4y3QWGPg="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-main.3_1622666396126_0.9350060445412929"},"_hasShrinkwrap":false},"2.1.5-main.4":{"name":"@atomist/sdm","version":"2.1.5-main.4","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"6228fc42d98df1161d789d478930b0a72aad8e19","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.5-main.4","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-j8W9QXVEEHyTpFR/hS7YkmlOFLT4BxcUAfuVHvQmJNXtLkfGbBe6V+m5Lyq3QVs7BIdKyW8Y1KzsomCeJ+LC/A==","shasum":"2503a4192407097e1498c0946f52ea200afc51ec","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5-main.4.tgz","fileCount":2436,"unpackedSize":10199199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgt+z4CRA9TVsSAnZWagAAjTkP/RBMxRX/P0sQoNCE7HdZ\nr/I7wi8tQEE6g24XuBzq2rVV3JbtIu088SgqTitrgG3IAnKER8NgryUQSnoa\nEcd7BKRUShmQ21UMNvHCbJxEmVKlTfqmqdAXeaXwxIf/CTsysuyR1W3T79Hk\nz2vG7lAIKH3TYiB8RiOeHGoGhFW+sCoveZ0n8DR6OhpUumE6HimZljpeH/iw\ntis9Qm7emqzv++ANthIRdz45VlmnYIkNho0WIY7FaripepmJsI4TeCgg1BE1\noAyEsqnx63kakPukG/CfIKNIyIvIiQUJ4BmO9DVs3wmdn0Ge2KltKnjzdjij\n4MJ5ZUkEaJ2idLf2p4e0NTMF8/LXs8kxAW4SSKRVmMnmiAwAbKQrMHbv0OMF\n44nZbynAIDVTGPEqoy4+kVgIvo+/lZ8oapj0rv3B4NDlG2L1MQprlDcCxKQy\n9+J99iIfFupGGFCCoePYLs7EbhumF+inOFdkR2tzVnq6f1CxJ3tGEbbaMM8i\nGkzkZek3AMjKAi2kSk8UixkUL0h6m+fvnLiY6ApV2AHODRJpHARV8PZd9wuG\nVDlhkfuuSrJGve5c66LeUJXl4kaq0Ih83LtxhMACpwMG2ksxZEndzcWomomT\nZlgCJ+GGIw91+txNx7p3cfVZvMqq9xGDnin4Zm3HZHzT0uzL0oUntmAp+R2Y\nynCU\r\n=0FQa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEYRfJY1eutDCeaj6ju23i/2Yk7CwyNyLwyGN3coSs82AiANjqvofHw6/xaVvo/hvdBLF3ZyiV57V1fvFFZMWyQC6A=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5-main.4_1622666488438_0.9001876943708402"},"_hasShrinkwrap":false},"2.1.5":{"name":"@atomist/sdm","version":"2.1.5","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"_id":"@atomist/sdm@2.1.5","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-Ou4AhqB/akJ3DEO1hFbw4mP9ErG4SbeANorA3pW0prjyLtrLtsFH7o4d+gigJcI8XfEDrXSvFffzmhphawAtOQ==","shasum":"421a679e4815c3a4127840ea5eb9b860021ef61d","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.5.tgz","fileCount":2436,"unpackedSize":10199192,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguKgRCRA9TVsSAnZWagAA4TAP/2wTnOyYKKmlitxVS2vE\nfc3roSK8j7UYChvey0AYfCs99d4pKLQGFkz1nVvoVQ7fpE2CDh5NG+VWO9Sh\n+mitK+hYsgu0Uc81ZlxQX+/xLVZQW+9Yp/LyVxcbTwjaThYBFCqQPnMfgC9/\nNlp4cCLptXfJy3BOOXhDhdhVaFhdBQ5kAY3t6UQIlH9GmQUomiSpZlkBkP2a\ns/i+IOGLqij00+CGSRTW/Fu+Rw9ZOyEOQRMUqnhNaH6albLUKG6vPMkeU+uE\n5yo0buoPSed68ok6tPHFMdJUJ1OlaBL76ogrcDZHoiZYvliI1pO0xJE//8Y4\ntjFJYSNOUH3df42E7GqckBlS5X83fjBaeQJaJIWlgNzXj/YqHc6TXM96WMcm\nZoZ/+zYAsIdXaaQlVe0L4+hVSGou7uyP4oa70nfOe4yoDj4cR06Ugr+g68VP\ns0T38GQeFVAaswm1asIQ9R/c2SJT3CDMtr6v27fUujxUrWiAc25MnqvyDeVA\nC/aFw6sl2stKk4ODeaMZZz0FPOMnYIycJIJwR/vCXqj4cHazomB4zFpYfPLb\nmogaYJQZ4+i4Xm0b3Dannh0CNpxbMUNlDyMQf4eKyqhu8q6kXI0TzhZnAYaB\nXgxHoRwfElVdU1Fco7ZnmevY4LCTcxLr4vYaeRJ5SwzT9Qj4QtJ7vSq1CkoH\nGT7o\r\n=v4CR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAQ8aPbKM6pGt+eulip14Zrvgo7k3QY1/JEzfoAVuFqfAiEAw5RSpaHCQMaxh9f3GNS1psNDXgLYqxtdeXzL6WMpNSI="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.5_1622714385046_0.8009548915482354"},"_hasShrinkwrap":false},"2.1.6-main.0":{"name":"@atomist/sdm","version":"2.1.6-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"f1b62a139cdd89821ccf45f769648f726c6727eb","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.6-main.0","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-gV5FUj2Lmf+UxRGH0Vg9o8zWij1yNtBqtM95yhZX+qrAQcfh7eF6pi8p+zrGRIJsAONhCp7RZiVZ7T4E35za6Q==","shasum":"eb5b2a455c2b72054503aa4a73128f2392214bef","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6-main.0.tgz","fileCount":2436,"unpackedSize":10199199,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguKoPCRA9TVsSAnZWagAAmkwP/2dzIMXkQOpw8K9BjXxh\nEYKD989EoFB0VwQBR1wuf4coDJ2V4ZPkOZGv/L/o2IMGiVhQldx2vymC/9lp\nZY+rfB9iagxLGWnMYKCJ5cmqpIQ8+2/oYJ8BKGip4yIz/M+UNyZ4gZvCkbKo\nrzlMO/+y94uNdp/0Z69icGEDTSQWqkyu1sGIkwNwSxWwgm1B3gSmZaJaITzt\nzpLqWyI7DPuJU+NW+dL6Sg/ZL/+r7YCfWPlnRBM1mxL3gkO2JsrTbCXCTfQE\nQrHs9ybZ65aTd8Shc0/IM+0Lj+Ia7nOF82md/ulGm2Eibw+Dj6HHYvNU1Cu4\nWdKNA9jO+jmvhksJmw6ezvBMYe0A1vSHDfZYhukqtrSpiPrrj2R5aVAdc8YM\nhsd7NJgMzjV0cPknQduJLmTQdgNW1jmJPWmHu+5W0ZnCIaZbD2coftK/dyHh\nw3sW3cXRfUkEzd6OnUyfHOf8OfZKlj7y1wgzd03awzSMTc/6H1C+dmy/9TA8\na796yysPLmmfhiHlDzQdh2LFJoU2HTP1ZEPNlV3Pri/1so2bo5tFFhEUGtWM\n5fWcBqvjH3lOdeg7n/I4lnQe9k35kfyCQkKr8iz4/EucvO2EoA0w490/fwQ8\nulARiCH0bPtfK4R/qVphfmcxxNbtJH0VKeq2tVa8CyWLZUgpQq+KAVcVvCWg\nhyA7\r\n=DXXQ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8DHjyd4C4tvBjqdnCDVy/3HThTlg2LN+2E/yhLyZ2KAIgcGKQruw8oyGxeMKbD0CJBQKCnXxRndf+VARr1ZL0NFk="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6-main.0_1622714895653_0.6367237550796383"},"_hasShrinkwrap":false},"2.1.6-main.1":{"name":"@atomist/sdm","version":"2.1.6-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"fcf5082e32afee1b6e05294069a16bdeed0dd16f","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.6-main.1","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-2hV5p6HGZA0p/NV6Cae3ossCQBCeY2aBAA1RXvZxkByy4/iw1XgUYJzQ659BwEIWz2unnQClHlptBjIx5uq3Nw==","shasum":"63f7d49a4c894bf9637075527f40739c8ffdd719","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6-main.1.tgz","fileCount":2436,"unpackedSize":10199278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguKqPCRA9TVsSAnZWagAAKvwP/j9Fk/khdsptc0MLoyBI\n0EDoKyZiLX7MRDj2DL+3m4jjEn1Rch/mNbH+1OmlPzJsBuz5S5ChI0JHquwI\n8O7/WV0dLCS7tSV993ZMdL+rE344mHGhKYbB/DigMYHFh8PClvhU8ZvETTFA\nhcE7fx8k8K6wdKVDcPOSEmv7AjrBR3A9rmMo/86BoqLMEflPvxvkIicfIGGf\n+lLo5y7P6rvEF8952ghGc1jlsnD6aX75xQT+HMQ81LtbyAQHrgRLj6yo0VwA\nffds7XG5Te1PGycLwZriWFsrXX1V08JJmHapWoUSmyzkmq+ldzbAgSPv+j/b\nIouL6/3M85iyXeHo0ZaXPpAwoXZa/f+tMXw8wdZLUyYyCjsp5FRWxim8p9FM\nEqJmE1zT9J7WOAoRb5oEHycF6C4B0hNXfQsSe57RkrCI58+u94p8hwkIQS/I\nYRa7rQWSfUWQ/OpOoOnwfapJhWeWsnh5yiQkqSnmyDW0BggKUvllXUUozgci\nAXJVp7CcC/WVpCQIG6zpZRl7Dum2Z8UAU8HvJ2Ze6DT+5dZvIhUi2AeXgswx\nRCNK2XaAZCX7YcLH3b3YkRe7YTiB9omAH9FV+v2U2RTL9bTzLTT12WzgqHrv\nBb2gWLw01WxAZmkQlRQlaZjUZ2TuR8rwDGAbNUjT2RNUkz3UAjCk1bmGAPm0\nqU1K\r\n=iL46\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEC1QS5oeejNWMF9GuRkPVUGw956aSe4Bm7EuntWXMZBAiEAr0oFAv0jI7FITvFxRCXDjkY4HpQbLT4dVQDAekDkk4o="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6-main.1_1622715023508_0.9836836887828855"},"_hasShrinkwrap":false},"2.1.6-main.2":{"name":"@atomist/sdm","version":"2.1.6-main.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"5129196303105eff38c02925a60cbef1ac464bf0","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.6-main.2","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-kH9+PwU4ftutMjboW5FGsm/Epx1j3/VhA6H8guVpr4nae9zdfeMBnIcQMynAf1vDiW6kX8XAJytc/9NoYwak7w==","shasum":"22b082ebcc03898bc6f9e97dcd5590b84435ed7f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6-main.2.tgz","fileCount":2436,"unpackedSize":10199463,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTQ/CRA9TVsSAnZWagAA5ZcP/jytXCOpiWnes69IBWfw\nK0TpudGxJexB2BYrjSmznQCirOnF31flMdvzMiASJ5EfyDTePFxb9r5pscS7\n+FxzedyC89C4u6c2khrqviqVeGIQXbZhqIGbgteBzx/yg6QUsyYO3gqa46YN\ncZnIDwt0oblCm2Y78KGhTRvoys77VFA0lx9RFUoIMX++KtcDUkhIAgwq9TAT\nmlaLzxds4ObL48QtdxqDKH3RXj2OLcSq1dN+yHptv3fCebvPQCKAqs4Sa75y\nQgBZWrQi6Iojd0zvZquRRmyAqxW7/7Ps5KaaJVRDHq9UfNzkzoZpwTraKsck\njwnq8nCaSEzC3yfigLOB+z01ap7rCTAnB4NwHyEjOBsW0hrW7srK6qf7UNd2\nOFhmyS8EefH+rJImRgZMz3vKepRf2kYM+RVwQc5qupdrJc7/zBcrF5h2v3VJ\n2yDTSwT09PHj/CE7eviyXv3eIvBWEk7mjf/aZgQHGkJIwxSW06u9l8r+L0lZ\nM+7WhqSnx6OAtTo0jBl7NspBR48ym1Nm9J2H0sAUqx8h1019WkKOBWb8oKF8\naTjN9YbpcPXfsgWfzK9vvFoZPYk9KKch945h46BOOYXopfkVNXhoNLCFlGma\no4wEPQiJybCJONjVovII//hdl14Q1kvwcCOLszNza+mok8bQqkgn3wstXtWF\nwl05\r\n=7Bk/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIAe21H30/WUT/gFXxoEYK5ykYEBAQCCQi5rHWuR3tPhFAiBt5dDyQuNAdS2/phLF2pRYQhXNzxOvXcIATQ33ILZXTA=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6-main.2_1622750270998_0.005084010317323084"},"_hasShrinkwrap":false},"2.1.6-main.3":{"name":"@atomist/sdm","version":"2.1.6-main.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"aac1d8ce90078068b0c8f028fdb14f96081975df","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.6-main.3","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-lLS8pdpeFoWGZI8yhki7iSEBgHkeINWVvUqYeo+E2TeQ3NTPlnD8A1LCuaMh4vdMkhRF8xQxTNJKwO8yjuF2/g==","shasum":"8c7e3e71515aa2ab1d50d2ca18c8fa907ed6eb4b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6-main.3.tgz","fileCount":2436,"unpackedSize":10199306,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTSVCRA9TVsSAnZWagAAm5wP/0FegcAfv01ViaTl09+b\n6Bdvbgr9x+6Wr9VUq+77ZIH3niu22oalFXaZQnqgBERb5g9gi2deTA1sXgqk\ndUCIO3BPD4nbAUHWbNI24445ruZ9j5FZqyDvV1VdXa9f2z1oiO1zCFXgVccB\n17aHhU2O6h8T3udLCiqdZB7CieVfLUfauJdIRvBpJNZimvS57KqmukXdT7yJ\nLI7qJr2XcY9miTdKFqLUSUHUfBQViHXNEsVdcCSiuPRM2sjbXhJvzPFlA0m3\nHEDO+uGFH83iAJ1hRPPnjYT4ddUdE8phMcAOjaKaplHG+LnPOHjIya6Mprsr\nq1XowFtfJVR/a0Ytp14hgCB08tsEI+96OMvAfFn4EUMV/GoEyXKQppMn2zsy\nApHiTDh4F4o/5DfvP8HWCqJLsFcIiTJawrpHRsVjpgciNm3ZQR18LFXEXB1V\nsfV8wS9YeqoPnl1GAMMaElhq4ro/bGUU5s8pF1WN9LzKRgE1zMyzzHNU/jL9\nMDkwguwkNo5psOt1jv3oriAVKPUWw2z/2t0dbfbMWrq2J1TO0L+ruUtzT7Ql\nu5utrlqKgAfOxGdW0p/fpGbiF9EJmz1Jm4Ijn9T6Zbsw1NYtVN8z/6maFq0V\nEuGZxlPQEkIIvPkSHZ8E7csIaqk6Ft6IWN+anhvcBQeAUCjOyKVSrL+TN1lK\nCODM\r\n=SzI4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHQ64n64PQqHj5SV/17/fSaoBVJa2S9aCFCn9CyrFhUkAiBUSZM34K+HrRlCBwXOuqYASTteDH51/ScGfOeXCik90g=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6-main.3_1622750357010_0.8211698686595341"},"_hasShrinkwrap":false},"2.1.6-branch-atomist-npm-update-main.0":{"name":"@atomist/sdm","version":"2.1.6-branch-atomist-npm-update-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","gitHead":"5129196303105eff38c02925a60cbef1ac464bf0","_id":"@atomist/sdm@2.1.6-branch-atomist-npm-update-main.0","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-N9pRqNkTQeN7F1X+z6ZcyTkHr+tZ3Ip3BFW3xWZ5otSw82z56ISyf9aT+nrPuiXLlimBW/lbfcOqaUZA5boIjw==","shasum":"0487823683c287c903e459c2a7b7e794bf421b30","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6-branch-atomist-npm-update-main.0.tgz","fileCount":2436,"unpackedSize":10199489,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTTcCRA9TVsSAnZWagAAZuUQAKBeb+Fv0oLFUBLNwGH4\nRWrqmZ5W2NAyOZa21YiIFYlOseoLJS3ks+wmlEJ9YxMFYzmeTHUXpdfRxzxY\nIDD8YD6qnGBliLuW6ZNRGTAuSws70JJIVmfFiMMcTu9TyqP0cKQeb7iOj0jX\nSvOoAuoJr0eqFVTxv6niK3TtNa+0mjtX0/tKwVezqgjB/c85zGOzko8F9TQQ\nsR3DmG8J+jlI+Es7+4eOIyBm+KrG4lNLYsvGI2OH+k6I6iLTFzAONjCgOEFv\n84hVBjJ5tQd/0m6wuF4y6F4gx+Y0IhSb6BGPSM35VkZx4apD5tbHLpjjZeK1\nmVkOIYLDp/Ltu7D5VM+CAan9BdyCwSwBMTnssBfeoJPn73potSkfVRWVOiHb\nUcy+oF6tze0MBQUBNdeKxQxExLUDTHCto0zzJGB/KThaOW/14pFQyYsfNpnQ\nOK8p3AiwjQ2lzJgJKqKEtDF1F+xQ1VYTI8PtVpZruMd+u8RpkNu3+2j5iQ1U\nM+CPQpHEYO3NO5o8CuFc95wzVDiZjDIUjYaY5vfbSHdKkdESKGFJu/ufpRfv\nt2ItIMRP+/oY4r/6w8DDrLV93DGuKXobq+aGwfvof7J9EWkLz0UU9YFavN4b\ntZ8L4DjI5ZiiuPiWDTFZYyCRl98gadpAemn9fGOAxUtXZHCqNjucZ1+I4RTG\n8CWO\r\n=PwBu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHLsL6e4RdPvba85xMdtgY5EjCa3ji7tha8Rmcb+vluHAiBSi/z5aFVuOpbYtZ9f6206TTtvg+uz08iRi096iZBf1g=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6-branch-atomist-npm-update-main.0_1622750428162_0.029533786956117858"},"_hasShrinkwrap":false},"2.1.6":{"name":"@atomist/sdm","version":"2.1.6","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"_id":"@atomist/sdm@2.1.6","_nodeVersion":"14.16.1","_npmVersion":"6.14.12","dist":{"integrity":"sha512-KnTvniUVzS3PYvZsyAxVOcwOqiA9wVrkIOV8Hv/KL7pNGqILfqngn0fVjLw5cTd8Ku7Dc3cxSKVZSEcxJK3jCA==","shasum":"b5db8d2f09e1734e1e4a913c88d17f9c7c57ec88","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.6.tgz","fileCount":2436,"unpackedSize":10199456,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTUUCRA9TVsSAnZWagAAk8UP/R1x04lahpU1IyKu5EJM\n/7r7684Vq254l/xGLR1ebs72U/7VcuiLGx3cnWW/8O5MKCqBVTynGSJmhWLJ\ncjIwul+8to3HfxaUEbdEL2nKiJF66R9Ffqaf681S7/9VeRvP66rtB0nDOcwz\nOAZog1lfvOjHxfY3snpwP2JK9uHs9AFbJdwBPmEe/kU2sksUo6vcIJANcub9\nSSM/htzdnZmWXPwJIPIQ0JjW7/fV4dyBXXpuHi1DCrj9t4RYiM23tPFEW4h6\nddhHN0Sg/HHbqvjcY3borFexAjZvTZAwRG2N0qnvP4hPCb1HkcHgTPOJIlDK\nyfzVIC1dj9hT5TtaifeU39mlyzWrLRNBUzMPdFj0ZQjCupiCd7SvJ8fp+BeZ\nFs154lBCE9AutcEa3/EFyRi8F/ls76Hl6wXfLumQ0VgN3tOHEN9qaYW3Rx05\n8QtlxfKtDnCCn6dz3tFvXlRlEfec6+j0Iwy4MEuU5deqrPPlrtFvlcQT7lYS\nxomnq7s/EbRLeoUvFoAk6VvQnEHyT8/lIyYDd2s4JPpUm/E/WMWSbJRIKBFg\nrS4AMyvGOnOtSWYKK1UJpPEf4YV44YFDIYOQ0n1qzboWsq9aTo26dTti2Vk3\nBgjDYyrwtAueqdybgLlu58iZum3UZL5YtVBkhwbdi2TtjsFQ6HNL1EhvY6AV\nuJu/\r\n=I2Zc\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH9BM5J2DjcH0bhoOiRj2evD7rRHAr57TC5zTgY7hsRsAiEAhhP4H3tAI9OUc3STQWqZgyKIy0l8K7mChXB329Zm904="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.6_1622750483706_0.8110765377079292"},"_hasShrinkwrap":false},"2.1.7-main.0":{"name":"@atomist/sdm","version":"2.1.7-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"e2fb0f1c899da90a8453b8dbc0d96d2c2ae0f448","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.0","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-qpYMIzi2iSssIKrWhUyQrJXGRwy8xavWTQSugSlBdJqEsT4TeXZB0x252AsujRKz1PHoseoca5mkdo7guzPkrA==","shasum":"d4c3f35be2438fd067721bd6c3f30ba417a3a219","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.0.tgz","fileCount":2436,"unpackedSize":10199463,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTbnCRA9TVsSAnZWagAAJFgP/07vAdf0lGOWfIGAmBxT\nP+r7nNQ9FHtQiHcbne2jz3Dsh1qTeyZw0RpsSzGbP7A92JWTnG/+UNOn5OvX\n0No6qnOK5LK5gKNuQK1jcoIPG0+u3usRhVyZ7CS9BB6LnVftCKmkeXhnxoK2\no0PeSbKGscyLhp7fkCRYSKlbs5RZjcTdPgeVMzDi/39XEM0IyvSF9s89V0i+\nUHb6e53m4oOuTtpetraXbppaMnyZHAgqbSDFganZy+0L5V0bnoGyGZxjopOC\nQgdSpdchPpzCcvqFl9uAHhFE592fiVGHQ3x4eOvGSiPDs9sXxMYsaSdi/NyE\n7PP949TbK8dz7G+xE3czRx1DmgyBcW9pvpGYMh0qVrmPY7+j8WXFX26chN8S\nJuxRevHzY2BZO0cU7oDrqydGEpRxKrdO/SJi6bAwAh+DIQpMF82PI63R6SeQ\n5MnRUsuXz5NdgvxYu9JTy+Gduhss/LI2K3JCVT39GvkvKPMvG7SXic+XQ588\nlVmA1xm3GGZC/Q9hkRT6Ge7Ge/JvqgtjE2yahEiAAxQVemW3l+EjNX0Tsl96\nKZx6v6fSSuBvFAhHlwm3c/2QLnfqF86gawb3Wqbp6Bh3CgdGiC/+g05ElP20\n6z09PH2wsuDyabfY4AQfxWx1dSAJn8OvND5iYubvf34wcpJZ3Uk+YmNqC43s\ndsXR\r\n=fMIy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDKOECTdBoHvzuG2hzDXdnpM/Ul+GZd9Mh1WL5g2kJVrQIgchZDhlihkVPucwm2trf8BFPab7/EZkQ/WFk15z/Aq9w="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.0_1622750951003_0.9251501066883696"},"_hasShrinkwrap":false},"2.1.7-main.1":{"name":"@atomist/sdm","version":"2.1.7-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"4cd831aae4feb05c3558a2186e2170ce936cdd5f","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.1","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-cte3X04mU6zuxcWOffI+gwriuoxJ/Us7JQ9paAW3pT5HCdKQm8lk6DQjUtvwoAHo6zFic5u/nUSowQWfKxrKPA==","shasum":"81b44f7d2125ff2303cf8a3b6492cb2902b7ed75","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.1.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJguTc1CRA9TVsSAnZWagAA0+8P/14SMjJulEMv4WhVejxv\nABpgq+kDD316f0/Om4hqxVE94ZPsnwiLqGtrZuByXHF6lCw9qmV1FAad+mgA\nVLNxjQDM27xiJPDzvFTL90E/+fZgjfLSgIkxcxbq+Vxx6G4ma+46yyiTSwB2\n6BdFzy2G8mgebnSgg/62j5CgCQifWwmIIBC/xqRXCBT7yuK9G2JjbqN7q/kz\nwkjqfqqphNvoVDiUWaAfc7u/xanXZEDmoirBg/dHyVZPuQ5PlFZTLMXsas0s\ntJcilFAQXxEAdlratNjOqR3bRvL81cvWysKl38Wl29NfyB1SwJKrUh88kTN2\nrUCaqQcXdQ4DigLV5aiMN9NAniMeehO4wBleeWZTSn8TZlKYbcLADuHF1mRi\nVEkclIBPDuDOMZLikgYWCM5QRvuhRDcqZJe0roqy2bDfisVX13YxRpc2aClg\nGEA59HxXJHYnwTGhJ686EdELqajGneKsU2cwRROnflRs+Od8ok1N4Y/Ts0qa\nwvignq/PUmBRrcFpgomqiKPuioyFsPBKTo/qg1zPWgmw8I3mRDnUSwFW5SSK\ngbzRidYc58JoB39mjhdlKYQaxteTPk7GD2/IRQb5wNRZvScLAzXSPa9nYzfG\nUkJUOWzzyuO93MnKMRY5k/hDfzCi/yPkcaKvtdTG13mMWcAejfrya7js5+at\nsFn/\r\n=1nMd\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCOeqZzGy9ypE2G7uOgo3nkrdcfRj9zjywkEQDdTEkScwIgHm9t7/nx5dY16+etk+r99qMszPAJi2ftoHOc++m0GTM="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.1_1622751029077_0.17348502934563803"},"_hasShrinkwrap":false},"2.1.7-branch-atomist-npm-audit-main.0":{"name":"@atomist/sdm","version":"2.1.7-branch-atomist-npm-audit-main.0","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"242f9f0bd74e2c778b41397767f6431d6074d011","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-branch-atomist-npm-audit-main.0","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-QIciLbKCtnYozSO9zHY6HPOqHpLfJtfHdCJ6dgHK6mjX37nKJH+xk66tPXsA0ZnTRveQT8QAtsQLE+JMfVigpQ==","shasum":"f550fd7e3cb3a8e4431f96198d6cef7ce15ad813","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-branch-atomist-npm-audit-main.0.tgz","fileCount":2436,"unpackedSize":10199567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwJPCCRA9TVsSAnZWagAAAp0P/1LXq8Qy/KBDyKO1C806\nar4pibzVlrohLhG9iSepY6OSOAYmyPhYnnG0p7k2piz09qzk7SnP0Mt6B9nJ\nGy7hoqE0/IUuyIWLsxiCNOPQaCHZig3YFnEwKtoiTn2uVMg+UZWqcvN0l8sn\nMTwxYkjSralg4kh5Nq81KJyjHcqy1W62Kgfo3yUPW/7tkm/5ABBbmMUeqsEE\nZ2xIaNtMZfeZbPpBjqaHOh7Rmnq2/Z5E8ggJdKgAAJPCrYP+F+U0FjYvNa1i\nUvV/MJUB7XFZjwiPGJ7mjykh3pI8ryRpGW7ui9DNNRcXYcN7CQMea9jlSB25\nxZWnG5gYngrCudvDqO6XHyJ60ixK/Cep7uQpITKh158XKary/SjbnzpcV6sr\ndD2kmckek6e+USc2VBNXdo60auEdXB9NnPxptYm4gG+7nZCWISGBmPHm3jcP\nWmlQNZId+++HWgVzn+WAwgAABqtB/Z3KVEEFQoxkvPK3ekWwPZDorr04Hw0K\nnwhIXeJo+In8aa1iYay+/weYcoEHqlPX2kc0nQp7X+fbnR1nYW1UBCl9i9Vn\nyU6YbTaL7HPP1hGnS+1rLnJdmpuaDU8fih+K07hqMFbpSZW8zVYXEaXCy3MN\n04UahKykXB4Nz1BBRzg+pdMrfSRx0O4P3iGazCQhdA0BFgWiAib06zN+EkfF\nEnG4\r\n=yf9I\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICwcTOem22SRaCy/XAC6ZFNQlTVBIkH9HbFZJJT1LNuwAiEAhiCtG8wJz3/bZ69kdPY28wQ7svGRKSlq02vJWSZw97M="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-branch-atomist-npm-audit-main.0_1623233474032_0.24479354488640626"},"_hasShrinkwrap":false},"2.1.7-main.3":{"name":"@atomist/sdm","version":"2.1.7-main.3","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"09bd36673c41880029dd6321b2c3f8884e30ee3c","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.3","_nodeVersion":"14.17.0","_npmVersion":"6.14.13","dist":{"integrity":"sha512-kXA7y12g76gnOuHG8nsDbAeaa7zSE+tKGCq6uBFq3l7GspnowqWV8cFrzkOeH8y8Tq0IfPxyiq9ItVyRfSgMDw==","shasum":"f5bc80c15e83dbc0725d0258ce48f7ab5747251f","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.3.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJgwJQ0CRA9TVsSAnZWagAAvioP/ioVhTbmQVZp2038lOVQ\nP1gGQEfAzQ7rZhtHY2XKpd3YaU4AhF0VOmRZayg8WB2oxy9IT+AIG2KQMRKy\ng46nj3oQR0d338bpIJi+z3hIyxAVKjLzwTkXaJzsY6JiNbsVGMjOekRVSx1C\ngXEukf0GrZaSwNCLlxy9KheC+NkbUNSbwF9HCdA58ZWCtwwwbDrv2R6n4126\nq/BshWeslzvTc5pw8kuwJaYKrMixxq8L9TFDAcl0iyHnoyz7ob8vd3yq1FUI\nPNR6rMLAHIQ9BuDj4ewTaZgKVpZCgHRYIYnqSmIMdoyJD1CBLk3yDifJZCck\n7liJSWSV/4IQJwZGtBNyMPj/GkPOUzsLFww1d74yOXXKt4c7NHjq5Wc+VV2K\ntXcVDNFZ3miiXgJVt+tebcnJdJwwNnFnPN654dogtn6xdM3+Z0p2C6NY/fAj\nDVI3jIFKweth2SYVu5vxXohTujlqx1V6prhrkNQPpvwjBHNjGtb8CP5lmh4m\n6P8ZA4ZX6kaYDW/GkYTm/ISc7C/lFMKN6z+Cxyh08+rCELZ/d1UXUGCQeNiM\n1YAon4U7p0/w4F+9CsBqMeA6iYY+mhj+fA/mmaLPPw43UKHNJ0hVISMLn+gh\n/ifGfRjpu1xkXIzh9pmiRCzoH983IRCcSEwFs6Yl5a429ejZLXLP8KzOW40x\nlR0q\r\n=Jg+t\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDm9ZIw9x2CI9ODZqX9OCcZwS8Hn3Q3MvOByp3uw95TFAIhALHWRGUOgIBVC2jt88Px5cEkpfVaJQOu0tg1h9STRGZE"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.3_1623233588105_0.22632161298951425"},"_hasShrinkwrap":false},"2.1.7-branch-atomist-npm-audit-main.1":{"name":"@atomist/sdm","version":"2.1.7-branch-atomist-npm-audit-main.1","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"8d3fe60ed98b11ca0842693d9fcd42a12b28091a","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-branch-atomist-npm-audit-main.1","_nodeVersion":"14.17.1","_npmVersion":"6.14.13","dist":{"integrity":"sha512-Oi9cpevA65kFiQOQ2DteEnndevkaFJ5M9Cy/EdBsQMetIJM3KiZErt/eHUUgi0AY+XoefrBEmh5pudxejgPqCA==","shasum":"25f2a8dcd19f55dc9925f6a32abd4218db6a50d4","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-branch-atomist-npm-audit-main.1.tgz","fileCount":2436,"unpackedSize":10199567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0bcYCRA9TVsSAnZWagAAcVgQAJ7hoEiraJxlwfIWAI97\n22216pjO36v9WPgibOc/UTjnK/iuLvo1KhDnq0WeqGS6n2N4N0SNjNpQNTRC\noZVAQCTtVnaC0W1cAPnrx7E2IYh/wRQJNP3ISEVX//eHS2ZWRmIdMOSTfHmA\nhBZOxhsaXaoEUMMo3Dw2HRy2F0BUu/8MTygk6Bitwxc1naO9mlyLI7yLkYs/\nW5qiJFru808uFleCej3Syjm04WFK6aQMZLRMQjKgY40DdLoirwIySwgY53IV\nxGWU4q6af7IaEGnY7h35+IvRbQdY1dvLvVPr6FS14LLdag2zeK5FOsRInEOb\nfLqoMDw09A4/Y+FKBON+FWLYXa75E/1JcvAArka1zrRyVB3WR4ad+J5Gd/xl\nP6Ldl3EjwzNkyeLMtqAGuMWV8VnpWNSBTfs1lO8CqEFgqkAaAoWjmZCtK4s1\nbACDPDQeSe4LqJZau/dWWXKKCWx5pLdqdeWvaMAseZK9zj9WvzrKJwItgLoH\n5D6gT8AcUF04am05F9yclViaRF00d/RHmdKOyRANe7p8i52ZclMXyg1v8qEJ\nsdMcWw1qIhAXYDp+7MHZw5lBFeLJ6c5timClS1LilF/jYgkM9xc/rW2W5GK/\nW9FU8CIFwUYTh2Oehj09+Hb36oSiG597lXRQHF1pXrxU1aj+HYQO1Xa2CDH+\n3ZY+\r\n=qsXR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF/B/gTImKLyn5Ph5t3I5EU9Cmlrn6SlFpKF3947poNtAiA7XWyIhS5kEKppUgwKNpijYSjqzC2LMmDb0/nNbSDR+w=="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-branch-atomist-npm-audit-main.1_1624356631507_0.37600494143359264"},"_hasShrinkwrap":false},"2.1.7-main.4":{"name":"@atomist/sdm","version":"2.1.7-main.4","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"5c97764a2b946b20207ad2bb42c997a6038597c3","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.4","_nodeVersion":"14.17.1","_npmVersion":"6.14.13","dist":{"integrity":"sha512-TH7h/g37NCacykbN1k6D0A8honpr05kJq1kXw3BR63trN3EarVDwMZGmUxfn1Dv2+HHlAKdG81LnkDh0Hq7Urw==","shasum":"0bd0e80605bfca3e32de8f2712b2089073a14629","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.4.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0bdcCRA9TVsSAnZWagAAYKgP/Am9IFC7byh2EW3qRh/G\nWYK8sC6KoneeZXJ+x6nknFW6+qSBvfGxQT0AMoKrjwH5cTt/BZxYhbnOUoMY\nVU/vvRwks34zWyRJe8OkkF0dvE9VBJZaHXsRsSdlWNAeTQ17guZZmkIypmbV\n3eLo03RNexk+xJuKdu7XZCQotYGj2JOeePygtQq3l0qVg9CBh3Jxi0LDlrsu\nVc3K1P8wQitGCKj25g8sDvdzp0v/crOfCxrG3nEJKMLYNN2oRxiN32cBmZil\nMkN5Md/bn4mDyDTM/hPXf7Qo5Lx3lQaKSYzH2qsc4DhsbM3OrWyiqjV99vw9\nxQ1wM753O0OlS65GviPDa5UTilUTIhe49XrIn9h8b1LN2L1iZA+RdGN5PICR\nHLfCx29nQ/TtWkq3wlS02L9+skeep/ueycN/lvalNGl6MnFgnMr5+5ruk1K9\n7tmE1jge7039vGyPnVoslYRtbASIj/PFHIYyIgkRLqc7Urt8EKOqYtsk24yJ\ntu2R6pOTvRrftAwYwkNS7WyjCnfOUR/mm+8j/zeHSPr+GIhqMwoyrM2waEey\nqG92SCYs3WlRzdVawP2OdIsAfs1/dD7jhraKyLEwcBEwIfTgwaTk7HSY15sL\n7iluxlh/51/vqHwx9ByqrEIwFvf3XUX06JYQFZHVWb6dctW3gLlFoyWacTyk\nTvXm\r\n=wLRB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICA9hg5mkYQapB/SrHJmi70SSNDAq48DKTrSxvkm7/ZWAiEA83DUkJvy+aNWgOsTdnj0e6D+b6xjwszl6TMHo20fJKY="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.4_1624356699598_0.21580478086612165"},"_hasShrinkwrap":false},"2.1.7-main.5":{"name":"@atomist/sdm","version":"2.1.7-main.5","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"5d5739d1fc413a7a499eeaebf7292e537227585e","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.5","_nodeVersion":"14.17.1","_npmVersion":"6.14.13","dist":{"integrity":"sha512-hEE/MwVtnJYA1wqoyOSuZKrMutAOAVhnrzYCVZpklUFV0MWsWA5/bwdT8kNtu+Sz/bJzm/K75ZJdEDVRftGCAg==","shasum":"c7858680a8a3188131c8fba3adfc4d65bc4e2415","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.5.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJg0bfQCRA9TVsSAnZWagAA0AAQAJK+6ivVT8eHXYLtGEP5\nAvuPUHNtKAtCLtACgJgdAvU/oswroFGs+tez0AYGlHkn1MqsTfkws/Dx6EDT\nCoQkAS68530jX1jlWMPh0h25E+h3UDSRF81RURtBpBCmjcwD7bsk7pdQpLFp\nNmha7eOrwtZL817Y7InjK3q7S/wHP1NXsdqOwavFUni6BeI8M0fi3eJ2nSMj\nIY2ClQJJg+13H5zYT8X3xj/bFaocqRi/olHRFYm1Ljv8nTmvIvTzlvHsWy5i\nnCvlqiLQprr0Z9uzZkW0G5Jgyf05cSbDxAMVRfviPLEFfkdNLJNP4oYLwOc6\nQM8vXZ84M5eF1WFBL9eVwh3ZQXIyMXmATH0kkZda5PriwUPH/KTUJUvAvIEp\nZzWmXZ9aseRk0YAIJWMKDEMUoDYLBn+wpHe4LWLQaj7oVLZHrkPsAZFT0gjY\nfXGBPv1dVa5SBh8MqMEgySSXzeBSmHPCC5aGtu4UV7sJWm0ACxf35AzrPNs6\nAURTOkLgOh1BnVndTZXnrsOri6A1KwkxsYtr8+supzvPaxmvgjd/XiGtCmD9\nhRY2TRCbhM4Z6wVtIeRtQdddli9ujZ8lSYYBAw8xyZ+SsK0bh3o4E/sZZBn3\njcviPvKZWzyvJeg0woLkFtU9sf1aicOqAAVEf2khLInp+5Q2UxeOY/Teuv8K\naYOg\r\n=GI3v\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAxz+qDviIa71Cb5I4PqVW8kmID/n0BygHQ5F+q5djvyAiEA+OQxv+EtAjI5UPdSiHEANGoLGAXYgZ+cUrDHSE/NgCM="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.5_1624356816086_0.8048135998781405"},"_hasShrinkwrap":false},"2.1.7-branch-atomist-npm-audit-main.2":{"name":"@atomist/sdm","version":"2.1.7-branch-atomist-npm-audit-main.2","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"b50381157093a570caa0d90f96d2553d6a93b6b4","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-branch-atomist-npm-audit-main.2","_nodeVersion":"14.17.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-ajZwuOXUw/QgEQd6uOy7K2V3bIHkcYG9dYQgbiEU32BNGm8g7iBufifKop4lRudypp4g7Qm4pxLImsG2LsUx9w==","shasum":"41970ce0929c960fcd0518ec2cdae3560f38cf6b","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-branch-atomist-npm-audit-main.2.tgz","fileCount":2436,"unpackedSize":10199567,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCkucCRA9TVsSAnZWagAA5IkP/3LKRm3vukzLwL9bD4vH\n1Jw2tiSKMaDmYdxIgbWDvTwEVjHbs9RTql05mh93OmCsGPsccM+Fq9eNAoxm\n0XDXY6105Av+oV4GId9fSGYeGQV6U7dItL8KAcMuLI6/xMBY2Nl7Cdb+vP1X\nmx7+IUEX5dkjCo1kylUnOzD3NiL64XMMy4Y18WVa80cM/AxCAzW/itkKLMNC\nVMs3WVz8+5SS8WAJfbD1uIbrUIdoykfS4qwt198A84BM3vsXoldUHjHrpyAv\n1MbMIdLaJljeXZT4jj9g0MffbMmH3+nMJrsmBUim2fwInHIsapEhDHvJMfiG\n3SWfb4yNTM+gIA17m5esRARNhLOsviyLHNNxc9OwRmddkfhluWmnzbQ2hrJR\no5OGAZmGjXdWQ+v0m478J+vIpaNQ8kM4Mfvh8vio+yw0QWymDD8VU6E0mXO/\nTXNznkUocUBE05wAOpxM0EcgnMYe8IWLOOcSfEfOTpqwEZGlW7jbfjEwwPN9\nvzNPHI8TDoS23dT7eSmFihPdKJMAI8/7q2q+IIDLI0H0cHSI1y6tmtchIl0J\nrlVe9B0L2SZsSpW/8iKBJwOTZC5MpBHeAYVKgl3Qf8K8Pvr4w1CD+VzTQ9Fj\npjTWMpYELbaI4H2r9DSrD+fVQx+lwQ3jF7yU5bSC/6zfH5XM2XAG5A5ZoEEE\n7LLK\r\n=Ga0o\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCkH7Nc0QPGEV5chtjRJHV6MudpUmZ19+7nNpq9RGpY7QIgCoezJP93ftRErmG/ti9VrJmU9vDD0F3Vfif9pLhyKhQ="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-branch-atomist-npm-audit-main.2_1628064668652_0.9583837504928827"},"_hasShrinkwrap":false},"2.1.7-main.6":{"name":"@atomist/sdm","version":"2.1.7-main.6","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"794711e89adb6de90aa553cfc6d6c26fa91cfd96","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.6","_nodeVersion":"14.17.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-nG+8Lu8+XsMIqfWNcVo6FotztWCi5Lfxs0xgcqHBOkak3UIvubSrSizwLgPcAdygI2Cm/pt0uxOveLV6/4Hp8A==","shasum":"a0b04813b06a5fd8f7c8f38755d3422a22fc7094","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.6.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCkvRCRA9TVsSAnZWagAAkzQP/jb7LvaFXRCxQ1/s7LRG\nhqb8yIBA9c4E1Gyuaex24vfttzwKel8jl5RG2pY8R2ThBMn+nfL7LJVCJi5h\n6c0Hhp/OVmzyQTmwxJUVpbejdTLsFQs8Z5jWjP0x4PsOiRnqJJyDAQwqgytX\nNy9w8boVbbCeEdLqbkkqevwEbqN+0wrkmTXWoCKG4HUNbO1+TN9VKPLYqyG8\nZjGTiTqcCDbcpssyNcuLHRyw+QkyGPlmsUnvxIm9Doti8M6gD88IQndhF9ao\n+fYbBqAqisHIwF4KmwP3sZsk9l8TBMDu4/zYs2RDY2CwYlj8R4CAMrDZwFAm\nvGafxnssl43LMQ4OH1DFGn5amzds9MnG+256f4XuGuYSce0nHcQkjxczcz62\nyo1WghoEcIfXQ0AHpm2xC75rWgzH7zAlzud7CRCOgZmAm3P3SCocIMBeHqBF\n8tQp2RYAjoqKSan7s3xlsamKFp3kO6Hb1Ee7KCk5g2TkFZkXzagZAFI3euok\nYlgHdYWAvOsSU95eDNfE5VBjXeGJ/PO1q4YiOwonNil7JU3z4SXPnaKl8U8I\nIwZfFU7YfqnyLhmb+pX2EHvjkF7jbR06eyraMNfgG3dDf1JFe+w2mPwC4eax\nIQxGuXAA+PXHMx4OJPSsYAXWMiaTIIE9St9Zud7Q7vgPttSbnf3lqPHBjKPm\n0zA1\r\n=3rp+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCfrmdAnw3SAZQNOgSi5k6lWQ9rH2zj/MN8h4bAfAV7DQIhAL3q2agPwR0DVy4jDnWQckE7l5Wnz9iO7sg/lnen6J0s"}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.6_1628064721361_0.6028024803076282"},"_hasShrinkwrap":false},"2.1.7-main.7":{"name":"@atomist/sdm","version":"2.1.7-main.7","description":"Atomist Software Delivery Machine SDK","license":"Apache-2.0","author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"keywords":["atomist","automation","sdm","ci","cd"],"homepage":"https://github.com/atomist/sdm#readme","bugs":{"url":"https://github.com/atomist/sdm/issues"},"main":"./index.js","types":"./index.d.ts","dependencies":{"@atomist/antlr":"1.0.3-master.20200709215047","@atomist/automation-client":"^2.2.0","@atomist/slack-messages":"^1.2.2","@atomist/tree-path":"^1.0.3","@google-cloud/storage":"^5.8.1","@kubernetes/client-node":"^0.13.2","@octokit/rest":"^18.3.5","@types/app-root-path":"^1.2.4","@types/cron":"^1.7.2","@types/dateformat":"^3.0.0","@types/find-up":"^2.1.1","@types/flat":"0.0.28","@types/fs-extra":"^9.0.8","@types/js-yaml":"^3.12.6","@types/json-stable-stringify":"^1.0.32","@types/json-stringify-safe":"^5.0.0","@types/jszip":"^3.4.1","@types/license-checker":"^25.0.1","@types/lodash":"^4.14.168","@types/micromatch":"^4.0.1","@types/node":"^14.14.34","@types/proper-lockfile":"^4.1.1","@types/request":"^2.48.5","@types/semver":"^7.3.4","@types/sprintf-js":"^1.1.2","@types/stack-trace":"^0.0.29","@types/xmldoc":"^1.1.5","app-root-path":"^3.0.0","aws-sdk":"^2.863.0","axios":"^0.21.1","camelcase-keys":"^6.2.2","chalk":"^2.4.2","change-case":"^3.1.0","cron":"^1.7.1","dateformat":"^3.0.3","docker-file-parser":"^1.0.5","dockerfile-ast":"0.0.27","fast-glob":"^3.2.5","find-up":"^4.0.0","flat":"^4.1.1","fs-extra":"^9.1.0","jose":"^3.11.6","js-yaml":"^3.14.1","json-stable-stringify":"^1.0.1","json-stringify-safe":"^5.0.1","jszip":"^3.6.0","license-checker":"^25.0.1","lodash":"^4.17.21","micromatch":"^4.0.2","moment":"^2.29.1","moment-duration-format":"^2.3.2","omit-empty":"^1.0.0","proper-lockfile":"^4.1.2","request":"^2.88.2","semver":"^7.3.4","sha-regex":"^1.0.12","spdx-license-list":"^6.4.0","sprintf-js":"^1.1.2","stack-trace":"^0.0.10","strip-ansi":"^5.2.0","ts-essentials":"^7.0.1","vscode-languageserver-textdocument":"^1.0.1","vscode-languageserver-types":"^3.16.0","xmldoc":"^1.1.2","yargs-parser":"^18.1.3"},"devDependencies":{"@atomist/npm-tools":"^0.1.1-main.0","@types/mocha":"^8.2.1","@types/power-assert":"^1.5.3","axios-mock-adapter":"^1.19.0","barrelsby":"^2.2.0","espower-typescript":"^9.0.2","mocha":"^8.3.2","npm-run-all":"^4.1.5","power-assert":"^1.6.1","prettier":"^2.2.1","rimraf":"^3.0.2","supervisor":"^0.12.0","tmp":"^0.2.1","tmp-promise":"^2.1.1","ts-node":"^9.1.1","tslint":"^6.1.3","typedoc":"^0.20.31","typescript":"^4.2.3"},"directories":{"test":"test"},"scripts":{"autotest":"supervisor --watch index.ts,lib,test --extensions ts --no-restart-on exit --quiet --exec npm -- test","build":"run-s clean compile test lint doc","build:fast":"run-p compile-test lint doc","clean":"run-p clean:compile clean:test clean:doc clean:run","clean:barrels":"rimraf index.ts","clean:compile":"rimraf git-info.json build \"index.*\" \"{bin,lib,test}/**/*.{d.ts,js}{,.map}\" lib/typings/types.ts","clean:dist":"run-s clean clean:npm","clean:doc":"rimraf doc","clean:npm":"rimraf node_modules","clean:run":"rimraf *-v8.log profile.txt log","clean:test":"rimraf .nyc_output coverage","compile":"run-s gql:gen compile:barrels compile:ts","compile:barrels":"run-s clean:barrels compile:barrels:create","compile:barrels:create":"barrelsby --directory . --location top --include \"/lib/(?:api(?:-helper)?|spi|typings)/\" --exclude \"/node_modules/\" --exclude \"\\.d\\.ts\\$\" --exclude \"Projects?OperationRegistration\" --exclude \"Deployment\" --exclude \"SdmGoalMessage\"","compile:ts":"tsc --project .","compile-test":"run-s compile test:js","doc":"typedoc --mode modules --excludeExternals --ignoreCompilerErrors --exclude \"**/*.d.ts\" --out doc lib","fmt":"run-s fmt:gql","fmt:gql":"prettier --write \"lib/graphql/**/*.graphql\"","gql:gen":"atm-gql-gen","lint":"run-p lint:ts lint:gql","lint:ts":"tslint --config tslint.json --format verbose --project .","lint:gql":"prettier --list-different \"lib/graphql/**/*.graphql\"","lint:fix":"run-p lint:ts:fix fmt:gql","lint:ts:fix":"npm run lint:ts -- --fix","test":"mocha --require espower-typescript/guess \"test/**/*.test.ts\"","test:js":"mocha \"test/**/*.test.js\"","test:one":"mocha --require espower-typescript/guess \"test/**/${TEST:-*.test.ts}\"","prepack":"atm-npm-tools source-maps"},"engines":{"node":">=10.3.0","npm":">=6.0.0"},"gitHead":"aed0f0e49eb2901dce282ad57d9488cbac51f34d","readme":"<p align=\"center\">\n  <img src=\"https://images.atomist.com/sdm/SDM-Logo-Dark.png\">\n</p>\n \n# Software Delivery Machine - `@atomist/sdm`\n\nThis is the home of the Software Delivery Machine (SDM) framework and\nrelated projects.\n\nThe SDM framework enables you to control your delivery process in\ncode. Think of it as an API for your software delivery. See the\n[Atomist SDM documentation][atomist-doc] for more information on the\nconcept of a software delivery machine and how to create and develop\nan SDM.\n\n[atomist-doc]: https://docs.atomist.com/ \"Atomist Documentation\"\n\n## Migrating to SDM 2.0\n\nSDM version 2.0 aims to make it easier to use the SDM framework. We\nhave consolidated the most useful NPM packages under\n[@atomist/sdm][sdm-npm] to simplify dependency management. Rather\nthan separately installing @atomist/automation-client,\n@atomist/sdm-core, and various @atomist/sdm-pack-\\*, you now just\ninstall @atomist/sdm. @atomist/sdm-local is deprecated.\n\nTo update to the new single package in your SDM, follow these steps:\n\n1.  Remove Atomist SDM and automation-client dependencies\n\n        $ npm uninstall @atomist/automation-client @atomist/sdm @atomist/sdm-core @atomist/sdm-local\n\n2.  Remove any extension SDM packs\n\n        $ npm uninstall @atomist/sdm-pack-ABC @atomist/sdm-pack-DEF @atomist/sdm-pack-GHI\n\n3.  Reinstall Atomist SDM\n\n        $ npm install @atomist/sdm\n\n4.  The consolidation of packages introduces a breaking change: you must\n    update your import statements. Specifically:\n\n    -   Change `import … \"@atomist/automation-client\"` to `import … \"@atomist/sdm/lib/client\"`\n    -   Change `import … \"@atomist/sdm-core\"` to `import … \"@atomist/sdm/lib/core\"`\n    -   Change `import … \"@atomist/sdm-pack-spring\"` to `import … \"@atomist/sdm/lib/pack/jvm\"`\n    -   Change `import … \"@atomist/sdm-pack-XYZ\"` to `import … \"@atomist/sdm/lib/pack/XYZ\"`\n\n5.  `EditMode` in automation-client is now `editModes.EditMode` in `import { editModes } from \"@atomist/sdm/lib/client\"`\n\n6.  Some packs that were part of sdm-core have been relocated under\n    `sdm/lib/pack`, so their imports change like these:\n\n        import { githubGoalStatusSupport } from \"@atomist/sdm/lib/pack/github-goal-status\";\n        import { goalStateSupport } from \"@atomist/sdm/lib/pack/goal-state\";\n        import { notificationSupport } from \"@atomist/sdm/lib/pack/notification\";\n\n7.  Deprecated exports have been removed from SDM 2.0. If you need\n    help moving away from capabilities that have been removed, please\n    [contact us](#support).\n\n[sdm-npm]: https://www.npmjs.com/package/@atomist/sdm\n\n## Contributing\n\nContributions to this project from community members are encouraged\nand appreciated. Please review the [Contributing\nGuidelines](CONTRIBUTING.md) for more information. Also see the\n[Development](#development) section in this document.\n\n## Code of conduct\n\nThis project is governed by the [Code of\nConduct](CODE_OF_CONDUCT.md). You are expected to act in accordance\nwith this code by participating. Please report any unacceptable\nbehavior to code-of-conduct@atomist.com.\n\n## Documentation\n\nPlease see [docs.atomist.com][atomist-doc] for\n[developer][atomist-doc-sdm] documentation.\n\n-   API documentation for this project: [@atomist/sdm TypeDoc][typedoc]\n-   List of third-party OSS licenses used in this project: [@atomist/sdm OSS licenses][licenses]\n\n[atomist-doc-sdm]: https://docs.atomist.com/developer/sdm/ \"Atomist Documentation - SDM Developer\"\n[typedoc]: https://atomist.github.io/sdm/ \"@atomist/sdm TypeDoc\"\n[licenses]: legal/THIRD_PARTY.md \"@atomist/sdm Third-Party Licenses\"\n\n## Connect\n\nFollow [@atomist][atomist-twitter] and [The Composition][atomist-blog]\nblog related to SDM.\n\n[atomist-twitter]: https://twitter.com/atomist \"Atomist on Twitter\"\n[atomist-blog]: https://the-composition.com/ \"The Composition - The Official Atomist Blog\"\n\n## Support\n\nGeneral support questions should be discussed in the `#help`\nchannel in the [Atomist community Slack workspace][slack].\n\nIf you find a problem, please create an [issue][].\n\n[issue]: https://github.com/atomist/sdm/issues\n\n## Development\n\nYou will need to install [Node.js][node] to build and test this\nproject.\n\n[node]: https://nodejs.org/ \"Node.js\"\n\n### Build and test\n\nInstall dependencies.\n\n```\n$ npm install\n```\n\nUse the `build` package script to compile, test, lint, and build the\ndocumentation.\n\n```\n$ npm run build\n```\n\n### Release\n\nReleases are handled via the [Atomist SDM][atomist-sdm]. Just press\nthe 'Approve' button in the Atomist dashboard or Slack.\n\n[atomist-sdm]: https://github.com/atomist/atomist-sdm \"Atomist Software Delivery Machine\"\n\n---\n\nCreated by [Atomist][atomist].\nNeed Help? [Join our Slack workspace][slack].\n\n[atomist]: https://atomist.com/ \"Atomist - Automate All the Software Things\"\n[slack]: https://join.atomist.com/ \"Atomist Community Slack\"\n","readmeFilename":"README.md","_id":"@atomist/sdm@2.1.7-main.7","_nodeVersion":"14.17.4","_npmVersion":"6.14.14","dist":{"integrity":"sha512-NfQyJm74mIBIwiwdHu/MiJ0RYAGlrO23pNn+3hQ1/7VHzutU2beK3dPWx7fiiBJ6pwXLDF5sJ0m3Tv4K0xYnew==","shasum":"ac63ce391d0c6921594b18af962f89586879d5b1","tarball":"https://registry.npmjs.org/@atomist/sdm/-/sdm-2.1.7-main.7.tgz","fileCount":2436,"unpackedSize":10199542,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.13\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJhCkwkCRA9TVsSAnZWagAAGrkQAJKaQBJ+aLNq522DwIPY\nIuM9hnLZ4IxBT3E7Qmk3S02vnHx8yc45N+/Tfaykh4LdcCZam1DrN5ecaIws\nJWEC1jvy1q6oW5MZtl4DD6IasVYBXkXaXcM4gQn0uHGJjheNgYIkjNQYe/Zm\n5B7FgmLI36Y6x3sEyb3i30QN/hWwp3aiqYq4+Iod/xjpdoJm5vgd2ITRLU1p\nuWHReuutG3iWdSa/ABGgfuWpBqT7vtQcI6FOKra5lDV2583MUGWLYMvgTRtm\nPa9QbeaolDzVCnyKYGCbrgIy1P3mB6usteTq3UM7q/LOCvq9xaZNf4lzo3T4\noHPXv/ROsk9+yHPpi4H2g50ceIb4E4RQEb1cTh5d04NcEDzt7QG3VHEVDeoc\nEtZVA4bW1qp6soK0zkzlkDCarPhRpKsiocqWI/mIsipSmKjBAnHmRQ77FaN/\nB38ZJrqPF1ZZsICJhIkcfdkpulbPFpDP1IHqaiFN3ngSV0utfFAuEiUM8NVQ\nXrkQAfwyB9sGZubIfVvHhg+YizIN5CnKVUOBgWRZhbnh0uHNq3H+yu8Ifz7Z\naNW/w4HtJdnwg6pQb9WaxN+XA6SRD2fAzULP2Kn5YKxVv2eou/DDgoeNncRp\nno+QbaHxRkgu98+13SlkcBPrW2/+9/U486OJ7Kd4F3coGVlk3ymhW1K7TNFF\n18FB\r\n=XwR1\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGD54RSkCOSNh7z5MAMeSDDHQPxYhlAMhHkHx6YoxFisAiEA3ibmdefOsdCQlj28IQP9cD+qBSJHDrCVg9ULckJ+Ncw="}]},"_npmUser":{"name":"atomist-bot","email":"npm@atomist.com"},"maintainers":[{"name":"atomist-bot","email":"npm@atomist.com"},{"name":"cdupuis","email":"cd@atomist.com"},{"name":"slimslenderslacks","email":"slimslenderslacks@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sdm_2.1.7-main.7_1628064803842_0.16547877070123418"},"_hasShrinkwrap":false}},"time":{"created":"2018-05-08T00:59:21.850Z","0.0.1-no-env-vars.20180508005131":"2018-05-08T00:59:22.998Z","modified":"2023-02-23T17:59:13.350Z","0.0.1-nortissej.goal-summary-status-344.20180508011302":"2018-05-08T01:19:43.347Z","0.0.1-nortissej.goal-summary-status-344.20180508041617":"2018-05-08T04:23:25.731Z","0.0.1-nortissej.goal-summary-status-344.20180508044906":"2018-05-08T04:56:19.201Z","0.0.1-nortissej.goal-summary-status-344.20180508054553":"2018-05-08T05:52:42.215Z","0.0.1-20180508202723":"2018-05-08T20:34:40.073Z","0.0.1-20180508211058":"2018-05-08T21:18:17.682Z","0.0.1-20180508213550":"2018-05-08T21:43:18.273Z","0.0.1-20180508231021":"2018-05-08T23:17:49.271Z","0.0.1-20180509001540":"2018-05-09T00:23:02.381Z","0.0.1-20180509010107":"2018-05-09T01:09:12.567Z","0.0.1-20180509012335":"2018-05-09T01:31:05.613Z","0.0.1-nortissej.goal-summary-status-344.20180509014815":"2018-05-09T01:55:37.867Z","0.0.1-no-env-vars.20180509053956":"2018-05-09T05:47:28.689Z","0.0.1-no-env-vars.20180509184313":"2018-05-09T18:50:25.069Z","0.0.1-no-env-vars.20180509190459":"2018-05-09T19:12:21.485Z","0.0.1-nortissej.goal-summary-status-344.20180509192134":"2018-05-09T19:28:27.208Z","0.0.1-no-env-vars.20180509192236":"2018-05-09T19:31:28.338Z","0.0.1-nortissej.goal-summary-status-344.20180509192411":"2018-05-09T19:31:39.436Z","0.0.1-nortissej.goal-summary-status-344.20180509210119-nortissej.goal-summary-status-344.20180509210126":"2018-05-09T21:11:08.844Z","0.0.1-no-env-vars.20180509210153":"2018-05-09T21:13:01.519Z","0.0.1-bb3.20180509211400":"2018-05-09T21:21:55.650Z","0.0.1-no-env-vars.20180509220330":"2018-05-09T22:10:55.974Z","0.0.1-20180509223333":"2018-05-09T22:40:54.138Z","0.0.1-no-env-vars.20180509230218":"2018-05-09T23:09:12.114Z","0.0.1-no-env-vars.20180509233440":"2018-05-09T23:41:49.055Z","0.0.1-20180509234340":"2018-05-09T23:50:27.060Z","0.0.1-20180510001024":"2018-05-10T00:18:50.302Z","0.0.1-20180510011339":"2018-05-10T01:20:37.460Z","0.0.1-seed-id.20180510035101":"2018-05-10T03:57:44.799Z","0.0.1-20180510040313":"2018-05-10T04:11:56.992Z","0.0.1-nortissej.logginess.20180510053540":"2018-05-10T05:42:07.197Z","0.0.1-20180510054448":"2018-05-10T05:51:12.994Z","0.0.1-20180510053118":"2018-05-10T06:05:37.791Z","0.0.1-20180510061729":"2018-05-10T06:24:40.906Z","0.0.1-20180510065314":"2018-05-10T06:59:43.326Z","0.0.1-20180510053020":"2018-05-10T07:27:26.231Z","0.0.1-nortissej.skippy.20180510052648":"2018-05-10T07:53:33.885Z","0.0.1-20180510053848":"2018-05-10T07:54:42.888Z","0.0.1-20180510163140":"2018-05-10T16:43:48.242Z","0.0.1-20180510163759":"2018-05-10T16:46:19.065Z","0.0.1-gen-intf.20180510173735":"2018-05-10T17:44:04.541Z","0.0.1-20180510191805":"2018-05-10T19:25:09.944Z","0.0.1-20180510195559":"2018-05-10T20:03:17.379Z","0.0.1-semver-regexp.20180511032212":"2018-05-11T03:29:14.301Z","0.0.1-20180511034642":"2018-05-11T03:53:24.423Z","0.0.1-20180514212955":"2018-05-14T21:36:52.990Z","0.0.1-20180515073807":"2018-05-15T07:44:24.817Z","0.0.1-20180515075538":"2018-05-15T08:02:21.339Z","0.0.1-20180515213224":"2018-05-15T21:40:39.716Z","0.0.1-dashboardLogUrl.20180516141325":"2018-05-16T14:20:04.263Z","0.1.0-20180516144425":"2018-05-16T14:51:16.957Z","0.1.0":"2018-05-16T14:55:39.300Z","0.1.0-20180516150239":"2018-05-16T15:09:39.638Z","0.1.1-20180516151058":"2018-05-16T15:17:24.879Z","0.1.1-20180516161858":"2018-05-16T16:26:34.568Z","0.1.1-20180517014316":"2018-05-17T01:51:10.857Z","0.1.1-20180517023538":"2018-05-17T02:43:34.982Z","0.1.1-20180517163727":"2018-05-17T16:45:17.775Z","0.1.1-20180518072833":"2018-05-18T07:36:11.147Z","0.1.1-20180518081018":"2018-05-18T08:18:11.422Z","0.1.1":"2018-05-18T09:23:25.768Z","0.1.1-20180518225035":"2018-05-18T22:58:04.153Z","0.1.1-bb4.20180521073510":"2018-05-21T07:43:01.414Z","0.1.1-20180521075352":"2018-05-21T08:01:26.622Z","0.1.1-20180522055644":"2018-05-22T06:03:49.477Z","0.1.1-20180522085739":"2018-05-22T09:11:48.150Z","0.1.1-generalize-goal.20180524082820":"2018-05-24T08:35:03.479Z","0.1.1-20180524085115":"2018-05-24T08:58:52.655Z","0.1.1-20180524100532":"2018-05-24T10:12:19.347Z","0.1.1-20180525014606":"2018-05-25T01:52:45.410Z","0.1.1-20180525032831":"2018-05-25T03:35:02.209Z","0.1.1-20180525053833":"2018-05-25T05:45:09.886Z","0.1.1-polishing.20180525073839":"2018-05-25T07:45:20.697Z","0.1.1-20180525075547":"2018-05-25T08:02:28.782Z","0.1.1-20180525080431":"2018-05-25T08:11:06.627Z","0.1.1-20180525092937":"2018-05-25T09:36:28.871Z","0.1.1-20180525123950":"2018-05-25T12:46:40.193Z","0.1.1-20180525230434":"2018-05-25T23:11:46.790Z","0.1.1-make-non-fulfillment-natural.20180526003358":"2018-05-26T00:41:58.075Z","0.1.1-20180526054313":"2018-05-26T05:50:07.251Z","0.1.1-20180526083456":"2018-05-26T08:40:52.414Z","0.1.1-20180526090148":"2018-05-26T09:09:14.619Z","0.1.1-20180526103827":"2018-05-26T10:45:11.583Z","0.1.1-20180526110303":"2018-05-26T11:09:39.114Z","0.1.1-20180526115224":"2018-05-26T11:59:31.190Z","0.1.1-make-non-fulfillment-natural.20180527002305":"2018-05-27T00:30:30.125Z","0.1.1-well-known-goal.20180527023300":"2018-05-27T02:39:20.528Z","0.1.1-well-known-goal.20180527034843":"2018-05-27T03:53:37.470Z","0.1.1-20180527043355":"2018-05-27T04:38:54.234Z","0.1.1-20180527075809":"2018-05-27T08:03:06.285Z","0.1.1-20180527081903":"2018-05-27T08:23:54.753Z","0.1.1-20180527084208":"2018-05-27T08:46:58.321Z","0.1.1-20180528004157":"2018-05-28T00:46:56.316Z","0.1.1-20180528035158":"2018-05-28T03:57:04.825Z","0.1.1-20180528101113":"2018-05-28T10:16:09.971Z","0.1.1-fptrActionsShouldNotRequireScreenName.20180528185540":"2018-05-28T19:00:31.748Z","0.1.1-20180528232407":"2018-05-28T23:29:03.382Z","0.1.1-20180528233959":"2018-05-28T23:44:43.761Z","0.1.1-interface.20180529005506":"2018-05-29T00:59:59.056Z","0.1.1-interface.20180529005931":"2018-05-29T01:04:20.801Z","0.1.1-interface.20180529012530":"2018-05-29T01:30:18.705Z","0.1.1-interface.20180529015252":"2018-05-29T01:57:49.558Z","0.1.1-20180529033448":"2018-05-29T03:39:36.239Z","0.1.1-20180529042523":"2018-05-29T04:30:19.481Z","0.1.1-20180529045407":"2018-05-29T04:59:02.679Z","0.1.1-api.20180529082227":"2018-05-29T08:27:15.678Z","0.2.0-api.20180529083549":"2018-05-29T08:40:40.999Z","0.2.0-api.20180529084109":"2018-05-29T08:45:55.934Z","0.2.0-api.20180529084331":"2018-05-29T08:48:20.465Z","0.2.0-api.20180529100906":"2018-05-29T10:13:51.296Z","0.2.0-api.20180529110711":"2018-05-29T11:12:08.850Z","0.2.0-api.20180529111106":"2018-05-29T11:15:52.463Z","0.2.0-api.20180529121135":"2018-05-29T12:16:46.032Z","0.2.0-api.20180529122027":"2018-05-29T12:25:32.174Z","0.2.1-api.20180529122644":"2018-05-29T12:31:47.051Z","0.2.1-postWithCurl.20180529152121":"2018-05-29T15:26:03.937Z","0.1.1-postWithCurl.20180529153923":"2018-05-29T15:44:49.474Z","0.2.1-api.20180529205953":"2018-05-29T21:04:59.026Z","0.2.1-api.20180529220151":"2018-05-29T22:25:11.471Z","0.2.1-20180529223510":"2018-05-29T22:40:03.097Z","0.2.1-polish.20180530002614":"2018-05-30T00:31:11.827Z","0.2.1-polish.20180530004736":"2018-05-30T00:52:07.876Z","0.2.1-polish.20180530034226":"2018-05-30T03:47:06.164Z","0.2.1-polish.20180530050908":"2018-05-30T05:14:14.006Z","0.2.1-20180530051024":"2018-05-30T05:15:23.990Z","0.2.1-20180530051716":"2018-05-30T05:22:26.615Z","0.2.1-enrich.20180530063850":"2018-05-30T06:43:49.443Z","0.2.1-20180530064817":"2018-05-30T06:54:06.963Z","0.2.1-20180530070203":"2018-05-30T07:09:40.488Z","0.2.1-20180530071633":"2018-05-30T07:24:58.146Z","0.2.1-20180530071326":"2018-05-30T07:25:16.812Z","0.2.1-20180530073225":"2018-05-30T07:37:08.727Z","0.2.1-20180530075827":"2018-05-30T08:03:17.366Z","0.2.1-editor-sdm.20180530090025":"2018-05-30T09:04:57.931Z","0.2.1-editor-sdm.20180530095850":"2018-05-30T10:03:23.366Z","0.2.1-editor-sdm.20180530120209":"2018-05-30T12:07:22.961Z","0.2.1-editor-sdm.20180530120342":"2018-05-30T12:09:32.734Z","0.2.1-editor-sdm.20180530121242":"2018-05-30T12:17:25.595Z","0.2.1-editor-sdm.20180530162437":"2018-05-30T16:29:59.575Z","0.2.1-errorReportingFromGoals.20180530163753":"2018-05-30T16:43:30.623Z","0.2.1-editor-sdm.20180530164525":"2018-05-30T16:50:36.512Z","0.2.1-errorReportingFromGoals.20180530170220":"2018-05-30T17:07:28.632Z","0.2.1-errorReportingFromGoals.20180530192143":"2018-05-30T19:26:44.270Z","0.2.1-errorReportingFromGoals.20180530195034":"2018-05-30T19:55:50.271Z","0.2.1-errorReportingFromGoals.20180530195232":"2018-05-30T19:57:26.674Z","0.2.1-errorReportingFromGoals.20180530211449":"2018-05-30T21:19:55.235Z","0.2.1-editor-sdm.20180530212852":"2018-05-30T21:33:51.975Z","0.2.1-editor-sdm.20180531011547":"2018-05-31T01:20:34.795Z","0.2.1-editor-sdm.20180531024918":"2018-05-31T02:54:13.335Z","0.2.1-20180531043448":"2018-05-31T04:39:50.437Z","0.2.1-reorg.20180531061248":"2018-05-31T06:17:47.570Z","0.2.1-20180531071516":"2018-05-31T07:20:43.807Z","0.2.1-errorReportingFromGoals.20180531135229":"2018-05-31T13:57:24.169Z","0.2.1-errorReportingFromGoals.20180531144715":"2018-05-31T14:52:04.220Z","0.2.1-errorReportingFromGoals.20180531151230":"2018-05-31T15:17:30.482Z","0.2.1-errorReportingFromGoals.20180531152246":"2018-05-31T15:27:29.083Z","0.2.1-errorReportingFromGoals.20180531154422":"2018-05-31T15:49:00.866Z","0.2.1-errorReportingFromGoals.20180531155225":"2018-05-31T15:57:08.414Z","0.2.1-20180531233725":"2018-05-31T23:42:19.126Z","0.2.1-20180601001211":"2018-06-01T00:17:07.880Z","0.2.1-20180601004700":"2018-06-01T00:51:34.623Z","0.2.1-20180601012851":"2018-06-01T01:33:40.937Z","0.2.1-20180601014418":"2018-06-01T01:49:07.055Z","0.2.1-20180531025539":"2018-06-01T03:59:56.519Z","0.2.1-command-context.20180601053941":"2018-06-01T05:44:23.777Z","0.2.1-command-context.20180601055341":"2018-06-01T05:58:20.101Z","0.2.1-command-context.20180601061921":"2018-06-01T06:23:54.876Z","0.2.1-command-context.20180601083159":"2018-06-01T08:36:47.955Z","0.2.1-20180601083910":"2018-06-01T08:43:47.563Z","0.2.1-config-options-rework.20180601123701":"2018-06-01T12:42:03.490Z","0.2.1-config-options-rework.20180601140919":"2018-06-01T14:14:33.825Z","0.2.1-config-options-rework.20180601230814":"2018-06-01T23:13:33.289Z","0.2.1-config-options-rework.20180601231049":"2018-06-01T23:16:03.635Z","0.2.1-20180601231552":"2018-06-01T23:20:42.808Z","0.2.1-nortissej.move-remote-repo-refs-down.20180602155932":"2018-06-02T16:03:59.149Z","0.2.1-nortissej.move-remote-repo-refs-down.20180602160134":"2018-06-02T16:06:42.581Z","0.2.1-nortissej.move-remote-repo-refs-down.20180602163041":"2018-06-02T16:35:25.698Z","0.2.1-20180602225124":"2018-06-02T22:56:23.302Z","0.2.1-20180602234651":"2018-06-02T23:52:04.409Z","0.2.1-nortissej.move-remote-repo-refs-down.20180602235207":"2018-06-02T23:57:20.468Z","0.2.1-20180603020351":"2018-06-03T02:08:29.004Z","0.2.1-goal-split.20180603042648":"2018-06-03T04:31:22.196Z","0.2.1-20180603044911":"2018-06-03T04:53:50.798Z","0.2.1-nortissej.move-remote-repo-refs-down.20180603050234":"2018-06-03T05:07:29.952Z","0.2.1-20180603060320":"2018-06-03T06:08:24.875Z","0.2.1-20180603060403":"2018-06-03T06:09:20.365Z","0.2.1-abstract-sdm.20180603062543":"2018-06-03T06:30:24.054Z","0.2.1-abstract-sdm.20180603062659":"2018-06-03T06:32:02.370Z","0.2.1-20180603063743":"2018-06-03T06:42:47.911Z","0.2.1-20180603071215":"2018-06-03T07:17:20.010Z","0.2.1-20180603082724":"2018-06-03T08:32:44.753Z","0.2.1-20180603084044":"2018-06-03T08:45:51.054Z","0.2.1-goal-info-in-log.20180603154838":"2018-06-03T15:53:48.059Z","0.2.1-goal-info-in-log.20180603174739":"2018-06-03T17:52:22.114Z","0.2.1-goal-info-in-log.20180603174850":"2018-06-03T17:53:33.305Z","0.2.1-goal-info-in-log.20180603180635":"2018-06-03T18:11:12.302Z","0.2.1-20180603222736":"2018-06-03T22:32:13.947Z","0.2.1-20180604015732":"2018-06-04T02:02:04.494Z","0.2.1-20180604023535":"2018-06-04T02:40:16.911Z","0.2.1-20180604033543":"2018-06-04T03:40:24.702Z","0.2.1-20180604055111":"2018-06-04T05:56:03.798Z","0.2.1-20180604074226":"2018-06-04T07:47:23.902Z","0.2.1-nortissej.move-remote-repo-refs-down.20180604155743":"2018-06-04T16:02:29.654Z","0.2.1-nortissej.move-remote-repo-refs-down.20180604212634":"2018-06-04T21:31:34.579Z","0.2.1-20180605033211":"2018-06-05T03:36:57.858Z","0.2.1-nortissej.move-remote-repo-refs-down.20180605033552":"2018-06-05T03:40:51.590Z","0.2.1-20180605033754":"2018-06-05T03:43:21.757Z","0.2.1-errorReportingFromGoals.20180605034944":"2018-06-05T03:54:43.334Z","0.2.1-20180605045818":"2018-06-05T05:03:16.843Z","0.2.1-20180605062502":"2018-06-05T06:29:57.316Z","0.2.1-20180605065906":"2018-06-05T07:04:26.847Z","0.2.1-npm-publish-tag-404.20180605171537":"2018-06-05T17:21:18.620Z","0.2.1-20180605180909":"2018-06-05T18:13:52.222Z","0.2.1-cred-res.20180606003341":"2018-06-06T00:38:40.332Z","0.2.1-20180606044140":"2018-06-06T04:46:27.432Z","0.2.1-20180606054624":"2018-06-06T05:51:05.718Z","0.2.1-nortissej.tag-as-branch.20180606180606":"2018-06-06T18:10:39.163Z","0.2.1-20180606233508":"2018-06-06T23:39:42.440Z","0.2.1-20180607000642":"2018-06-07T00:11:58.157Z","0.2.1-local-sdm.20180607004549":"2018-06-07T00:50:44.666Z","0.2.1-local-sdm.20180607043659":"2018-06-07T04:42:07.372Z","0.2.1-local-sdm.20180607052512":"2018-06-07T05:30:17.788Z","0.2.1-local-sdm.20180607062909":"2018-06-07T06:34:15.474Z","0.2.1-local-sdm.20180607065849":"2018-06-07T07:03:52.231Z","0.2.1-20180607073348":"2018-06-07T07:38:27.401Z","0.2.1-local-sdm.20180607074133":"2018-06-07T07:46:30.482Z","0.2.1-20180607135915":"2018-06-07T14:06:15.721Z","0.2.1-20180607235138":"2018-06-07T23:56:27.176Z","0.2.1-local-sdm.20180608022719":"2018-06-08T02:32:18.145Z","0.2.1-local-sdm.20180608025633":"2018-06-08T03:01:46.950Z","0.2.1-local-sdm.20180608031417":"2018-06-08T03:19:18.934Z","0.2.1-local-sdm.20180608032949":"2018-06-08T03:34:42.495Z","0.2.1-local-sdm.20180608054758":"2018-06-08T05:53:03.292Z","0.2.1-extract-packs.20180608092040":"2018-06-08T09:25:21.592Z","0.2.1-extract-packs.20180608111718":"2018-06-08T11:21:50.745Z","0.2.1-20180608130242":"2018-06-08T13:07:29.470Z","0.2.1-20180608143410":"2018-06-08T14:41:11.868Z","0.2.1":"2018-06-08T14:41:49.160Z","0.2.2-20180609080246":"2018-06-09T08:08:13.836Z","0.2.2-20180610013638":"2018-06-10T01:41:19.916Z","0.2.2-select-own-goals.20180611045118":"2018-06-11T04:55:59.176Z","0.2.2-select-own-goals.20180611045205":"2018-06-11T04:57:02.944Z","0.2.2-20180611112048":"2018-06-11T11:25:30.687Z","0.2.2-extract-packs.20180612003823":"2018-06-12T00:42:33.450Z","0.2.2-extract-packs.20180612004641":"2018-06-12T00:50:50.801Z","0.2.2-20180613171031":"2018-06-13T17:15:24.076Z","0.2.2-20180613210712":"2018-06-13T21:11:28.750Z","0.2.2-20180613231852":"2018-06-13T23:23:07.718Z","0.2.2-20180614004626":"2018-06-14T00:50:43.189Z","0.2.2-nortissej.client-as-peer.20180614023122":"2018-06-14T02:35:44.386Z","0.2.2-20180614082404":"2018-06-14T08:28:44.354Z","0.2.2-20180614151507":"2018-06-14T15:19:29.862Z","0.2.2-20180614153700":"2018-06-14T15:41:25.640Z","0.2.2-20180614162156":"2018-06-14T16:26:14.919Z","0.2.2-20180614203952":"2018-06-14T20:44:01.917Z","0.2.2-client-as-peer.20180615205956":"2018-06-15T21:04:32.597Z","0.2.2-20180615211533":"2018-06-15T21:20:13.263Z","0.2.2":"2018-06-15T21:21:39.882Z","0.2.3-20180615212238":"2018-06-15T21:26:47.033Z","0.2.3-nortissej.catch-deploy-enablement-exception.20180616014324":"2018-06-16T01:47:49.445Z","0.2.3-nortissej.break-out-api.20180618174354":"2018-06-18T17:48:24.167Z","0.2.3-nortissej.break-out-api.20180618192614":"2018-06-18T19:30:43.158Z","0.2.3-20180618195715":"2018-06-18T20:01:46.816Z","0.2.3-nortissej.break-out-api.20180618203943":"2018-06-18T20:44:59.944Z","0.2.3-nortissej.break-out-api.20180618205826":"2018-06-18T21:03:00.490Z","0.2.3-nortissej.bb-curl-or-axios.20180619000533":"2018-06-19T00:09:58.112Z","0.2.3-20180619015100":"2018-06-19T01:55:22.068Z","0.2.3-20180619084714":"2018-06-19T08:52:49.907Z","0.2.3-20180619175324":"2018-06-19T17:57:45.668Z","0.2.3-nortissej.custom-seed.20180619232043":"2018-06-19T23:25:23.043Z","0.2.3-nortissej.custom-seed.20180619234931":"2018-06-19T23:55:55.266Z","0.2.3-20180620025559":"2018-06-20T03:01:25.569Z","0.2.3":"2018-06-20T03:10:00.156Z","0.2.4-20180620031154":"2018-06-20T03:16:41.685Z","0.2.4-20180620070909":"2018-06-20T07:13:37.539Z","0.2.4-20180620095255":"2018-06-20T09:57:17.049Z","0.2.4-20180620102234":"2018-06-20T10:26:47.811Z","0.2.4-20180620104508":"2018-06-20T10:49:27.498Z","0.2.4-20180620115206":"2018-06-20T11:56:40.341Z","0.2.4-20180620132420":"2018-06-20T13:30:18.363Z","0.2.4-20180620134904":"2018-06-20T13:53:07.042Z","0.2.4-20180620134805":"2018-06-20T13:53:27.715Z","0.2.4-20180621002745":"2018-06-21T00:32:01.347Z","0.2.4-20180621120207":"2018-06-21T12:06:27.715Z","0.2.4-20180621141531":"2018-06-21T14:20:39.269Z","0.2.4-nortissej.break-out-api.20180621141931":"2018-06-21T14:24:55.902Z","0.2.4-nortissej.break-out-api.20180621143116":"2018-06-21T14:35:40.290Z","0.2.4-nortissej.break-out-api.20180621143608":"2018-06-21T14:41:15.619Z","0.2.4-nortissej.break-out-api.20180621144050":"2018-06-21T14:47:17.741Z","0.2.4-nortissej.break-out-api.20180621144851":"2018-06-21T14:54:11.789Z","0.2.4-nortissej.break-out-api.20180621145721":"2018-06-21T15:02:19.244Z","0.2.4-nortissej.break-out-api.20180621150023":"2018-06-21T15:05:53.534Z","0.2.4-nortissej.break-out-api.20180621151712":"2018-06-21T15:23:42.289Z","0.2.4-nortissej.break-out-api.20180621173032":"2018-06-21T17:34:44.889Z","0.2.4-nortissej.break-out-api.20180621180625":"2018-06-21T18:10:36.974Z","0.2.4-nortissej.break-out-api.20180621181311":"2018-06-21T18:16:21.908Z","0.2.4-nortissej.break-out-api.20180621212047":"2018-06-21T21:24:26.973Z","0.3.0-nortissej.break-out-api.20180622000216":"2018-06-22T00:05:04.755Z","0.3.0-nortissej.break-out-api.20180622003029":"2018-06-22T00:33:16.542Z","0.3.0-nortissej.break-out-api.20180624172126":"2018-06-24T17:24:14.429Z","0.3.0-nortissej.break-out-api.20180624192958":"2018-06-24T19:32:31.385Z","0.2.4-20180625012428":"2018-06-25T01:29:17.711Z","0.2.4-20180625012513":"2018-06-25T01:29:47.374Z","0.3.0-nortissej.break-out-api.20180625013532":"2018-06-25T01:38:15.645Z","0.3.0-nortissej.break-out-api.20180625025708":"2018-06-25T03:00:07.354Z","0.2.4-20180625070826":"2018-06-25T07:12:40.380Z","0.3.0-nortissej.break-out-api.20180625080552":"2018-06-25T08:11:56.400Z","0.3.0-nortissej.break-out-api.20180625084757":"2018-06-25T08:50:44.454Z","0.3.0-20180625085140":"2018-06-25T08:54:27.272Z","0.3.0-20180625094611":"2018-06-25T09:49:05.644Z","0.3.0-20180625100939":"2018-06-25T10:12:30.170Z","0.3.0-20180625110209":"2018-06-25T11:04:59.678Z","0.3.0":"2018-06-25T19:00:58.690Z","0.3.1-20180625190323":"2018-06-25T19:07:15.155Z","0.3.1-20180625215920":"2018-06-25T22:02:25.066Z","0.3.1-nortissej.keep-log-newlines.20180625225614":"2018-06-25T22:59:20.790Z","0.3.1-nortissej.keep-log-newlines.20180625230522":"2018-06-25T23:08:27.109Z","0.3.1-20180626143351":"2018-06-26T14:37:19.243Z","0.3.1-reg-simplification.20180628133119":"2018-06-28T13:34:19.864Z","0.3.1-reg-simplification.20180628134819":"2018-06-28T14:21:12.656Z","0.3.1-reg-simplification.20180628162605":"2018-06-28T16:30:57.265Z","0.3.1-reg-simplification.20180628172510":"2018-06-28T17:28:00.319Z","0.3.1-20180628192236":"2018-06-28T19:26:01.636Z","0.3.1-reg-simplification.20180628200224":"2018-06-28T20:05:33.529Z","0.3.1-ingester-event-registration.20180628202635":"2018-06-28T20:29:38.762Z","0.3.1-20180628203626":"2018-06-28T20:39:44.765Z","0.3.1-20180628221505":"2018-06-28T22:18:18.456Z","0.3.1-20180629045944":"2018-06-29T05:02:43.791Z","0.3.1-20180629115205":"2018-06-29T11:54:53.395Z","0.3.1-20180629125403":"2018-06-29T12:56:45.550Z","0.3.1-20180629160827":"2018-06-29T16:11:16.547Z","0.3.1-params-builder.20180629200133":"2018-06-29T20:04:43.442Z","0.3.1-20180629201053":"2018-06-29T20:14:00.002Z","0.3.1-20180629203710":"2018-06-29T20:40:17.464Z","0.3.1-20180629204030":"2018-06-29T20:43:28.372Z","0.3.1-20180629211402":"2018-06-29T21:16:55.260Z","0.3.1-20180629214333":"2018-06-29T21:46:19.089Z","0.3.1-20180629220406":"2018-06-29T22:07:01.991Z","0.3.1-20180630015315":"2018-06-30T01:56:09.719Z","0.3.1-transform.20180701205251":"2018-07-01T20:55:42.429Z","0.3.1-transform.20180701205833":"2018-07-01T21:01:23.472Z","0.3.1-transform.20180701213407":"2018-07-01T21:37:04.982Z","0.3.1-transform.20180701215152":"2018-07-01T21:55:10.592Z","0.3.1-gen-starting-point.20180702002619":"2018-07-02T00:29:15.393Z","0.3.1-gen-starting-point.20180702002850":"2018-07-02T00:31:49.676Z","0.3.1-gen-starting-point.20180702010626":"2018-07-02T01:09:25.363Z","0.3.1-gen-starting-point.20180702015639":"2018-07-02T01:59:36.886Z","0.3.1-gen-starting-point.20180702021430":"2018-07-02T02:17:33.027Z","0.3.1-gen-starting-point.20180702024421":"2018-07-02T02:47:22.162Z","0.3.1-20180702115400":"2018-07-02T11:57:06.890Z","0.3.1-20180702171026":"2018-07-02T17:12:55.583Z","0.3.1-gen-starting-point.20180702195220":"2018-07-02T19:55:30.558Z","0.3.1-gen-starting-point.20180702202418":"2018-07-02T20:27:02.551Z","0.3.1-20180702210421":"2018-07-02T21:06:58.409Z","0.3.1-gen-starting-point.20180702213722":"2018-07-02T21:40:16.810Z","0.3.1-20180702214201":"2018-07-02T21:46:24.211Z","0.3.1-20180702214401":"2018-07-02T21:48:44.493Z","0.3.1-20180703023241":"2018-07-03T02:34:52.711Z","0.3.1-nortissej.deprecate-status.20180703024032":"2018-07-03T02:43:11.665Z","0.3.1-nortissej.deprecate-status.20180703032749":"2018-07-03T03:30:01.886Z","0.3.1-nortissej.deprecate-status.20180703033650":"2018-07-03T03:39:02.017Z","0.3.1-nortissej.deprecate-status.20180703035816":"2018-07-03T04:00:44.812Z","0.3.1-nortissej.deprecate-status.20180703041319":"2018-07-03T04:15:31.114Z","0.3.1-20180703095807":"2018-07-03T10:00:42.537Z","0.3.1-20180703134245":"2018-07-03T13:44:59.128Z","0.3.1-nortissej.deprecate-status.20180703140257":"2018-07-03T14:05:13.932Z","0.3.1-nortissej.deprecate-status.20180703141847":"2018-07-03T14:21:37.377Z","0.3.1-20180703145217":"2018-07-03T14:54:46.713Z","0.3.1-20180703151658":"2018-07-03T15:20:30.085Z","0.3.1-nortissej.deprecate-status.20180703151900":"2018-07-03T15:21:59.668Z","0.3.1-20180703151953":"2018-07-03T15:23:36.819Z","0.3.1-20180703152732":"2018-07-03T15:30:25.446Z","0.3.1-nortissej.ES2017.20180703182321":"2018-07-03T18:26:16.014Z","0.3.1-20180703193445":"2018-07-03T19:37:59.492Z","0.3.1-logPushTestResults.20180703194659":"2018-07-03T19:50:00.084Z","0.3.1-20180703195415":"2018-07-03T19:57:25.921Z","0.3.1-nortissej.log-caching.20180703204622":"2018-07-03T20:49:08.974Z","0.3.1-20180703221152":"2018-07-03T22:15:25.465Z","0.3.1-nortissej.manual-memoize.20180703221210":"2018-07-03T22:15:32.920Z","0.3.1-20180704045853":"2018-07-04T05:01:56.973Z","0.3.1-20180704065423":"2018-07-04T06:57:25.876Z","0.3.1-20180704120133":"2018-07-04T12:04:45.834Z","0.3.1-20180704121313":"2018-07-04T12:16:17.368Z","0.3.1-code-transform-registerable.20180705002613":"2018-07-05T00:39:10.786Z","0.3.1-20180705073411":"2018-07-05T07:37:06.111Z","0.3.1":"2018-07-05T15:44:51.233Z","0.3.2-20180705154718":"2018-07-05T15:50:38.646Z","0.3.2-20180705215343":"2018-07-05T21:56:52.660Z","0.3.2-gen-magic.20180705234214":"2018-07-05T23:45:16.492Z","0.3.2-gen-magic.20180706003901":"2018-07-06T00:42:04.691Z","0.3.2-gen-magic.20180706012312":"2018-07-06T01:26:41.125Z","0.3.2-gen-magic.20180706013226":"2018-07-06T01:35:36.185Z","0.3.2-gen-magic.20180706063739":"2018-07-06T06:40:43.518Z","0.3.2-goal-paging.20180706100341":"2018-07-06T10:07:12.601Z","0.3.2-20180706100354":"2018-07-06T10:07:28.101Z","0.3.2-goal-paging.20180706103636":"2018-07-06T10:39:44.183Z","0.3.2-goals-dsl.20180706160415":"2018-07-06T16:07:23.619Z","0.3.2-20180706160956":"2018-07-06T16:13:07.069Z","0.3.2-20180706161628":"2018-07-06T16:19:33.176Z","0.3.2-20180706165913":"2018-07-06T17:02:26.233Z","0.3.2-goals-dsl.20180706171459":"2018-07-06T17:18:15.523Z","0.3.2-goals-dsl.20180706175123":"2018-07-06T17:54:48.126Z","0.3.2-goals-dsl.20180706200437":"2018-07-06T20:07:48.260Z","0.3.2-nortissej.remove-status-field.20180706202026":"2018-07-06T20:23:31.175Z","0.3.2-20180706211438":"2018-07-06T21:18:14.566Z","0.3.2-dry.20180707081308":"2018-07-07T08:16:40.593Z","0.3.2-dry.20180707082114":"2018-07-07T08:24:46.728Z","0.3.2-dry.20180707082827":"2018-07-07T08:31:57.218Z","0.3.2-dry.20180707083101":"2018-07-07T08:34:33.725Z","0.3.2-dry.20180707083834":"2018-07-07T08:41:52.272Z","0.3.2-dry.20180707102223":"2018-07-07T10:25:52.043Z","0.3.2-dry.20180708020623":"2018-07-08T02:09:32.562Z","0.3.2-dry.20180708023417":"2018-07-08T02:37:25.948Z","0.3.2-dry3.20180708083849":"2018-07-08T08:42:02.908Z","0.3.2-dry4.20180708130221":"2018-07-08T13:05:41.967Z","0.3.2-dry4.20180708132247":"2018-07-08T13:26:06.055Z","0.3.2-dry4.20180708135227":"2018-07-08T13:55:40.742Z","0.3.2-dry4-simpler.20180708230924":"2018-07-08T23:12:35.576Z","0.3.2-dry4-simpler.20180708233810":"2018-07-08T23:41:24.489Z","0.3.2-dry4-simpler.20180708235213":"2018-07-08T23:55:29.512Z","0.3.2-dry4-simpler.20180709000355":"2018-07-09T00:07:28.734Z","0.3.2-dry4-simpler.20180709004010":"2018-07-09T00:43:18.411Z","0.3.2-20180709010018":"2018-07-09T01:03:54.097Z","0.3.2-dry4-simpler.20180709014010":"2018-07-09T01:43:26.363Z","0.3.2-20180709021108":"2018-07-09T02:14:14.463Z","0.3.2-inspect.20180709024734":"2018-07-09T02:50:55.596Z","0.3.2-inspect.20180709025105":"2018-07-09T02:54:13.759Z","0.3.2-inspect.20180709044431":"2018-07-09T04:47:42.692Z","0.3.2-inspect.20180709050213":"2018-07-09T05:05:14.100Z","0.3.2-inspect.20180709052220":"2018-07-09T05:25:25.813Z","0.3.2-inspect.20180709053644":"2018-07-09T05:39:55.681Z","0.3.2-inspect.20180709060712":"2018-07-09T06:10:18.690Z","0.3.2-inspect.20180709064926":"2018-07-09T06:52:36.784Z","0.3.2-20180709081501":"2018-07-09T08:18:13.719Z","0.3.2-inspect.20180709085810":"2018-07-09T09:01:38.394Z","0.3.2-20180709120101":"2018-07-09T10:03:06.137Z","0.3.2-inspect.20180709103118":"2018-07-09T10:33:54.732Z","0.3.2-20180709125009":"2018-07-09T12:53:27.908Z","0.3.2-inspect.20180709150530":"2018-07-09T15:08:50.500Z","0.3.2-20180709215344":"2018-07-09T21:57:17.630Z","0.3.2-stronger-typed.20180709223359":"2018-07-09T22:37:26.646Z","0.3.2-stronger-typed.20180709224410":"2018-07-09T22:47:33.830Z","0.3.2-nortissej.barrels-behave.20180709225535":"2018-07-09T22:59:17.117Z","0.3.2-20180709225831":"2018-07-09T23:02:05.054Z","0.3.2-20180709232854":"2018-07-09T23:32:32.164Z","0.3.2-stronger-typed.20180710001447":"2018-07-10T00:18:11.453Z","0.3.2-stronger-typed.20180710002349":"2018-07-10T00:27:20.464Z","0.3.2-20180710010159":"2018-07-10T01:05:45.148Z","0.3.2-remove-deprecated.20180710013529":"2018-07-10T01:39:38.127Z","0.3.2-20180710042338":"2018-07-10T04:26:32.560Z","0.3.2-20180710050414":"2018-07-10T05:07:07.604Z","0.3.2-lazy.20180711052503":"2018-07-11T05:28:16.130Z","0.3.2-lazy.20180711053019":"2018-07-11T05:33:29.195Z","0.3.2-lazy.20180711065346":"2018-07-11T06:57:01.695Z","0.3.2-lazy.20180711071049":"2018-07-11T07:14:02.961Z","0.3.2-lazy.20180712000747":"2018-07-12T00:11:07.376Z","0.3.2-lazy.20180712013217":"2018-07-12T01:35:32.995Z","0.3.2-targeting.20180712024716":"2018-07-12T02:50:38.864Z","0.3.2-targeting.20180712025100":"2018-07-12T02:54:25.694Z","0.3.2-targeting.20180712034742":"2018-07-12T03:50:55.076Z","0.3.2-targeting.20180712041512":"2018-07-12T04:18:29.403Z","0.3.2-targeting.20180712043342":"2018-07-12T04:37:01.526Z","0.3.2-targeting.20180712050924":"2018-07-12T05:12:35.313Z","0.3.2-targeting.20180712054358":"2018-07-12T05:47:18.950Z","0.3.2-targeting.20180712055106":"2018-07-12T05:54:20.496Z","0.3.2-targeting.20180712071458":"2018-07-12T07:18:19.008Z","0.3.2-targeting.20180712071725":"2018-07-12T07:20:52.254Z","0.3.2-targeting.20180712073656":"2018-07-12T07:40:32.795Z","0.3.2-lieven.npm-audit-fix.20180712082812":"2018-07-12T08:31:25.557Z","0.3.2-lieven.npm-audit-fix.20180712085609":"2018-07-12T08:59:19.895Z","0.3.2-targeting.20180712091712":"2018-07-12T09:21:26.028Z","0.3.2-20180712092230":"2018-07-12T09:26:12.936Z","0.3.2-progress-report.20180712161734":"2018-07-12T16:21:18.394Z","0.3.2-progress-report.20180712164148":"2018-07-12T16:45:23.374Z","0.3.2-20180712205800":"2018-07-12T21:01:18.827Z","0.3.2-progress-report.20180712205814":"2018-07-12T21:01:40.055Z","0.3.2-progress-report.20180712212457":"2018-07-12T21:28:26.917Z","0.3.2-progress-report.20180712213419":"2018-07-12T21:37:36.673Z","0.3.2-progress-report.20180712215649":"2018-07-12T22:00:08.913Z","0.3.2-progress-report.20180712224306":"2018-07-12T22:46:40.413Z","0.3.2-20180712231942":"2018-07-12T23:23:25.328Z","0.3.2-progress-report.20180712232213":"2018-07-12T23:25:39.104Z","0.3.2-progress-report.20180712232736":"2018-07-12T23:31:07.929Z","0.3.2-rename-listener.20180713021926":"2018-07-13T02:22:42.214Z","0.3.2-20180713054022":"2018-07-13T05:43:49.140Z","0.3.2-command2.20180713065152":"2018-07-13T06:55:07.790Z","0.3.2-progress-report.20180713074226":"2018-07-13T07:45:58.303Z","0.3.2-command2.20180713074528":"2018-07-13T07:49:07.332Z","0.3.2-20180713075016":"2018-07-13T07:53:55.500Z","0.3.2-20180713081422":"2018-07-13T08:18:01.493Z","0.3.2-progress-report.20180713165947":"2018-07-13T17:03:14.148Z","0.3.2-progress-report.20180715094910":"2018-07-15T09:53:20.975Z","0.3.2-progress-report.20180715095014":"2018-07-15T09:54:28.853Z","0.3.2-progress-report.20180715095023":"2018-07-15T09:54:42.237Z","0.3.2-progress-report.20180715101132":"2018-07-15T10:14:53.479Z","0.3.2-progress-report.20180715101233":"2018-07-15T10:15:52.713Z","0.3.2-progress-report.20180715101232":"2018-07-15T10:16:14.411Z","0.3.2-issue-456.20180715211127":"2018-07-15T21:14:38.543Z","0.3.2-20180715223627":"2018-07-15T22:39:41.198Z","0.3.2-20180716044057":"2018-07-16T04:44:10.213Z","0.3.2-20180716074440":"2018-07-16T07:47:10.285Z","0.3.2-20180716080725":"2018-07-16T08:10:34.894Z","0.3.2-laz.20180718005131":"2018-07-18T00:54:55.637Z","0.3.2-laz.20180718010840":"2018-07-18T01:12:03.581Z","0.3.2-20180718011920":"2018-07-18T01:23:32.356Z","0.3.2-20180718025643":"2018-07-18T02:59:59.372Z","0.3.2-pm-analyzer.20180718085338":"2018-07-18T08:56:52.216Z","0.3.2-20180718223432":"2018-07-18T22:37:42.240Z","0.3.2-20180719073654":"2018-07-19T07:39:46.522Z","0.3.2-store-push.20180720060041":"2018-07-20T06:03:53.660Z","0.3.2-store-push.20180720070631":"2018-07-20T07:09:02.778Z","0.3.2-20180720074808":"2018-07-20T07:51:10.338Z","0.3.2-20180722233612":"2018-07-22T23:39:35.353Z","0.3.2-20180723055531":"2018-07-23T05:58:45.192Z","0.3.2-20180724035903":"2018-07-24T04:01:50.275Z","0.3.2-20180724040432":"2018-07-24T04:08:03.943Z","0.3.2-20180724061156":"2018-07-24T06:15:23.369Z","0.3.2-goal-contributor-names.20180724150634":"2018-07-24T15:10:40.569Z","0.3.2-20180724205737":"2018-07-24T21:01:12.478Z","0.3.2-project-selector.20180724231905":"2018-07-24T23:22:35.909Z","0.3.2-20180724232530":"2018-07-24T23:29:08.153Z","0.3.2-project-selector.20180724233536":"2018-07-24T23:39:17.131Z","0.3.2-project-selector.20180725013924":"2018-07-25T01:42:46.631Z","0.3.2-project-selector.20180725023436":"2018-07-25T02:38:12.307Z","0.3.2-20180725024250":"2018-07-25T02:46:31.465Z","0.3.2-20180725024434":"2018-07-25T02:47:57.830Z","0.3.2-20180725124734":"2018-07-25T12:50:41.638Z","0.3.2-20180725140911":"2018-07-25T14:12:33.702Z","0.3.2-20180725153700":"2018-07-25T15:40:54.190Z","0.3.2-20180726115716":"2018-07-26T12:07:53.382Z","0.3.2-20180726152101":"2018-07-26T15:24:43.277Z","0.3.2-goal-approval.20180726204640":"2018-07-26T20:49:51.019Z","0.3.2-goal-approval.20180726213627":"2018-07-26T21:39:48.596Z","0.3.2-goal-approval.20180726220957":"2018-07-26T22:13:12.746Z","0.3.2-20180727002548":"2018-07-27T00:29:15.010Z","0.3.2-goal-status.20180727014407":"2018-07-27T01:46:52.183Z","0.3.2-goal-status.20180727015906":"2018-07-27T02:03:07.509Z","0.3.2-goal-status.20180727020042":"2018-07-27T02:04:05.501Z","0.3.2-goal-status.20180727023826":"2018-07-27T02:41:10.780Z","0.3.2-20180727025011":"2018-07-27T02:53:54.017Z","0.3.2-goal-approval.20180727043112":"2018-07-27T04:33:51.058Z","0.3.2-goal-approval.20180727053802":"2018-07-27T05:41:13.976Z","0.3.2-goal-approval.20180727065857":"2018-07-27T07:01:44.481Z","0.3.2-20180727070836":"2018-07-27T07:11:17.976Z","0.3.2-20180727072507":"2018-07-27T07:28:20.649Z","0.3.2-20180727074150":"2018-07-27T07:45:06.669Z","0.3.2-20180727082021":"2018-07-27T08:23:56.556Z","0.3.2-20180727121721":"2018-07-27T12:33:18.040Z","0.3.2-20180727125411":"2018-07-27T12:57:21.875Z","0.3.2-20180728015553":"2018-07-28T01:59:18.964Z","0.3.2-nortissej.nortissej.elide-axios-response.20180728202703":"2018-07-28T20:29:50.029Z","0.3.2-review-listener-name.20180729003330":"2018-07-29T00:36:36.298Z","0.3.2-20180729004006":"2018-07-29T00:42:39.578Z","0.3.2-20180729004819":"2018-07-29T00:51:25.930Z","0.3.2-20180729224902":"2018-07-29T22:52:15.417Z","0.3.2-20180729225208":"2018-07-29T22:55:12.182Z","0.3.2-20180730003728":"2018-07-30T00:40:40.988Z","0.3.2-20180730010005":"2018-07-30T01:03:25.074Z","0.3.2-seal-goals.20180731034437":"2018-07-31T03:51:26.846Z","0.3.2-20180731092523":"2018-07-31T09:28:36.141Z","0.3.2-goal-lock.20180731103853":"2018-07-31T10:42:08.203Z","0.3.2-20180731124532":"2018-07-31T12:48:56.011Z","0.3.2-goal-lock.20180731224349":"2018-07-31T22:57:22.329Z","0.3.2-goal-lock.20180801010341":"2018-08-01T01:07:15.001Z","0.3.2-20180801010455":"2018-08-01T01:08:28.532Z","0.3.2-20180801093406":"2018-08-01T09:37:23.874Z","0.3.2-lock2.20180801100043":"2018-08-01T10:04:12.161Z","0.3.2-20180801104145":"2018-08-01T10:45:04.739Z","0.3.2-20180801120305":"2018-08-01T12:06:24.628Z","0.3.2-20180801121109":"2018-08-01T12:14:36.676Z","0.4.0-20180803134505":"2018-08-03T13:48:54.456Z","0.4.0-nortissej.default-transform-reaction.20180803220626":"2018-08-03T22:09:18.716Z","0.4.0-nortissej.fix-transform-all.20180803231953":"2018-08-03T23:23:15.486Z","0.4.0-20180803232455":"2018-08-03T23:28:16.232Z","0.4.0-nortissej.default-transform-reaction.20180803233032":"2018-08-03T23:33:57.359Z","0.4.0-nortissej.default-transform-reaction.20180803233624":"2018-08-03T23:39:52.618Z","0.4.0-20180807022357":"2018-08-07T02:27:17.158Z","0.4.0-20180807022858":"2018-08-07T02:32:26.277Z","0.4.0-20180807090053":"2018-08-07T09:04:12.416Z","0.4.0-20180807113905":"2018-08-07T11:43:49.876Z","0.4.0":"2018-08-07T11:55:59.564Z","0.4.1-20180807115715":"2018-08-07T12:11:27.128Z","0.4.1-20180809172213":"2018-08-09T17:25:44.870Z","0.4.1":"2018-08-09T17:28:01.865Z","0.4.2-20180809172928":"2018-08-09T17:33:14.488Z","0.4.2-20180809210204":"2018-08-09T21:05:45.951Z","0.4.2-forward.20180811233712":"2018-08-11T23:39:57.461Z","0.4.2-forward.20180812174441":"2018-08-12T17:47:56.226Z","0.4.2-forward.20180812180244":"2018-08-12T18:06:05.323Z","0.4.2-forward.20180812190256":"2018-08-12T19:06:15.004Z","0.4.2-20180813031424":"2018-08-13T03:17:38.509Z","0.4.2-invariant.20180814001448":"2018-08-14T00:18:17.433Z","0.4.2-logplus.20180814004022":"2018-08-14T00:43:50.916Z","0.4.2-20180814004316":"2018-08-14T00:46:42.440Z","0.5.0-indexes.20180814083803":"2018-08-14T08:41:34.707Z","0.4.2-nortissej.valid-branch-name.20180815222149":"2018-08-15T22:24:53.005Z","0.4.2-20180815224143":"2018-08-15T22:45:06.425Z","0.4.2-20180815230951":"2018-08-15T23:12:51.042Z","0.4.2-20180815231715":"2018-08-15T23:20:57.424Z","0.4.2-20180817205802":"2018-08-17T21:01:48.538Z","0.4.2":"2018-08-17T21:48:58.988Z","0.4.3-20180817215044":"2018-08-17T21:53:58.624Z","0.4.2-invariant.20180818184529":"2018-08-18T18:48:24.979Z","0.4.3-20180821132724":"2018-08-21T13:29:55.207Z","0.4.3-20180821142132":"2018-08-21T14:24:10.597Z","0.4.3":"2018-08-21T14:25:22.322Z","0.4.4-20180821142646":"2018-08-21T14:29:14.325Z","0.4.4-20180822160505":"2018-08-22T16:07:37.128Z","0.4.4":"2018-08-22T16:08:53.299Z","0.4.5-20180822161007":"2018-08-22T16:12:41.416Z","0.4.5-20180822172253":"2018-08-22T17:26:06.220Z","0.4.5-20180822212440":"2018-08-22T21:28:37.420Z","0.4.5-20180822213544":"2018-08-22T21:38:19.629Z","0.4.5-20180822213720":"2018-08-22T21:40:00.292Z","0.4.5-20180822214349":"2018-08-22T21:46:33.861Z","0.4.5-20180822214840":"2018-08-22T21:51:44.486Z","0.4.5-20180823082746":"2018-08-23T08:30:13.171Z","0.4.5-20180823094122":"2018-08-23T09:43:40.302Z","0.4.5-20180823100141":"2018-08-23T10:04:24.062Z","0.4.5-20180823100916":"2018-08-23T10:12:42.209Z","0.4.5":"2018-08-23T10:20:56.955Z","0.4.6-20180823102228":"2018-08-23T10:26:25.719Z","0.4.6-nortissej.cleanup-log.20180823175234":"2018-08-23T17:55:54.358Z","0.4.6-20180823202257":"2018-08-23T20:25:49.248Z","0.4.6-20180823211528":"2018-08-23T21:18:38.645Z","0.4.6-20180823211731":"2018-08-23T21:20:47.529Z","0.4.6-20180823213826":"2018-08-23T21:42:01.843Z","0.4.6":"2018-08-24T04:48:06.067Z","0.4.7-20180824044915":"2018-08-24T04:52:25.943Z","0.4.7-20180824050851":"2018-08-24T05:11:30.377Z","0.4.7-20180824051434":"2018-08-24T05:17:26.567Z","0.4.7":"2018-08-24T05:29:57.650Z","0.4.8-20180824053120":"2018-08-24T05:34:19.520Z","0.4.6-nortissej.cleanup-log.20180824093414":"2018-08-24T09:36:34.334Z","0.4.8-20180824093654":"2018-08-24T09:39:26.320Z","0.4.8-nortissej.print-more-on-diff-error.20180825231317":"2018-08-25T23:15:44.760Z","0.4.8-nortissej.print-more-on-diff-error.20180825232717":"2018-08-25T23:29:44.679Z","0.4.8-20180826124729":"2018-08-26T12:50:19.056Z","0.4.8-20180826233743":"2018-08-26T23:40:28.978Z","0.4.8":"2018-08-27T04:14:49.112Z","0.4.9-20180827041548":"2018-08-27T04:18:53.983Z","1.0.0-20180827101919":"2018-08-27T10:22:19.928Z","1.0.0-atomist-update-next-1535365519905.20180827102529":"2018-08-27T10:27:53.020Z","1.0.0-20180827102904":"2018-08-27T10:31:40.490Z","1.0.0-M.1":"2018-08-27T10:37:33.641Z","1.0.0-invariant.20180828232825":"2018-08-28T23:31:00.931Z","1.0.0-invariant.20180828235510":"2018-08-28T23:57:46.342Z","1.0.0-invariant.20180829001446":"2018-08-29T00:17:17.053Z","1.0.0-master.20180829051001":"2018-08-29T05:12:50.721Z","1.0.0-master.20180829090751":"2018-08-29T09:13:25.899Z","1.0.0-master.20180829151906":"2018-08-29T15:24:25.226Z","1.0.0-review.20180830044852":"2018-08-30T04:51:32.212Z","1.0.0-review.20180830050717":"2018-08-30T05:09:52.510Z","1.0.0-goal-implementation-selection.20180830080444":"2018-08-30T08:07:20.110Z","1.0.0-review.20180830083524":"2018-08-30T08:38:02.733Z","1.0.0-master.20180830083824":"2018-08-30T08:41:19.582Z","1.0.0-goal-implementation-selection.20180830083958":"2018-08-30T08:42:32.189Z","1.0.0-goal-implementation-selection.20180830084832":"2018-08-30T08:51:03.451Z","1.0.0-spring-initializr-generator.20180830085419":"2018-08-30T08:57:00.369Z","1.0.0-goal-implementation-selection.20180830085631":"2018-08-30T08:58:52.754Z","1.0.0-goal-implementation-selection.20180830085850":"2018-08-30T09:01:53.202Z","1.0.0-master.20180830085826":"2018-08-30T09:02:24.258Z","1.0.0-goal-implementation-selection.20180830090216":"2018-08-30T09:04:22.936Z","1.0.0-master.20180830092848":"2018-08-30T09:31:25.625Z","1.0.0-spring-initializr-generator.20180830095355":"2018-08-30T09:56:43.042Z","1.0.0-master.20180830102832":"2018-08-30T10:31:23.399Z","1.0.0-master.20180830103116":"2018-08-30T10:34:07.144Z","1.0.0-spring-initializr-generator.20180830112324":"2018-08-30T11:26:10.317Z","1.0.0-master.20180830163028":"2018-08-30T16:33:22.658Z","1.0.0-master.20180830204234":"2018-08-30T20:45:23.627Z","1.0.0-nortissej.update-print.20180830210428":"2018-08-30T21:07:03.192Z","1.0.0-nortissej.update-print.20180830211058":"2018-08-30T21:13:36.144Z","1.0.0-master.20180830211714":"2018-08-30T21:20:04.322Z","1.0.0-push-pull-496.20180830214549":"2018-08-30T21:48:17.411Z","1.0.0-master.20180830215540":"2018-08-30T21:58:30.407Z","1.0.0-nortissej.print-stack.20180831045646":"2018-08-31T04:59:20.383Z","1.0.0-nortissej.print-stack.20180831051129":"2018-08-31T05:13:59.809Z","1.0.0-master.20180831051425":"2018-08-31T05:17:12.960Z","1.0.0-master.20180831092458":"2018-08-31T09:27:49.305Z","1.0.0-master.20180831123943":"2018-08-31T12:42:50.850Z","1.0.0-nortissej.first-push.20180831205527":"2018-08-31T20:58:01.798Z","1.0.0-nortissej.observesOnly.20180831220937":"2018-08-31T22:12:00.714Z","1.0.0-nortissej.first-push.20180831225158":"2018-08-31T22:54:26.644Z","1.0.0-master.20180901020009":"2018-09-01T02:02:40.439Z","1.0.0-transform-transform-testNaming-1535773767577.20180901034944":"2018-09-01T03:52:18.756Z","1.0.0-blog2.20180901042134":"2018-09-01T04:24:28.691Z","1.0.0-blog2.20180901042248":"2018-09-01T04:25:34.119Z","1.0.0-master.20180901092113":"2018-09-01T09:24:01.404Z","1.0.0-master.20180901121655":"2018-09-01T18:21:38.124Z","1.0.0-nortissej.observesOnly.20180901184855":"2018-09-01T18:51:25.573Z","1.0.0-master.20180901185236":"2018-09-01T18:55:03.068Z","1.0.0-nortissej.remove-deprecation-from-client.20180901190705":"2018-09-01T19:09:13.290Z","1.0.0-master.20180901214816":"2018-09-01T21:50:41.332Z","1.0.0-richer-goals.20180902005722":"2018-09-02T00:59:50.979Z","1.0.0-richer-goals.20180902013119":"2018-09-02T01:33:59.032Z","1.0.0-richer-goals.20180902103109":"2018-09-02T10:33:13.609Z","1.0.0-richer-goals.20180902110454":"2018-09-02T11:07:08.725Z","1.0.0-richer-goals.20180902112126":"2018-09-02T11:23:33.405Z","1.0.0-richer-goals.20180902113407":"2018-09-02T11:36:02.489Z","1.0.0-richer-goals.20180902121350":"2018-09-02T12:16:27.206Z","1.0.0-richer-goals.20180902165419":"2018-09-02T16:56:31.287Z","1.0.0-richer-goals.20180902181556":"2018-09-02T18:18:24.068Z","1.0.0-nortissej.first-push.20180902192959":"2018-09-02T19:32:33.001Z","1.0.0-richer-goals.20180902193803":"2018-09-02T19:40:19.438Z","1.0.0-master.20180902203942":"2018-09-02T20:42:22.639Z","1.0.0-spring-initializr-generator.20180902214700":"2018-09-02T21:49:03.433Z","1.0.0-master.20180902224739":"2018-09-02T22:50:00.206Z","1.0.0-spring-initializr-generator.20180902225703":"2018-09-02T22:59:26.463Z","1.0.0-spring-initializr-generator.20180902225953":"2018-09-02T23:02:02.900Z","1.0.0-spring-initializr-generator.20180902230714":"2018-09-02T23:09:57.926Z","1.0.0-master.20180902231058":"2018-09-02T23:13:50.959Z","1.0.0-master.20180902232137":"2018-09-02T23:23:49.653Z","1.0.0-master.20180902233103":"2018-09-02T23:33:52.375Z","1.0.0-master.20180903050226":"2018-09-03T05:05:11.353Z","1.0.0-typed-build-goal.20180903073906":"2018-09-03T07:41:14.813Z","1.0.0-master.20180903074915":"2018-09-03T07:51:59.292Z","1.0.0-master.20180903095316":"2018-09-03T09:53:57.513Z","1.0.0-goal-names.20180903112552":"2018-09-03T11:27:43.745Z","1.0.0-goal-names.20180903124928":"2018-09-03T12:51:26.101Z","1.0.0-goal-names.20180903130920":"2018-09-03T13:11:05.237Z","1.0.0-goal-names.20180903133924":"2018-09-03T13:42:34.872Z","1.0.0-goal-names.20180903160605":"2018-09-03T16:29:48.668Z","1.0.0-master.20180903162723":"2018-09-03T16:30:14.816Z","1.0.0-goal-names.20180903172102":"2018-09-03T17:25:36.993Z","1.0.0-master.20180903173217":"2018-09-03T17:35:30.853Z","1.0.0-goal-names.20180903175029":"2018-09-03T17:52:57.625Z","1.0.0-goal-names.20180903175118":"2018-09-03T18:03:41.609Z","1.0.0-master.20180903201422":"2018-09-03T20:20:16.969Z","1.0.0-master.20180903202920":"2018-09-03T20:32:58.330Z","1.0.0-master.20180903205421":"2018-09-03T21:05:14.455Z","1.0.0-goal-cleanup.20180903214357":"2018-09-03T21:46:51.755Z","1.0.0-goal-cleanup.20180903221226":"2018-09-03T22:15:10.737Z","1.0.0-goal-cleanup.20180903224934":"2018-09-03T22:51:41.548Z","1.0.0-master.20180903225558":"2018-09-03T22:59:02.244Z","1.0.0-master.20180903235234":"2018-09-03T23:55:24.310Z","1.0.0-master.20180904000529":"2018-09-04T00:08:16.180Z","1.0.0-master.20180904005606":"2018-09-04T01:00:30.575Z","1.0.0-master.20180904101721":"2018-09-04T10:19:23.238Z","1.0.0-transform-result.20180904124741":"2018-09-04T13:02:04.043Z","1.0.0-master.20180904134435":"2018-09-04T13:56:32.067Z","1.0.0-atomist-update-1.0.0-M.3-1536072573431.20180904144941":"2018-09-04T15:07:12.704Z","1.0.0-master.20180904150844":"2018-09-04T15:21:19.356Z","1.0.0-M.3":"2018-09-04T15:23:42.489Z","1.0.0-master.20180904201909":"2018-09-04T20:22:09.440Z","1.0.0-master.20180904211628":"2018-09-04T21:28:49.944Z","1.0.0-master.20180905004040":"2018-09-05T00:43:40.428Z","1.0.0-clone-depth.20180905084253":"2018-09-05T08:44:58.679Z","1.0.0-clone-depth.20180905205613":"2018-09-05T20:58:31.578Z","1.0.0-clone-depth.20180906155733":"2018-09-06T16:00:21.321Z","1.0.0-clone-depth.20180906165909":"2018-09-06T17:01:42.061Z","1.0.0-clone-depth.20180906204847":"2018-09-06T20:51:32.495Z","1.0.0-clone-depth.20180906212149":"2018-09-06T21:24:23.953Z","1.0.0-master.20180906213037":"2018-09-06T21:33:41.009Z","1.0.0-nortissej.default-transform-result.20180906223648":"2018-09-06T22:39:02.818Z","1.0.0-master.20180907090021":"2018-09-07T09:02:59.222Z","1.0.0-master.20180907090623":"2018-09-07T09:09:17.788Z","1.0.0-nortissej.default-transform-result.20180907220813":"2018-09-07T22:11:01.560Z","1.0.0-nortissej.push-impact-listener.20180907221954":"2018-09-07T22:23:00.122Z","1.0.0-master.20180907223209":"2018-09-07T22:35:24.242Z","1.0.0-nortissej.upclient.20180907224944":"2018-09-07T22:52:36.971Z","1.0.0-master.20180907234351":"2018-09-07T23:47:10.418Z","1.0.0-master.20180908002213":"2018-09-08T00:25:42.107Z","1.0.0-context-created-event.20180908003259":"2018-09-08T00:35:42.833Z","1.0.0-context-created-event.20180908014511":"2018-09-08T01:47:55.511Z","1.0.0-context-created-event.20180908020015":"2018-09-08T02:02:58.983Z","1.0.0-nortissej.default-transform-result.20180908020233":"2018-09-08T02:04:49.196Z","1.0.0-context-created-event.20180908023334":"2018-09-08T02:36:12.322Z","1.0.0-nortissej.default-transform-result.20180908160252":"2018-09-08T16:05:16.478Z","1.0.0-context-created-event.20180908172152":"2018-09-08T17:24:41.801Z","1.0.0-context-created-event.20180908172754":"2018-09-08T17:30:51.494Z","1.0.0-deprecate-global.20180908175912":"2018-09-08T18:06:36.310Z","1.0.0-fingerprintarray.20180908184713":"2018-09-08T18:50:20.937Z","1.0.0-master.20180908185452":"2018-09-08T18:58:53.083Z","1.0.0-master.20180909022053":"2018-09-09T02:24:11.877Z","1.0.0-master.20180909085133":"2018-09-09T08:54:48.284Z","1.0.0-context-created-event.20180909085220":"2018-09-09T08:55:11.636Z","1.0.0-context-created-event.20180909090246":"2018-09-09T09:05:26.038Z","1.0.0-deprecate-global.20180909093829":"2018-09-09T09:41:06.270Z","1.0.0-deprecate-global.20180909094050":"2018-09-09T09:43:24.599Z","1.0.0-context-created-event.20180909094224":"2018-09-09T09:45:12.843Z","1.0.0-master.20180909192848":"2018-09-09T19:32:15.564Z","1.0.0-master.20180909194634":"2018-09-09T19:49:31.567Z","1.0.0-master.20180909201953":"2018-09-09T20:22:57.943Z","1.0.0-master.20180909203432":"2018-09-09T20:37:37.463Z","1.0.0-master.20180909205905":"2018-09-09T21:04:27.109Z","1.0.0-master.20180909210749":"2018-09-09T21:11:05.472Z","1.0.0-deprecate-global.20180910065534":"2018-09-10T06:58:09.579Z","1.0.0-master.20180910070004":"2018-09-10T07:03:27.348Z","1.0.0-clone-options.20180910185946":"2018-09-10T19:02:43.112Z","1.0.0-nortissej.autofix-warning.20180910203230":"2018-09-10T20:35:27.331Z","1.0.0-nortissej.autofix-warning.20180910210323":"2018-09-10T21:06:08.113Z","1.0.0-master.20180910213210":"2018-09-10T21:35:06.127Z","1.0.0-master.20180911004414":"2018-09-11T00:47:17.941Z","1.0.0-nortissej.checkout-branch.20180911070300":"2018-09-11T07:07:09.635Z","1.0.0-nortissej.checkout-branch.20180911074648":"2018-09-11T08:23:52.881Z","1.0.0-nortissej.checkout-branch.20180911091518":"2018-09-11T09:28:01.569Z","1.0.0-nortissej.checkout-branch.20180911093258":"2018-09-11T09:42:27.830Z","1.0.0-master.20180911102033":"2018-09-11T10:24:07.443Z","1.0.0-master.20180913074630":"2018-09-13T07:50:21.083Z","1.0.0-master.20180913101258":"2018-09-13T10:15:41.859Z","1.0.0-atomist-update-branch-master-1536845873907.20180913140545":"2018-09-13T14:08:45.651Z","1.0.0-update-latest-master.20180913151805":"2018-09-13T15:21:03.042Z","1.0.0-update-latest-master.20180913155945":"2018-09-13T16:02:40.981Z","1.0.0-update-latest-master.20180913201907":"2018-09-13T20:22:05.127Z","1.0.0-update-latest-master.20180913210549":"2018-09-13T21:08:54.369Z","1.0.0-master.20180913213812":"2018-09-13T21:41:34.169Z","1.0.0-update-latest-master.20180913221116":"2018-09-13T22:14:47.138Z","1.0.0-update-latest-master.20180914075429":"2018-09-14T07:57:21.316Z","1.0.0-master.20180914083639":"2018-09-14T08:52:44.604Z","1.0.0-master.20180914100050":"2018-09-14T10:03:58.690Z","1.0.0-master.20180914100848":"2018-09-14T10:11:56.462Z","1.0.0-master.20180914101315":"2018-09-14T10:17:01.425Z","1.0.0-retry-docker-builds.20180914105128":"2018-09-14T10:54:48.456Z","1.0.0-master.20180914130235":"2018-09-14T13:07:26.206Z","1.0.0-master.20180914132622":"2018-09-14T13:30:10.657Z","1.0.0-master.20180914145042":"2018-09-14T14:54:48.160Z","1.0.0-master.20180914155617":"2018-09-14T16:00:09.671Z","1.0.0-master.20180914160741":"2018-09-14T16:11:37.436Z","1.0.0-master.20180915075111":"2018-09-15T07:54:53.874Z","1.0.0-atomist-update-1.0.0-M.4-1537095659873.20180916110109":"2018-09-16T11:04:22.108Z","1.0.0-atomist-update-1.0.0-1537095919244.20180916110528":"2018-09-16T11:09:07.059Z","1.0.0-master.20180916110551":"2018-09-16T11:10:43.657Z","1.0.0-master.20180916111029":"2018-09-16T11:15:00.732Z","1.0.0-M.4":"2018-09-16T11:43:07.301Z","1.0.0-master.20180918120220":"2018-09-18T12:05:43.391Z","1.0.0-master.20180918121412":"2018-09-18T12:17:28.326Z","1.0.0-autoinspect.20180918230735":"2018-09-18T23:10:57.173Z","1.0.0-master.20180919001918":"2018-09-19T00:22:39.345Z","1.0.0-master.20180919030855":"2018-09-19T03:12:10.736Z","1.0.0-master.20180919071807":"2018-09-19T07:21:57.612Z","1.0.0-master.20180919072129":"2018-09-19T07:25:30.508Z","1.0.0-master.20180919081349":"2018-09-19T08:17:32.834Z","1.0.0-new-goal-states.20180920085246":"2018-09-20T08:56:10.489Z","1.0.0-new-goal-states.20180920112504":"2018-09-20T11:28:28.086Z","1.0.0-atomist-update-branch-master-1537443790870.20180920114318":"2018-09-20T11:47:56.711Z","1.0.0-master.20180920114937":"2018-09-20T11:53:58.018Z","1.0.0-new-goal-states.20180920125024":"2018-09-20T12:54:01.440Z","1.0.0-master.20180920125425":"2018-09-20T12:58:49.765Z","1.0.0-master.20180920130138":"2018-09-20T13:05:27.392Z","1.0.0-master.20180920152437":"2018-09-20T15:28:44.989Z","1.0.0-code-project-hooks.20180922193416":"2018-09-22T19:37:43.705Z","1.0.0-code-project-hooks.20180922200631":"2018-09-22T20:09:35.704Z","1.0.0-code-project-hooks.20180922210120":"2018-09-22T21:04:51.244Z","1.0.0-code-project-hooks.20180923150236":"2018-09-23T15:05:36.341Z","1.0.0-code-project-hooks.20180924063742":"2018-09-24T06:40:30.393Z","1.0.0-code-project-hooks.20180924071955":"2018-09-24T07:23:10.598Z","1.0.0-code-project-hooks.20180924072514":"2018-09-24T07:28:18.581Z","1.0.0-code-project-hooks.20180924084015":"2018-09-24T08:43:34.018Z","1.0.0-code-project-hooks.20180924084400":"2018-09-24T08:46:52.257Z","1.0.0-code-project-hooks.20180924085548":"2018-09-24T08:58:51.209Z","1.0.0-atomist-update-branch-master-1537825734574.20180924214903":"2018-09-24T21:52:24.314Z","1.0.0-master.20180924215352":"2018-09-24T21:57:32.056Z","1.0.0-code-project-hooks.20180925192043":"2018-09-25T19:24:04.074Z","1.0.0-code-project-hooks.20180925195238":"2018-09-25T19:56:02.175Z","1.0.0-code-project-hooks.20180925195558":"2018-09-25T19:59:27.651Z","1.0.0-code-project-hooks.20180925205747":"2018-09-25T21:00:55.906Z","1.0.0-pre-approval.20180925210943":"2018-09-25T21:12:39.783Z","1.0.0-pre-approval.20180925211450":"2018-09-25T21:17:39.217Z","1.0.0-pre-approval.20180926013753":"2018-09-26T01:41:02.758Z","1.0.0-pre-approval.20180926021752":"2018-09-26T02:21:10.317Z","1.0.0-master.20180926142938":"2018-09-26T14:32:43.557Z","1.0.0-master.20180926150603":"2018-09-26T15:09:32.721Z","1.0.0-master.20180926153733":"2018-09-26T15:46:50.193Z","1.0.0-pre-approval.20180926162300":"2018-09-26T16:26:36.962Z","1.0.0-atomist-update-1.0.0-M.5-1537987285510.20180926184133":"2018-09-26T18:46:09.642Z","1.0.0-master.20180926184253":"2018-09-26T18:46:57.928Z","1.0.0-master.20180926184746":"2018-09-26T18:51:19.920Z","1.0.0-atomist-update-1.0.0-1537989599984.20180926192011":"2018-09-26T19:23:59.518Z","1.0.0-master.20180926192524":"2018-09-26T19:29:10.472Z","1.0.0-M.5":"2018-09-26T19:46:37.052Z","1.0.0-master.20180927040520":"2018-09-27T04:09:12.649Z","1.0.0-master.20180927202548":"2018-09-27T20:29:07.569Z","1.0.0-master.20180927204822":"2018-09-27T20:51:43.506Z","1.0.0-master.20180927211538":"2018-09-27T21:18:58.054Z","1.0.0-master.20180928134252":"2018-09-28T13:45:37.961Z","1.0.0-callback-goal.20180928182322":"2018-09-28T18:26:25.565Z","1.0.0-callback-goal.20180929193408":"2018-09-29T19:36:48.571Z","1.0.0-callback-goal.20180929200616":"2018-09-29T20:09:13.063Z","1.0.0-master.20180929201807":"2018-09-29T20:21:18.098Z","1.0.0-remove-well-known-goals.20180929204153":"2018-09-29T20:44:39.431Z","1.0.0-master.20180930191921":"2018-09-30T19:22:17.407Z","1.0.0-master.20180930194455":"2018-09-30T19:47:58.597Z","1.0.0-master.20180930195234":"2018-09-30T19:55:40.378Z","1.0.0-master.20181001033214":"2018-10-01T03:35:09.045Z","1.0.0-master.20181001142547":"2018-10-01T14:28:55.841Z","1.0.0-master.20181001170815":"2018-10-01T17:11:13.709Z","1.0.0-master.20181001172107":"2018-10-01T17:24:06.091Z","1.0.0-master.20181001194516":"2018-10-01T19:48:17.245Z","1.0.0-master.20181002152828":"2018-10-02T15:31:51.100Z","1.0.0-issue-542.20181002222106":"2018-10-02T22:24:28.283Z","1.0.0-master.20181002231555":"2018-10-02T23:19:08.579Z","1.0.0-master.20181003213948":"2018-10-03T21:42:58.673Z","1.0.0-master.20181004153227":"2018-10-04T15:36:20.063Z","1.0.0-remove-global.20181006052454":"2018-10-06T05:28:14.343Z","1.0.0-master.20181006081131":"2018-10-06T08:14:49.107Z","1.0.0-master.20181006094538":"2018-10-06T09:49:13.682Z","1.0.0-master.20181006100238":"2018-10-06T10:05:58.324Z","1.0.0-master.20181007092752":"2018-10-07T09:31:11.000Z","1.0.0-master.20181007193107":"2018-10-07T19:34:35.710Z","1.0.0-master.20181007213741":"2018-10-07T21:40:43.645Z","1.0.0-update-master.20181008123217":"2018-10-08T12:35:18.885Z","1.0.0-automation-client-export-544.20181008132305":"2018-10-08T13:25:59.017Z","1.0.0-automation-client-export-544.20181008132956":"2018-10-08T13:33:11.797Z","1.0.0-remove-add-goal-sideeffect.20181008163725":"2018-10-08T16:40:54.335Z","1.0.0-remove-add-goal-sideeffect.20181008180252":"2018-10-08T18:06:48.485Z","1.0.0-master.20181008203922":"2018-10-08T20:42:37.125Z","1.0.0-master.20181008215342":"2018-10-08T21:56:56.875Z","1.0.0-automation-client-export-544.20181009011731":"2018-10-09T01:20:56.767Z","1.0.0-automation-client-export-544.20181009012548":"2018-10-09T01:28:59.887Z","1.0.0-master.20181009063318":"2018-10-09T06:36:17.523Z","1.0.0-master.20181009131042":"2018-10-09T13:14:58.999Z","1.0.0-master.20181009132540":"2018-10-09T13:29:53.596Z","1.0.0-master.20181009135628":"2018-10-09T14:00:53.685Z","1.0.0-master.20181010074610":"2018-10-10T07:50:04.287Z","1.0.0-master.20181010081849":"2018-10-10T08:22:12.480Z","1.0.0-master.20181010085046":"2018-10-10T08:54:14.483Z","1.0.0-master.20181010145707":"2018-10-10T15:00:19.544Z","1.0.0-master.20181014192008":"2018-10-14T19:23:00.292Z","1.0.0-atomist-update-branch-master-1539546589474.20181014195104":"2018-10-14T19:53:36.036Z","1.0.0-master.20181014195612":"2018-10-14T19:58:37.043Z","1.0.0-atomist-update-1.0.0-RC.1-1539603672076.20181015114213":"2018-10-15T11:44:45.537Z","1.0.0-atomist-update-1.0.0-1539603949460.20181015114652":"2018-10-15T11:49:19.033Z","1.0.0-master.20181015114715":"2018-10-15T11:50:06.987Z","1.0.0-master.20181015115148":"2018-10-15T11:54:15.357Z","1.0.0-RC.1":"2018-10-15T11:55:09.250Z","1.0.0-master.20181017081924":"2018-10-17T08:22:08.390Z","1.0.0-master.20181017121533":"2018-10-17T15:30:38.535Z","1.0.0-master.20181017215705":"2018-10-17T21:59:54.998Z","1.0.0-master.20181018153716":"2018-10-18T15:40:23.869Z","1.0.0-nortissej.apidoc-comment.20181019153307":"2018-10-19T15:36:15.244Z","1.0.0-nortissej.apidoc-comment.20181019164400":"2018-10-19T16:46:51.817Z","1.0.0-nogoals.20181021234208":"2018-10-21T23:45:15.807Z","1.0.0-master.20181022013021":"2018-10-22T01:33:27.721Z","1.0.0-nortissej.docs-only.20181022031406":"2018-10-22T03:16:51.666Z","1.0.0-issue-551.20181023092857":"2018-10-23T09:32:03.037Z","1.0.0-issue-551.20181023114228":"2018-10-23T11:45:45.642Z","1.0.0-issue-551.20181023145323":"2018-10-23T14:56:37.175Z","1.0.0-issue-551.20181023175727":"2018-10-23T18:00:24.267Z","1.0.0-master.20181024141507":"2018-10-24T14:18:34.849Z","1.0.0-pushtest-structure-more.20181025042300":"2018-10-25T04:25:40.268Z","1.0.0-pushtest-structure-more.20181025051523":"2018-10-25T05:18:14.455Z","1.0.0-master.20181025094406":"2018-10-25T09:47:21.576Z","1.0.0-master.20181025110208":"2018-10-25T11:04:49.146Z","1.0.0-goals-in-subscription.20181025144307":"2018-10-25T14:46:25.474Z","1.0.0-goals-in-subscription.20181025152318":"2018-10-25T15:26:05.915Z","1.0.0-goals-in-subscription.20181025155243":"2018-10-25T15:55:35.370Z","1.0.0-master.20181025155704":"2018-10-25T16:00:13.269Z","1.0.0-master.20181026095548":"2018-10-26T09:58:40.395Z","1.0.0-master.20181026131020":"2018-10-26T13:13:04.826Z","1.0.0-master.20181026131748":"2018-10-26T13:20:37.431Z","1.0.0-predicated-goal.20181028063641":"2018-10-28T06:39:12.345Z","1.0.0-pushtest-structure-more.20181028211828":"2018-10-28T21:20:57.804Z","1.0.0-master.20181029095411":"2018-10-29T09:56:34.221Z","1.0.0-master.20181029154613":"2018-10-29T15:49:01.262Z","1.0.0-commit-images-96.20181029175450":"2018-10-29T17:57:16.743Z","1.0.0-master.20181029215123":"2018-10-29T21:54:45.991Z","1.0.0-master.20181029215632":"2018-10-29T21:59:16.799Z","1.0.0-master.20181029230642":"2018-10-29T23:09:26.646Z","1.0.0-atomist-update-1.0.0-RC.2-1540910327337.20181030143950":"2018-10-30T14:42:36.475Z","1.0.0-master.20181030144507":"2018-10-30T14:48:19.821Z","1.0.0-RC.2":"2018-10-30T14:49:31.493Z","1.0.0-master.20181101044435":"2018-11-01T04:47:31.376Z","1.0.0-well-known-goals.20181101070159":"2018-11-01T07:04:37.031Z","1.0.0-master.20181101103750":"2018-11-01T10:40:34.235Z","1.0.0-selective.20181103034717":"2018-11-03T03:49:54.907Z","1.0.0-selective.20181103035613":"2018-11-03T03:58:57.036Z","1.0.0-master.20181105091644":"2018-11-05T09:19:33.812Z","1.0.0-master.20181105092518":"2018-11-05T09:28:18.409Z","1.0.0":"2018-11-05T10:37:58.350Z","1.0.1-master.20181105103846":"2018-11-05T10:42:34.747Z","1.0.0-master.20181105105216":"2018-11-05T10:55:24.716Z","1.0.0-master.20181105144545":"2018-11-05T14:49:09.074Z","1.0.0-master.20181105150406":"2018-11-05T15:06:55.369Z","1.0.0-master.20181106103407":"2018-11-06T10:37:10.762Z","1.0.0-master.20181106123058":"2018-11-06T12:34:15.512Z","1.0.0-master.20181106214407":"2018-11-06T21:47:17.170Z","1.0.0-master.20181107002813":"2018-11-07T00:31:24.776Z","1.0.0-master.20181107115558":"2018-11-07T11:58:46.125Z","1.0.0-master.20181107122754":"2018-11-07T12:31:54.469Z","1.0.0-master.20181107124111":"2018-11-07T12:43:56.674Z","1.0.0-master.20181107134130":"2018-11-07T13:44:43.435Z","1.0.0-master.20181107135131":"2018-11-07T13:54:48.620Z","1.0.0-master.20181107202213":"2018-11-07T20:25:48.642Z","1.0.0-promise-any.20181107234952":"2018-11-07T23:52:41.088Z","1.0.0-promise-any.20181108080819":"2018-11-08T08:11:06.075Z","1.0.0-promise-any.20181108081251":"2018-11-08T08:15:35.890Z","1.0.0-master.20181108084636":"2018-11-08T08:49:29.801Z","1.0.0-master.20181108102031":"2018-11-08T10:24:24.826Z","1.0.0-master.20181108102426":"2018-11-08T10:28:35.832Z","1.0.0-promise-any.20181108102545":"2018-11-08T10:29:51.099Z","1.0.0-master.20181108123227":"2018-11-08T12:35:20.748Z","1.0.0-master.20181109073515":"2018-11-09T07:37:59.352Z","1.0.1-master.20181109101137":"2018-11-09T10:14:20.120Z","1.0.1":"2018-11-09T10:22:46.490Z","1.0.2-cancel-goal.20181109210447":"2018-11-09T21:07:19.940Z","1.0.2-cancel-goal.20181109215556":"2018-11-09T21:58:46.616Z","1.0.2-cancel-goal.20181109221949":"2018-11-09T22:22:21.596Z","1.0.2-cancel-goal.20181109232037":"2018-11-09T23:23:10.957Z","1.0.2-cancel-goal.20181109233205":"2018-11-09T23:34:35.761Z","1.0.2-cancel-goal.20181109233711":"2018-11-09T23:39:41.777Z","1.1.0-master.20181110133644":"2018-11-10T13:39:17.905Z","1.1.0-cancel-goal.20181110134006":"2018-11-10T13:42:37.338Z","1.1.0-cancel-goal.20181112085621":"2018-11-12T08:58:54.215Z","1.1.0-cancel-goal.20181112135708":"2018-11-12T14:00:59.725Z","1.1.0-master.20181112135855":"2018-11-12T14:01:50.919Z","1.1.0-master.20181112163921":"2018-11-12T16:42:04.522Z","1.1.0-master.20181113110342":"2018-11-13T11:07:05.370Z","1.1.0-master.20181113110510":"2018-11-13T11:08:18.436Z","1.1.0-master.20181113165254":"2018-11-13T16:55:33.395Z","1.1.0-goal-review-listener.20181113195827":"2018-11-13T20:01:30.961Z","1.1.0-master.20181113220303":"2018-11-13T22:05:48.487Z","1.1.0-master.20181113220448":"2018-11-13T22:07:22.416Z","1.1.0-master.20181113220725":"2018-11-13T22:09:58.271Z","1.1.0-master.20181113223011":"2018-11-13T22:33:19.388Z","1.1.0-master.20181113225717":"2018-11-13T22:59:52.685Z","1.1.0-master.20181114125422":"2018-11-14T12:57:06.265Z","1.1.0-master.20181114141651":"2018-11-14T14:19:22.626Z","1.1.0-master.20181114154353":"2018-11-14T15:48:53.453Z","1.1.0-master.20181114165510":"2018-11-14T16:57:38.642Z","1.1.0-master.20181115095747":"2018-11-15T10:00:14.272Z","1.1.0-master.20181115110328":"2018-11-15T11:05:49.769Z","1.1.0-master.20181115112041":"2018-11-15T11:23:24.863Z","1.1.0-master.20181116234208":"2018-11-16T23:44:36.055Z","1.0.0-pushtest-structure-more.20181117002331":"2018-11-17T00:25:53.613Z","1.1.0-master.20181118082921":"2018-11-18T08:32:16.502Z","1.1.0-master.20181119070450":"2018-11-19T07:07:10.102Z","1.1.0-master.20181119140839":"2018-11-19T14:11:11.290Z","1.1.0-master.20181119142637":"2018-11-19T14:29:02.012Z","1.1.0-master.20181124164852":"2018-11-24T16:51:07.304Z","1.1.0-master.20181124165720":"2018-11-24T16:59:41.718Z","1.1.0-master.20181124172708":"2018-11-24T17:29:32.845Z","1.1.0-atomist-update-latest-1543307364811.20181127083331":"2018-11-27T08:38:54.167Z","1.1.0-master.20181127084510":"2018-11-27T08:49:34.590Z","1.1.0-child-process-575.20181127205825":"2018-11-27T21:01:15.014Z","1.1.0-child-process-575.20181128133819":"2018-11-28T13:40:39.516Z","1.1.0-master.20181128135825":"2018-11-28T14:01:38.206Z","1.1.0-master.20181128135959":"2018-11-28T14:03:05.739Z","1.1.0-optional-code.20181129141121":"2018-11-29T14:13:38.502Z","1.1.0-optional-code.20181129142328":"2018-11-29T14:26:05.843Z","1.1.0-optional-code.20181129142724":"2018-11-29T14:30:00.776Z","1.1.0-master.20181129143153":"2018-11-29T14:34:36.663Z","1.1.0-lr-polish.20181130030555":"2018-11-30T03:08:20.827Z","1.1.0-master.20181130062949":"2018-11-30T06:32:20.525Z","1.1.0-master.20181130091621":"2018-11-30T09:19:17.983Z","1.1.0-ro-array.20181130232751":"2018-11-30T23:30:40.315Z","1.1.0-master.20181201200312":"2018-12-01T20:05:47.552Z","1.1.0-triggered-listener.20181203220611":"2018-12-03T22:09:11.844Z","1.1.0-push-test-sequential.20181204173513":"2018-12-04T17:37:36.147Z","1.1.0-master.20181205060812":"2018-12-05T06:10:38.558Z","1.1.0-triggered-listener.20181205075941":"2018-12-05T08:02:33.142Z","1.1.0-master.20181205145517":"2018-12-05T14:58:11.263Z","1.1.0-master.20181205145708":"2018-12-05T15:00:00.839Z","1.1.0-master.20181205153758":"2018-12-05T15:40:42.479Z","1.1.0-goalset-state.20181205153823":"2018-12-05T15:41:26.254Z","1.1.0-goalset-state.20181205161400":"2018-12-05T16:16:58.192Z","1.1.0-goalset-state.20181205190934":"2018-12-05T19:12:28.150Z","1.1.0-goalset-state.20181205212718":"2018-12-05T21:30:09.464Z","1.1.0-goalset-state.20181206090736":"2018-12-06T09:10:23.480Z","1.1.0-goalset-state.20181206101139":"2018-12-06T10:15:20.142Z","1.1.0-goalset-state.20181206210725":"2018-12-06T21:10:47.006Z","1.1.0-atomist-update-branch-master-1544134392070.20181206221431":"2018-12-06T22:17:23.527Z","1.1.0-goalset-state.20181207082036":"2018-12-07T08:23:56.958Z","1.1.0-atomist-update-branch-master-1544181930259.20181207112641":"2018-12-07T11:29:22.942Z","1.1.0-atomist-update-branch-master-1544185950404.20181207123414":"2018-12-07T12:37:09.456Z","1.1.0-goalset-state.20181207124548":"2018-12-07T12:48:33.185Z","1.1.0-master.20181207130717":"2018-12-07T13:10:45.556Z","1.1.0-master.20181207130842":"2018-12-07T13:11:27.463Z","1.1.0-unknown-goal-error.20181207142836":"2018-12-07T14:31:59.460Z","1.1.0-master.20181207150021":"2018-12-07T15:03:08.495Z","1.1.0-master.20181207154036":"2018-12-07T15:43:32.796Z","1.1.0-master.20181208220140":"2018-12-08T22:04:38.479Z","1.1.0":"2018-12-08T22:09:35.886Z","1.1.1-master.20181210044844":"2018-12-10T04:51:42.721Z","1.1.1-master.20181210121555":"2018-12-10T12:18:58.933Z","1.1.1-master.20181211135728":"2018-12-11T14:00:27.115Z","1.1.1-code-transform-dry-run.20181211200115":"2018-12-11T20:04:15.984Z","1.1.1-code-transform-dry-run.20181211202316":"2018-12-11T20:26:16.282Z","1.1.1-code-transform-dry-run.20181211214559":"2018-12-11T21:49:06.986Z","1.1.1-master.20181211233651":"2018-12-11T23:40:00.615Z","1.1.1-with-repos.20181212053139":"2018-12-12T05:34:35.797Z","1.2.0-master.20181212094407":"2018-12-12T09:47:04.973Z","1.1.1-with-repos.20181212132523":"2018-12-12T13:28:22.584Z","1.1.1-with-repos.20181212172919":"2018-12-12T17:32:04.448Z","1.2.0-master.20181212222153":"2018-12-12T22:25:08.891Z","1.2.0-nortissej.pli.20181213055544":"2018-12-13T05:58:32.309Z","1.2.0-master.20181213080727":"2018-12-13T08:10:10.827Z","1.2.0-child-process-on-project.20181213085914":"2018-12-13T09:02:24.353Z","1.2.0-child-process-on-project.20181213145618":"2018-12-13T14:59:02.755Z","1.2.0-master.20181213154601":"2018-12-13T15:49:15.421Z","1.2.0-child-process-on-project-delog.20181213154618":"2018-12-13T15:49:36.412Z","1.2.0-child-process-on-project-delog.20181213170109":"2018-12-13T17:03:59.868Z","1.2.0-child-process-on-project.20181213170552":"2018-12-13T17:08:40.888Z","1.2.0-master.20181213171110":"2018-12-13T17:14:09.914Z","1.2.0-lazy-project-fix.20181213231834":"2018-12-13T23:21:34.266Z","1.2.0-master.20181214081302":"2018-12-14T08:27:07.305Z","1.2.0-master.20181214131108":"2018-12-14T13:13:56.565Z","1.2.0-master.20181214135136":"2018-12-14T13:54:25.636Z","1.2.0-lazy-project-fix.20181214165700":"2018-12-14T17:00:52.112Z","1.2.0-lazy-project-fix.20181214170715":"2018-12-14T17:10:07.620Z","1.2.0-master.20181214184627":"2018-12-14T18:49:52.639Z","1.2.0-master.20181214184807":"2018-12-14T18:51:12.795Z","1.2.0-master.20181215074737":"2018-12-15T07:50:29.424Z","1.2.0-master.20181216144121":"2018-12-16T14:44:45.527Z","1.2.0-compute-extr.20181217010313":"2018-12-17T01:06:23.729Z","1.2.0-compute-extr-jess.20181217012006":"2018-12-17T01:23:50.734Z","1.2.0-compute-extr-jess.20181217012345":"2018-12-17T01:27:02.567Z","1.2.0-compute-extr-jess.20181217015741":"2018-12-17T02:01:14.538Z","1.2.0-master.20181217135326":"2018-12-17T13:56:08.588Z","1.2.0-master.20181217165228":"2018-12-17T16:56:04.106Z","1.2.0-master.20181218005143":"2018-12-18T00:54:46.114Z","1.2.0-fact.20181218214318":"2018-12-18T21:46:29.126Z","1.2.0-fact.20181218220425":"2018-12-18T22:07:29.848Z","1.2.0-fact.20181218233453":"2018-12-18T23:38:02.901Z","1.2.0-master.20181219005648":"2018-12-19T01:00:04.910Z","1.2.0-master.20181219005843":"2018-12-19T01:01:37.317Z","1.2.0-master.20181219023620":"2018-12-19T02:39:21.893Z","1.2.0-master.20181219083048":"2018-12-19T08:33:53.712Z","1.2.0-master.20181219091010":"2018-12-19T09:13:08.766Z","1.2.0-targets-branch.20181220113940":"2018-12-20T11:42:53.494Z","1.2.0-targets-branch.20181220172547":"2018-12-20T17:28:42.014Z","1.2.0-master.20181220184732":"2018-12-20T18:50:58.109Z","1.2.0-code-transform-branch.20181221193253":"2018-12-21T19:35:44.283Z","1.2.0-master.20181222155954":"2018-12-22T16:02:58.995Z","1.2.0-master.20181222160121":"2018-12-22T16:04:12.810Z","1.2.0-nortissej.doc.20181222191227":"2018-12-22T19:15:25.099Z","1.2.0-master.20181223112642":"2018-12-23T11:29:58.142Z","1.2.0-setGoalsWhen.20181224014656":"2018-12-24T01:49:49.443Z","1.2.0-setGoalsWhen.20181224050605":"2018-12-24T05:08:59.022Z","1.2.0-setGoalsWhen.20181224052930":"2018-12-24T05:32:24.151Z","1.2.0-master.20181224082627":"2018-12-24T08:29:25.660Z","1.2.0-master.20181226045517":"2018-12-26T04:57:58.650Z","1.2.0-master.20181227211347":"2018-12-27T21:16:54.753Z","1.2.0-master.20181227213905":"2018-12-27T21:42:03.903Z","1.2.0-master.20181227220209":"2018-12-27T22:05:14.872Z","1.2.0":"2018-12-27T22:10:20.349Z","1.2.1-master.20181229020155":"2018-12-29T02:04:53.179Z","1.2.1-param-matching.20190102182225":"2019-01-02T18:25:17.961Z","1.2.1-nortissej.print-stderr.20190106022600":"2019-01-06T02:28:56.705Z","1.2.1-master.20190106171203":"2019-01-06T17:16:03.852Z","1.2.1-nortissej.populate-sha.20190106193518":"2019-01-06T19:38:19.844Z","1.2.1-master.20190106203935":"2019-01-06T20:42:07.253Z","1.2.1-master.20190107094905":"2019-01-07T09:51:55.765Z","1.2.1-master.20190107115548":"2019-01-07T11:58:34.058Z","1.2.1-param-matching.20190107120429":"2019-01-07T12:07:07.521Z","1.2.1-master.20190107130520":"2019-01-07T13:08:56.167Z","1.2.1-master.20190107152354":"2019-01-07T15:28:20.324Z","1.2.1-master.20190108095009":"2019-01-08T09:53:40.130Z","1.2.1-master.20190108111305":"2019-01-08T11:16:40.487Z","1.2.1-master.20190111162309":"2019-01-11T16:28:53.500Z","1.2.1-master.20190111162937":"2019-01-11T16:35:33.340Z","1.3.0-goal-launching.20190112115551":"2019-01-12T11:58:47.004Z","1.3.0-goal-launching.20190114154445":"2019-01-14T15:48:22.811Z","1.3.0-master.20190114164829":"2019-01-14T16:51:58.914Z","1.3.0-master.20190114212208":"2019-01-14T21:25:26.402Z","1.3.0-master.20190114224136":"2019-01-14T22:44:44.288Z","1.3.0-pref-store.20190115075711":"2019-01-15T08:00:17.308Z","1.3.0-pref-store.20190115091121":"2019-01-15T09:14:29.457Z","1.3.0-pref-store.20190115102339":"2019-01-15T10:27:08.420Z","1.3.0-pref-store.20190115170119":"2019-01-15T17:04:24.933Z","1.3.0-pref-store.20190115171938":"2019-01-15T17:22:51.192Z","1.3.0-master.20190115175702":"2019-01-15T18:01:01.045Z","1.3.0-master.20190115180107":"2019-01-15T18:04:12.875Z","1.3.0-master.20190115211119":"2019-01-15T21:14:34.303Z","1.3.0-master.20190115223640":"2019-01-15T22:39:47.916Z","1.3.0-master.20190117162257":"2019-01-17T16:26:32.592Z","1.3.0-master.20190119144619":"2019-01-19T14:50:04.339Z","1.3.0-master.20190119145642":"2019-01-19T14:59:49.542Z","1.3.0-master.20190120184307":"2019-01-20T18:46:53.337Z","1.3.0-master.20190121093726":"2019-01-21T09:40:38.377Z","1.3.0-master.20190122140855":"2019-01-22T14:12:39.502Z","1.3.0-master.20190122142907":"2019-01-22T14:33:14.009Z","1.3.0-dyn-parameter.20190123102941":"2019-01-23T10:33:15.927Z","1.3.0-master.20190123140058":"2019-01-23T14:04:10.917Z","1.3.0-dyn-parameter.20190123145143":"2019-01-23T14:55:14.365Z","1.3.0-master.20190123192146":"2019-01-23T19:25:43.407Z","1.3.0-context.20190124003249":"2019-01-24T00:36:10.563Z","1.3.0-context.20190124005641":"2019-01-24T00:59:52.407Z","1.3.0-context.20190124020424":"2019-01-24T02:07:53.483Z","1.3.0-master.20190124081217":"2019-01-24T08:15:43.972Z","1.3.0-master.20190124082220":"2019-01-24T08:25:34.804Z","1.3.0-master.20190124083739":"2019-01-24T08:40:48.093Z","1.3.0-master.20190124112759":"2019-01-24T11:31:30.043Z","1.3.0-master.20190124114347":"2019-01-24T11:47:17.747Z","1.3.0-master.20190124121344":"2019-01-24T12:17:15.460Z","1.3.0-master.20190124135729":"2019-01-24T14:01:00.519Z","1.3.0-master.20190124182949":"2019-01-24T18:33:07.222Z","1.3.0-master.20190124204210":"2019-01-24T20:45:28.712Z","1.3.0-master.20190125092715":"2019-01-25T09:30:29.770Z","1.3.0-master.20190125112534":"2019-01-25T11:28:49.875Z","1.3.0-master.20190125192906":"2019-01-25T19:32:13.597Z","1.3.0-master.20190126163729":"2019-01-26T16:40:36.005Z","1.3.0-master.20190127063950":"2019-01-27T06:43:22.035Z","1.3.0-master.20190127102926":"2019-01-27T10:32:23.025Z","1.3.0-master.20190127165149":"2019-01-27T16:55:31.476Z","1.3.0-master.20190128152439":"2019-01-28T15:27:47.505Z","1.3.0-master.20190128154917":"2019-01-28T15:52:57.118Z","1.3.0-master.20190129200809":"2019-01-29T20:11:41.902Z","1.3.0-master.20190129201953":"2019-01-29T20:23:27.576Z","1.3.0-master.20190129202210":"2019-01-29T20:25:10.773Z","1.3.0-master.20190129205714":"2019-01-29T21:00:16.818Z","1.3.0-nortissej.typedoc-transformPresentation.20190129220519":"2019-01-29T22:09:18.801Z","1.3.0-nortissej.onTransformResults.20190129232536":"2019-01-29T23:28:35.194Z","1.3.0-master.20190130172518":"2019-01-30T17:28:41.767Z","1.3.0-master.20190131081428":"2019-01-31T08:17:59.846Z","1.3.0-master.20190131081656":"2019-01-31T08:20:16.799Z","1.3.0-master.20190131082854":"2019-01-31T08:32:23.898Z","1.3.0-master.20190131083054":"2019-01-31T08:34:12.267Z","1.3.0-master.20190202151920":"2019-02-02T15:22:36.161Z","1.3.0-master.20190202163411":"2019-02-02T16:37:13.182Z","1.3.0-master.20190202171433":"2019-02-02T17:17:34.537Z","1.3.0-master.20190203161514":"2019-02-03T16:18:18.676Z","1.3.0-master.20190203165108":"2019-02-03T16:54:42.012Z","1.3.0-master.20190203165250":"2019-02-03T16:55:52.272Z","1.3.0-nortissej.progressLog-codeInspect.20190204203552":"2019-02-04T20:38:50.500Z","1.3.0-nortissej.progressLog-codeInspect.20190204211245":"2019-02-04T21:16:10.490Z","1.3.0-nortissej.progressLog-codeInspect.20190204214545":"2019-02-04T21:48:38.020Z","1.3.0-nortissej.progressLog-codeInspect.20190204221749":"2019-02-04T22:20:45.019Z","1.3.0-nortissej.onTransformResults.20190204223823":"2019-02-04T22:41:23.148Z","1.3.0-nortissej.onTransformResults.20190204225549":"2019-02-04T22:59:51.102Z","1.3.0-master.20190205121804":"2019-02-05T12:21:07.551Z","1.3.0-master.20190205230426":"2019-02-05T23:08:39.299Z","1.3.0-master.20190206111710":"2019-02-06T11:21:21.791Z","1.3.0-master.20190206171739":"2019-02-06T17:23:33.563Z","1.3.0-master.20190206205235":"2019-02-06T20:56:27.633Z","1.3.0-master.20190207212851":"2019-02-07T21:32:11.175Z","1.3.0-master.20190213001109":"2019-02-13T00:14:58.087Z","1.3.0-master.20190213150905":"2019-02-13T15:13:13.281Z","1.3.0-master.20190213165837":"2019-02-13T17:02:53.056Z","1.3.0-master.20190213165808":"2019-02-13T17:03:34.349Z","1.3.0-master.20190213170255":"2019-02-13T17:07:07.460Z","1.3.0-master.20190214083859":"2019-02-14T08:43:04.757Z","1.3.0-master.20190214094113":"2019-02-14T09:44:39.266Z","1.3.0-master.20190214121328":"2019-02-14T12:16:46.193Z","1.3.0-master.20190214192224":"2019-02-14T19:26:18.869Z","1.3.0-master.20190214193633":"2019-02-14T19:40:05.763Z","1.3.0-master.20190215112213":"2019-02-15T11:26:42.332Z","1.3.0-new-code-gen.20190215113440":"2019-02-15T11:38:14.453Z","1.3.0-new-code-gen.20190215122905":"2019-02-15T12:32:42.753Z","1.3.0-master.20190218084049":"2019-02-18T08:44:10.896Z","1.3.0-master.20190218090039":"2019-02-18T09:04:32.795Z","1.3.0-nortissej.accept-dot-extensions.20190219003021":"2019-02-19T00:35:01.838Z","1.3.0-master.20190219054656":"2019-02-19T05:50:27.020Z","1.3.0-master.20190219122245":"2019-02-19T12:27:25.396Z","1.3.0-ff.20190220085712":"2019-02-20T09:01:00.050Z","1.3.0-master.20190221190336":"2019-02-21T19:07:25.264Z","1.3.0-goal-signing.20190221190343":"2019-02-21T19:07:45.968Z","1.3.0-master.20190221190606":"2019-02-21T19:09:46.404Z","1.3.0-goal-signing.20190221193315":"2019-02-21T19:36:41.573Z","1.3.0-goal-signing.20190222073550":"2019-02-22T07:38:55.044Z","1.3.0-goal-signing.20190222131507":"2019-02-22T13:18:31.231Z","1.3.0-goal-signing.20190223211723":"2019-02-23T21:20:33.127Z","1.3.0-goal-signing.20190225074725":"2019-02-25T07:50:34.655Z","1.3.0-goal-signing.20190225081105":"2019-02-25T08:14:12.928Z","1.3.0-goal-signing.20190225082334":"2019-02-25T08:26:49.965Z","1.3.0-goal-signing.20190225083806":"2019-02-25T08:41:36.594Z","1.3.0-goal-signing.20190225114107":"2019-02-25T11:44:28.733Z","1.3.0-master.20190225134151":"2019-02-25T13:46:47.139Z","1.3.0-master.20190225134646":"2019-02-25T13:50:15.048Z","1.3.0-master.20190226083351":"2019-02-26T08:37:29.671Z","1.3.0-master.20190226090409":"2019-02-26T09:07:39.483Z","1.3.0-nortissej.addOptions.20190226180726":"2019-02-26T18:10:33.888Z","1.3.0-nortissej.addOptions.20190226182659":"2019-02-26T18:30:04.431Z","1.3.0-nortissej.addOptions.20190226201908":"2019-02-26T20:22:36.960Z","1.3.0-nortissej.addOptions.20190226220012":"2019-02-26T22:03:09.833Z","1.3.0-nortissej.addOptions.20190226220907":"2019-02-26T22:12:20.956Z","1.3.0-nortissej.addOptions.20190227162629":"2019-02-27T16:30:28.290Z","1.3.0-nortissej.addOptions.20190227202820":"2019-02-27T20:31:29.847Z","1.3.0-nortissej.addOptions.20190227210207":"2019-02-27T21:05:24.790Z","1.3.0-master.20190227211200":"2019-02-27T21:16:45.750Z","1.3.0-master.20190227213422":"2019-02-27T21:38:45.497Z","1.3.0-issue-682.20190228124705":"2019-02-28T12:50:38.297Z","1.3.0-master.20190228135053":"2019-02-28T13:55:11.859Z","1.3.0-master.20190228135408":"2019-02-28T13:57:22.784Z","1.3.0-atomist-update-branch-master-20190228135129.20190228135314":"2019-02-28T13:57:52.793Z","1.3.0-master.20190228135710":"2019-02-28T14:00:54.342Z","1.3.0-master.20190228140124":"2019-02-28T14:05:10.154Z","1.3.0-master.20190228212004":"2019-02-28T21:23:47.294Z","1.3.0-master.20190228213223":"2019-02-28T21:36:05.503Z","1.3.0-master.20190228214652":"2019-02-28T21:50:29.815Z","1.3.0-master.20190228215409":"2019-02-28T21:57:48.007Z","1.3.0-master.20190228220157":"2019-02-28T22:06:09.304Z","1.3.0-nortissej.apidoc.20190228221827":"2019-02-28T22:22:10.250Z","1.3.0-master.20190301193744":"2019-03-01T19:41:21.199Z","1.3.0-master.20190301225910":"2019-03-01T23:03:03.531Z","1.3.0-master.20190305064801":"2019-03-05T06:51:48.764Z","1.3.0-master.20190305073353":"2019-03-05T07:37:14.513Z","1.3.0-master.20190311140443":"2019-03-11T14:09:20.975Z","1.3.0-master.20190312121102":"2019-03-12T12:14:46.627Z","1.3.0-atomist-update-branch-master-20190313091831.20190313092013":"2019-03-13T09:24:05.042Z","1.3.0-atomist-update-branch-master-20190313114903.20190313115058":"2019-03-13T11:55:13.951Z","1.3.0-master.20190313115850":"2019-03-13T12:02:52.525Z","1.3.0-master.20190314232345":"2019-03-14T23:29:32.344Z","1.3.0":"2019-03-14T23:39:15.434Z","1.3.1-master.20190320072151":"2019-03-20T07:24:39.159Z","1.3.1-master.20190320085838":"2019-03-20T09:01:59.091Z","1.3.1-nortissej.instructions-after-create-sdm.20190325210343":"2019-03-25T21:07:46.467Z","1.3.1-master.20190326042612":"2019-03-26T04:29:12.913Z","1.3.1-master.20190328163658":"2019-03-28T16:40:47.964Z","1.3.1-date-format-utc-1.20190329132023":"2019-03-29T13:23:54.880Z","1.3.1-date-format-utc-1.20190329132633":"2019-03-29T13:29:50.703Z","1.3.1-master.20190329134249":"2019-03-29T13:46:32.029Z","1.3.1":"2019-03-29T15:26:46.360Z","1.3.2-project-aware.20190401130953":"2019-04-01T13:12:57.560Z","1.3.2-master.20190401141922":"2019-04-01T14:22:54.117Z","1.4.0-master.20190401142233":"2019-04-01T14:41:26.101Z","1.4.0":"2019-04-01T14:59:05.517Z","1.4.1-master.20190401161541":"2019-04-01T16:18:38.779Z","1.4.1":"2019-04-01T19:17:57.857Z","1.4.2-master.20190404124146":"2019-04-04T12:44:27.337Z","1.4.2-cleanup-push-impact-invocation.20190405085137":"2019-04-05T08:54:35.583Z","1.4.2-master.20190405122526":"2019-04-05T12:28:44.734Z","1.4.2-master.20190409221444":"2019-04-09T22:17:58.492Z","1.4.2-master.20190409224151":"2019-04-09T22:45:15.913Z","1.5.0-master.20190409224437":"2019-04-09T22:48:27.548Z","1.5.0-master.20190409224507":"2019-04-09T22:48:58.700Z","1.5.0-master.20190411065419":"2019-04-11T06:57:13.133Z","1.5.0-master.20190411070302":"2019-04-11T07:05:36.556Z","1.5.0-master.20190411091959":"2019-04-11T09:23:30.196Z","1.5.0-master.20190412095048":"2019-04-12T09:54:23.130Z","1.5.0-master.20190412214956":"2019-04-12T21:53:39.213Z","1.5.0-master.20190413072358":"2019-04-13T07:26:58.082Z","1.5.0-master.20190413092902":"2019-04-13T09:32:12.571Z","1.5.0-enclosing-rule.20190414091448":"2019-04-14T09:17:47.322Z","1.5.0-master.20190414092108":"2019-04-14T09:24:09.748Z","1.5.0-master.20190414092206":"2019-04-14T09:25:02.617Z","1.5.0-master.20190414160706":"2019-04-14T16:10:00.102Z","1.5.0-master.20190416091757":"2019-04-16T09:20:52.822Z","1.5.0-master.20190416173751":"2019-04-16T17:41:07.455Z","1.5.0-master.20190416173936":"2019-04-16T17:42:26.684Z","1.5.0-master.20190417073800":"2019-04-17T07:40:46.807Z","1.5.0-issue-687.20190418153138":"2019-04-18T15:35:46.419Z","1.5.0-issue-687.20190418175340":"2019-04-18T17:56:30.938Z","1.5.0-issue-687.20190418205513":"2019-04-18T20:58:17.111Z","1.5.0-issue-687.20190419091130":"2019-04-19T09:15:11.313Z","1.5.0-issue-687.20190419091627":"2019-04-19T09:20:10.086Z","1.5.0-issue-687.20190419092434":"2019-04-19T09:28:13.130Z","1.5.0-issue-687.20190419093456":"2019-04-19T09:38:14.917Z","1.5.0-master.20190419141554":"2019-04-19T14:19:34.430Z","1.5.0-master.20190427145304":"2019-04-27T14:56:03.158Z","1.5.0-master.20190427145645":"2019-04-27T14:59:45.356Z","1.5.0-master.20190429154414":"2019-04-29T15:47:57.965Z","1.5.0-master.20190429185458":"2019-04-29T19:15:45.667Z","1.5.0-master.20190429191706":"2019-04-29T19:19:48.284Z","1.5.0-master.20190429215139":"2019-04-29T21:54:54.942Z","1.5.0-remove-deployer.20190430085219":"2019-04-30T08:55:48.202Z","1.5.0-master.20190430120612":"2019-04-30T12:09:31.734Z","1.5.0-thread-ts-promptFor.20190430120507":"2019-04-30T12:09:53.343Z","1.5.0-master.20190503092343":"2019-05-03T09:27:53.772Z","1.5.0-master.20190503092504":"2019-05-03T09:28:30.446Z","1.5.0-master.20190506192615":"2019-05-06T19:29:52.644Z","1.5.0-master.20190508003011":"2019-05-08T00:33:03.307Z","1.5.0-master.20190508172907":"2019-05-08T17:33:36.133Z","1.5.0-master.20190511041707":"2019-05-11T04:20:36.765Z","1.5.0-master.20190511145424":"2019-05-11T14:57:41.293Z","1.5.0-master.20190511161506":"2019-05-11T16:17:48.028Z","1.5.0-mono.20190514194057":"2019-05-14T19:44:24.674Z","1.5.0-master.20190515131109":"2019-05-15T13:14:51.515Z","1.5.0-master.20190515140745":"2019-05-15T14:11:14.535Z","1.5.0-master.20190515212527":"2019-05-15T21:29:55.497Z","1.5.0-master.20190515212733":"2019-05-15T21:31:07.008Z","1.5.0-master.20190516102822":"2019-05-16T10:31:25.274Z","1.5.0-master.20190516103533":"2019-05-16T10:38:23.682Z","1.5.0-master.20190517174921":"2019-05-17T17:51:58.113Z","1.5.0-more-progresslog.20190520124626":"2019-05-20T12:49:31.635Z","1.5.0-issue-728.20190520133606":"2019-05-20T13:41:04.334Z","1.5.0-deprecate-deployer.20190520141744":"2019-05-20T14:21:04.655Z","1.5.0-issue-728.20190520143009":"2019-05-20T14:33:36.301Z","1.5.0-master.20190520145854":"2019-05-20T15:02:09.224Z","1.5.0-master.20190520150051":"2019-05-20T15:04:00.306Z","1.5.0-master.20190520150716":"2019-05-20T15:10:53.207Z","1.5.0-more-progresslog.20190520150914":"2019-05-20T15:12:33.583Z","1.5.0-master.20190520180214":"2019-05-20T18:05:41.267Z","1.5.0-master.20190520191309":"2019-05-20T19:16:06.978Z","1.5.0-master.20190520195042":"2019-05-20T19:53:37.996Z","1.5.0-more-progresslog.20190521095429":"2019-05-21T09:58:51.657Z","1.5.0-master.20190521095948":"2019-05-21T10:04:03.393Z","1.5.0-master.20190521100119":"2019-05-21T10:05:17.801Z","1.5.0-thread-ts-promptFor.20190522124319":"2019-05-22T12:46:19.301Z","1.5.0-thread-ts-promptFor.20190522124937":"2019-05-22T12:52:37.377Z","1.5.0-thread-ts-promptFor.20190523202129":"2019-05-23T20:24:26.964Z","1.5.0-master.20190524065739":"2019-05-24T07:00:55.390Z","1.5.0-thread-ts-promptFor.20190524082655":"2019-05-24T08:31:07.898Z","1.5.0-thread-ts-promptFor.20190524123617":"2019-05-24T12:39:21.506Z","1.5.0-master.20190524125535":"2019-05-24T13:00:28.045Z","1.5.0-master.20190524125746":"2019-05-24T13:01:13.640Z","1.5.0-master.20190525114317":"2019-05-25T11:46:14.245Z","1.5.0-master.20190525114357":"2019-05-25T11:46:42.523Z","1.5.0-master.20190525161944":"2019-05-25T16:22:42.866Z","1.5.0-master.20190526082545":"2019-05-26T08:28:28.129Z","1.5.0-master.20190527173300":"2019-05-27T17:35:41.305Z","1.5.0-master.20190527203034":"2019-05-27T20:34:37.140Z","1.5.0-master.20190527210711":"2019-05-27T21:10:10.747Z","1.5.0":"2019-05-27T21:11:19.727Z","1.5.1-master.20190528101111":"2019-05-28T10:14:38.577Z","1.5.1-master.20190528154220":"2019-05-28T15:46:03.281Z","1.5.1-master.20190528195240":"2019-05-28T19:56:02.442Z","1.5.1-master.20190529121551":"2019-05-29T12:18:56.610Z","1.5.1-master.20190529203520":"2019-05-29T20:39:50.442Z","1.5.1-master.20190529213155":"2019-05-29T21:36:03.622Z","1.5.1-master.20190530113228":"2019-05-30T11:35:05.261Z","1.5.1-master.20190530205831":"2019-05-30T21:04:06.887Z","1.5.1-atomist-update-latest-20190530224936.20190531064640":"2019-05-31T06:49:46.768Z","1.5.1-master.20190531065738":"2019-05-31T07:00:50.236Z","1.5.1-master.20190531070736":"2019-05-31T07:11:38.031Z","1.5.1-project-contributions.20190531194459":"2019-05-31T19:48:10.689Z","1.5.1-master.20190601211624":"2019-06-01T21:20:57.239Z","1.5.1-master.20190604145220":"2019-06-04T14:55:35.574Z","1.5.1":"2019-06-04T15:00:51.244Z","1.5.2-master.20190606192339":"2019-06-06T19:26:59.327Z","1.5.2-master.20190611171316":"2019-06-11T17:17:06.006Z","1.5.2-master.20190611205206":"2019-06-11T20:55:09.202Z","1.5.2-master.20190612101932":"2019-06-12T10:24:08.563Z","1.5.2-master.20190612102726":"2019-06-12T10:30:38.095Z","1.5.2-master.20190612110323":"2019-06-12T11:06:10.953Z","1.5.2-master.20190612123215":"2019-06-12T12:37:39.520Z","1.5.2-nortissej.definiton.20190616125658":"2019-06-16T13:00:41.411Z","1.5.2-nortissej.autofix-location.20190616160706":"2019-06-16T16:11:25.347Z","1.5.2-nortissej.autofix-location.20190616161721":"2019-06-16T16:20:21.614Z","1.5.2-nortissej.autofix-location.20190616162212":"2019-06-16T16:25:26.027Z","1.5.2-master.20190616203149":"2019-06-16T20:35:33.024Z","1.5.2-nortissej.definiton.20190616203428":"2019-06-16T20:37:38.999Z","1.5.2-master.20190616203852":"2019-06-16T20:42:01.446Z","1.5.2-master.20190621121418":"2019-06-21T12:16:58.566Z","1.5.2-master.20190621132332":"2019-06-21T13:27:33.236Z","1.5.2-master.20190626145926":"2019-06-26T15:03:24.721Z","1.5.2-to-job.20190628100243":"2019-06-28T10:06:13.329Z","1.5.2-to-job.20190628112616":"2019-06-28T11:30:53.458Z","1.5.2-master.20190702112950":"2019-07-02T11:32:42.519Z","1.6.0-master.20190702113604":"2019-07-02T11:41:27.356Z","1.6.0-master.20190702114952":"2019-07-02T11:53:26.334Z","1.6.0-logging.20190703173010":"2019-07-03T17:33:23.844Z","1.6.0-logging.20190703181203":"2019-07-03T18:17:43.665Z","1.6.0-master.20190705154040":"2019-07-05T15:44:35.911Z","1.6.0-master.20190705162535":"2019-07-05T16:29:12.573Z","1.6.0-master.20190705164219":"2019-07-05T16:45:17.438Z","1.6.0-logging.20190706134756":"2019-07-06T13:51:34.761Z","1.6.0-master.20190706145121":"2019-07-06T14:54:44.198Z","1.6.0-master.20190709001900":"2019-07-09T00:23:43.231Z","1.6.0":"2019-07-09T01:18:31.752Z","1.6.1-master.20190709092809":"2019-07-09T09:31:00.516Z","1.6.1-master.20190709141445":"2019-07-09T14:18:33.458Z","1.6.1-master.20190711072944":"2019-07-11T07:34:48.979Z","1.6.1-master.20190711073255":"2019-07-11T07:36:31.394Z","1.6.1":"2019-07-11T07:42:36.625Z","1.6.2-master.20190731211703":"2019-07-31T21:21:38.448Z","1.6.2-master.20190731211828":"2019-07-31T21:22:57.415Z","1.6.2-master.20190803123555":"2019-08-03T12:40:24.023Z","1.6.2-no-parameters-create-job.20190813203351":"2019-08-13T20:39:29.357Z","1.6.2-no-parameters-create-job.20190813204003":"2019-08-13T20:46:34.821Z","1.6.2-no-parameters-create-job.20190813205053":"2019-08-13T20:55:46.662Z","1.6.2-master.20190813210448":"2019-08-13T21:09:54.866Z","1.6.2-master.20190820164637":"2019-08-20T16:50:34.240Z","1.6.2-master.20190821114832":"2019-08-21T11:53:46.314Z","1.6.2-master.20190821200131":"2019-08-21T20:05:44.919Z","1.6.2-master.20190821200521":"2019-08-21T20:10:06.055Z","1.6.2-master.20190821222758":"2019-08-21T22:32:53.939Z","1.6.2-master.20190824124708":"2019-08-24T12:51:22.572Z","1.6.2-master.20190827165730":"2019-08-27T17:02:48.590Z","1.6.2-master.20190827180600":"2019-08-27T18:10:46.891Z","1.6.2-master.20190827182350":"2019-08-27T18:28:04.314Z","1.6.2-master.20190827210741":"2019-08-27T21:11:42.979Z","1.6.2-master.20190827224115":"2019-08-27T22:45:17.443Z","1.6.2-atomist.typescript-version.master.20190828144728":"2019-08-28T14:52:31.028Z","1.6.2-master.20190828144646":"2019-08-28T14:53:02.678Z","1.6.2-master.20190828145741":"2019-08-28T15:02:43.141Z","1.6.2-master.20190831065543":"2019-08-31T07:01:02.021Z","1.6.2-atomist.t29e48p34.typescript-version.master.20190831065703":"2019-08-31T07:03:07.611Z","1.6.2-master.20190831070552":"2019-08-31T07:11:07.462Z","1.6.2-master.20190831070813":"2019-08-31T07:14:14.139Z","1.6.2-graphql-code-generator-1.20190902160514":"2019-09-02T16:10:06.078Z","1.6.2-graphql-code-generator-1.20190903172744":"2019-09-03T17:32:38.154Z","1.6.2-master.20190903184405":"2019-09-03T18:50:05.929Z","1.6.2-master.20190903184656":"2019-09-03T18:52:04.679Z","1.6.2-master.20190905161334":"2019-09-05T16:18:29.816Z","1.7.0-master.20190906124024":"2019-09-06T12:47:56.055Z","1.7.0-master.20190906125625":"2019-09-06T13:00:12.201Z","1.7.0-master.20190909163442":"2019-09-09T16:39:54.459Z","1.7.0":"2019-09-09T18:01:10.872Z","1.7.1-autofix-log.20190912175507":"2019-09-12T18:01:15.144Z","1.7.1-autofix-log.20190912180333":"2019-09-12T18:10:34.827Z","1.7.1-master.20190912221843":"2019-09-12T22:24:37.493Z","1.7.1-less-verbose.20190915011836":"2019-09-15T01:22:54.807Z","1.7.1-atomist.a260p86nh.typescript-version.master.20190915212245":"2019-09-15T21:27:22.234Z","1.7.1-master.20190915225602":"2019-09-15T23:00:34.505Z","1.7.1-master.20190920014925":"2019-09-20T01:54:08.350Z","1.7.1-less-verbose.20190922200723":"2019-09-22T20:10:15.607Z","1.7.1-less-verbose.20190922214812":"2019-09-22T21:51:05.890Z","1.7.1-less-verbose.20190922220434":"2019-09-22T22:07:18.605Z","1.7.1-less-verbose.20190922223307":"2019-09-22T22:36:42.430Z","1.7.1-master.20190923001857":"2019-09-23T00:22:40.872Z","1.7.1-master.20190923002339":"2019-09-23T00:26:22.485Z","1.7.1-master.20190923161521":"2019-09-23T16:18:19.717Z","1.8.0-master.20190924042736":"2019-09-24T04:30:55.001Z","1.8.0-master.20190926164141":"2019-09-26T16:45:06.143Z","1.8.0-master.20191007123745":"2019-10-07T12:40:57.888Z","1.8.0-master.20191007123910":"2019-10-07T12:41:53.696Z","1.8.0-globby.20191008212622":"2019-10-08T21:28:47.655Z","1.8.0-master.20191009103804":"2019-10-09T10:40:53.618Z","1.8.0-dynamic-goals.20191011070241":"2019-10-11T07:05:13.837Z","1.8.0-dynamic-goals.20191011075330":"2019-10-11T08:51:26.439Z","1.8.0-master.20191011091430":"2019-10-11T09:16:51.344Z","1.8.0-master.20191011091613":"2019-10-11T09:18:30.298Z","1.8.0-master.20191011095543":"2019-10-11T09:58:07.980Z","1.8.0-master.20191011095607":"2019-10-11T09:58:53.995Z","1.8.0-dynamic-goals.20191011125733":"2019-10-11T13:00:12.250Z","1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130407":"2019-10-11T13:06:38.155Z","1.8.0-atomist.t29e48p34.npm-project-deps.master.20191011130502":"2019-10-11T13:07:26.764Z","1.8.0-master.20191011130951":"2019-10-11T13:12:11.446Z","1.8.0-dynamic-goals.20191011160139":"2019-10-11T16:04:25.419Z","1.8.0-dynamic-goals.20191011185910":"2019-10-11T19:03:41.447Z","1.8.0-dynamic-goals.20191012135711":"2019-10-12T13:59:35.953Z","1.8.0-dynamic-goals.20191012160056":"2019-10-12T16:03:23.377Z","1.8.0-dynamic-goals.20191012171147":"2019-10-12T17:14:10.785Z","1.8.0-dynamic-goals.20191012191805":"2019-10-12T19:20:32.066Z","1.8.0-dynamic-goals.20191012194127":"2019-10-12T19:43:53.327Z","1.8.0-dynamic-goals.20191013133647":"2019-10-13T13:39:07.000Z","1.8.0-dynamic-goals.20191016170727":"2019-10-16T17:10:05.683Z","1.8.0-dynamic-goals.20191016171656":"2019-10-16T17:19:20.118Z","1.8.0-dynamic-goals.20191017203928":"2019-10-17T20:42:49.640Z","1.8.0-test-pr.20191018001935":"2019-10-18T00:22:28.404Z","1.8.0-dynamic-goals.20191018002540":"2019-10-18T00:28:22.429Z","1.8.0-dynamic-goals.20191022214309":"2019-10-22T21:46:54.837Z","1.8.0-dynamic-goals.20191022221055":"2019-10-22T22:13:55.219Z","1.8.0-dynamic-goals.20191022230656":"2019-10-22T23:18:05.760Z","1.8.0-dynamic-goals.20191023001720":"2019-10-23T00:21:20.832Z","1.8.0-dynamic-goals.20191023164640":"2019-10-23T16:50:38.330Z","1.8.0-dynamic-goals.20191023194056":"2019-10-23T19:45:44.254Z","1.8.0-dynamic-goals.20191023194052":"2019-10-23T19:46:19.649Z","1.8.0-dynamic-goals.20191023214836":"2019-10-23T22:08:19.246Z","1.8.0-execute-goal-result.20191029193519":"2019-10-29T19:38:00.662Z","1.8.0-master.20191101163456":"2019-11-01T16:37:46.247Z","1.8.0-master.20191102181820":"2019-11-02T18:20:24.039Z","1.8.0-master.20191102193548":"2019-11-02T19:37:50.518Z","1.8.0-master.20191102194220":"2019-11-02T19:44:23.772Z","1.8.0-dynamic-goals.20191103135949":"2019-11-03T14:02:49.061Z","1.8.0-dynamic-goals.20191103140112":"2019-11-03T14:03:45.395Z","1.8.0-dynamic-goals.20191103141020":"2019-11-03T14:13:01.612Z","1.8.0-dynamic-goals.20191103163255":"2019-11-03T16:35:33.586Z","1.8.0-dynamic-goals.20191103163724":"2019-11-03T16:39:39.203Z","1.8.0-dynamic-goals.20191103175627":"2019-11-03T17:59:13.598Z","1.8.0-dynamic-goals.20191103180327":"2019-11-03T18:05:51.817Z","1.8.0-dynamic-goals.20191103181155":"2019-11-03T18:14:28.527Z","1.8.0-dynamic-goals.20191106115038":"2019-11-06T11:53:00.407Z","1.8.0-dynamic-goals.20191106130028":"2019-11-06T13:02:56.843Z","1.8.0-dynamic-goals.20191106170334":"2019-11-06T17:06:45.024Z","1.8.0-dynamic-goals.20191106181220":"2019-11-06T18:16:55.826Z","1.8.0-dynamic-goals.20191107230956":"2019-11-07T23:14:39.275Z","1.8.0-dynamic-goals.20191108140247":"2019-11-08T14:06:49.221Z","1.8.0-dynamic-goals.20191109122945":"2019-11-09T12:32:06.373Z","1.8.0-master.20191111173823":"2019-11-11T17:42:20.989Z","1.8.0-master.20191111174038":"2019-11-11T17:44:14.503Z","1.8.0-dynamic-goals.20191113080433":"2019-11-13T08:07:14.727Z","1.8.0-dynamic-goals.20191113081117":"2019-11-13T08:13:23.030Z","1.8.0-remove-deployer.20191113154207":"2019-11-13T15:45:12.477Z","1.8.0-dynamic-goals.20191113201158":"2019-11-13T20:15:56.482Z","1.8.0-dynamic-goals.20191113203602":"2019-11-13T20:40:25.349Z","1.8.0-dynamic-goals.20191115141431":"2019-11-15T14:17:23.133Z","1.8.0-dynamic-goals.20191115143623":"2019-11-15T14:38:43.393Z","1.8.0-dynamic-goals.20191115155618":"2019-11-15T15:58:27.381Z","1.8.0-dynamic-goals.20191115162021":"2019-11-15T16:22:30.877Z","1.8.0-dynamic-goals.20191118084837":"2019-11-18T08:51:07.790Z","1.8.0-dynamic-goals.20191118103208":"2019-11-18T10:34:18.052Z","1.8.0-dynamic-goals.20191118104426":"2019-11-18T10:47:09.887Z","1.8.0-dynamic-goals.20191118110012":"2019-11-18T11:02:55.240Z","1.8.0-dynamic-goals.20191118110845":"2019-11-18T11:11:32.404Z","1.8.0-dynamic-goals.20191120223639":"2019-11-20T22:40:19.478Z","1.8.0-dynamic-goals.20191127164826":"2019-11-27T16:50:52.326Z","1.8.0-dynamic-goals.20191127165233":"2019-11-27T16:55:17.029Z","1.8.0-dynamic-goals.20191129124919":"2019-11-29T12:51:53.695Z","1.8.0-dynamic-goals.20191129130631":"2019-11-29T13:09:02.964Z","1.8.0-dynamic-goals.20191129181045":"2019-11-29T18:13:13.363Z","1.8.0-dynamic-goals.20191129181414":"2019-11-29T18:16:35.750Z","1.8.0-dynamic-goals.20191202125053":"2019-12-02T12:53:20.126Z","1.8.0-dynamic-goals.20191202135940":"2019-12-02T14:02:13.448Z","1.8.0-dynamic-goals.20191202150525":"2019-12-02T15:08:03.916Z","1.8.0-dynamic-goals.20191202152057":"2019-12-02T15:23:27.525Z","1.8.0-dynamic-goals.20191202154744":"2019-12-02T15:50:20.946Z","1.8.0-dynamic-goals.20191202172611":"2019-12-02T17:30:44.290Z","1.8.0-dynamic-goals.20191202175738":"2019-12-02T18:02:00.022Z","1.8.0-master.20191206133846":"2019-12-06T13:41:10.313Z","1.8.0-master.20191206134419":"2019-12-06T13:46:51.007Z","1.8.0":"2019-12-06T13:49:07.304Z","1.8.1-dynamic-goals.20191206144201":"2019-12-06T14:44:22.014Z","1.8.1-master.20191206150641":"2019-12-06T15:09:37.765Z","2.0.0-master.20191206153457":"2019-12-06T15:37:21.729Z","2.0.0-master.20191207100157":"2019-12-07T10:04:25.348Z","2.0.0-master.20191208190204":"2019-12-08T19:04:32.447Z","2.0.0-master.20191209161414":"2019-12-09T16:17:36.001Z","2.0.0-master.20191210185902":"2019-12-10T19:04:01.305Z","2.0.0-master.20191212173159":"2019-12-12T17:35:35.795Z","2.0.0-sdm-mutations.20191213002536":"2019-12-13T00:28:11.894Z","2.0.0-sdm-mutations.20191213003742":"2019-12-13T00:41:14.580Z","2.0.0-sdm-mutations.20191213110738":"2019-12-13T11:10:19.099Z","2.0.0-sdm-mutations.20191213111915":"2019-12-13T11:21:28.231Z","2.0.0-sdm-mutations.20191213133649":"2019-12-13T13:39:40.981Z","2.0.0-sdm-mutations.20191213143057":"2019-12-13T14:33:23.908Z","2.0.0-sdm-mutations.20191213145220":"2019-12-13T14:54:43.428Z","2.0.0-sdm-mutations.20191213211016":"2019-12-13T21:13:05.994Z","2.0.0-master.20191213213803":"2019-12-13T21:41:34.965Z","2.0.0-master.20191213214023":"2019-12-13T21:43:31.376Z","2.0.0-simplify-k8s-scheduler.20191217085758":"2019-12-17T09:00:20.826Z","2.0.0-ipcrm-793.20191223231011":"2019-12-23T23:12:19.295Z","2.0.0-ipcrm-793.20191226133958":"2019-12-26T13:42:13.863Z","2.0.0-master.20191226140700":"2019-12-26T14:09:21.505Z","2.0.0-master.20191230163626":"2019-12-30T16:38:31.917Z","2.0.0-remove-index.20191230181344":"2019-12-30T18:15:53.950Z","2.0.0-remove-index.20200103222253":"2020-01-03T22:25:12.165Z","2.0.0-remove-index.20200105145023":"2020-01-05T14:52:30.193Z","2.0.0-remove-index.20200105193510":"2020-01-05T19:37:17.832Z","2.0.0-remove-index.20200107085713":"2020-01-07T08:59:16.623Z","2.0.0-master.20200107202413":"2020-01-07T20:27:00.600Z","2.0.0-remove-index.20200112171637":"2020-01-12T17:18:53.308Z","2.0.0-remove-index.20200113115436":"2020-01-13T11:56:53.089Z","2.0.0-remove-index.20200114085202":"2020-01-14T08:54:15.404Z","2.0.0-remove-index.20200115132157":"2020-01-15T13:24:21.225Z","2.0.0-master.20200115150346":"2020-01-15T15:06:13.965Z","2.0.0-remove-index.20200115150505":"2020-01-15T15:07:53.230Z","2.0.0-remove-index.20200120213952":"2020-01-20T21:42:04.070Z","2.0.0-remove-index.20200120225927":"2020-01-20T23:01:39.996Z","2.0.0-master.20200131202557":"2020-01-31T20:28:06.954Z","2.0.0-master.20200131203114":"2020-01-31T20:33:21.000Z","2.0.0-master.20200201115609":"2020-02-01T11:58:29.345Z","2.0.0-master.20200201162131":"2020-02-01T16:23:33.155Z","2.0.0-master.20200204153359":"2020-02-04T15:36:25.316Z","2.0.0-skill-output.20200206145221":"2020-02-06T14:54:49.145Z","2.0.0-skill-output.20200206153703":"2020-02-06T15:39:16.643Z","2.0.0-skill-output.20200208122908":"2020-02-08T12:31:26.356Z","2.0.0-skill-output.20200208174656":"2020-02-08T17:49:36.529Z","2.0.0-master.20200213184450":"2020-02-13T18:47:38.686Z","2.0.0-skill-output.20200214121905":"2020-02-14T12:21:18.747Z","2.0.0-skill-output.20200214123548":"2020-02-14T12:38:14.170Z","2.0.0-master.20200214123953":"2020-02-14T12:42:17.669Z","2.0.0-master.20200222012253":"2020-02-22T01:25:32.795Z","2.0.0-add-core.20200224203654":"2020-02-24T20:41:56.905Z","2.0.0-add-core.20200224205437":"2020-02-24T20:59:33.729Z","2.0.0-master.20200225134700":"2020-02-25T13:51:59.688Z","2.0.0-master.20200225134833":"2020-02-25T13:53:17.763Z","2.0.0-master.20200225142134":"2020-02-25T14:26:29.427Z","2.0.0-master.20200225144423":"2020-02-25T14:48:55.901Z","2.0.0-k8s-cleanup.20200226094648":"2020-02-26T09:51:12.999Z","2.0.0-k8s-cleanup.20200226133342":"2020-02-26T13:38:05.020Z","2.0.0-k8s-cleanup.20200226192147":"2020-02-26T19:26:36.403Z","2.0.0-k8s-cleanup.20200226201257":"2020-02-26T20:17:30.186Z","2.0.0-master.20200226210530":"2020-02-26T21:10:20.518Z","2.0.0-master.20200227091110":"2020-02-27T09:16:29.364Z","2.0.0-master.20200228152343":"2020-02-28T15:28:29.088Z","2.0.0-k8s-container-job-816.20200228212446":"2020-02-28T21:29:39.803Z","1.9.0-1.9-fixes.20200303005416":"2020-03-03T00:56:36.733Z","2.0.0-master.20200303103640":"2020-03-03T10:41:58.701Z","1.9.0-1.9-fixes.20200303141649":"2020-03-03T14:19:28.186Z","1.9.0":"2020-03-03T14:33:10.433Z","1.9.1-1.9-fixes.20200303143711":"2020-03-03T14:39:45.681Z","2.0.0-k8s-patch-strategy-819.20200304133717":"2020-03-04T13:41:26.024Z","2.0.0-master.20200306162936":"2020-03-06T16:35:27.251Z","1.9.1-1.9-fixes.20200310183143":"2020-03-10T18:34:06.214Z","2.0.0-side-effect-defaults.20200310184749":"2020-03-10T18:51:58.247Z","2.0.0-master.20200310192307":"2020-03-10T19:28:03.279Z","2.0.0-master.20200311001438":"2020-03-11T00:18:51.134Z","1.9.1-1.9-fixes.20200311125136":"2020-03-11T12:54:02.118Z","1.9.1-1.9-fixes.20200311134607":"2020-03-11T13:48:32.380Z","2.0.0-master.20200311172920":"2020-03-11T17:32:09.221Z","2.0.0-sync-repo-github-app.20200312133402":"2020-03-12T13:36:21.103Z","2.0.0-sync-repo-github-app.20200312140329":"2020-03-12T14:05:47.381Z","2.0.0-master.20200312140727":"2020-03-12T14:09:54.814Z","1.9.1-1.9-fixes.20200318141711":"2020-03-18T14:19:33.440Z","2.0.0-master.20200323171527":"2020-03-23T17:17:51.106Z","2.0.0-master.20200324201747":"2020-03-24T20:22:34.368Z","2.0.0-k8s-sync-repo-creds-828.20200324204549":"2020-03-24T20:50:48.417Z","2.0.0-master.20200324211658":"2020-03-24T21:21:19.112Z","2.0.0-k8s-sync-repo-creds-worker-828.20200325010940":"2020-03-25T01:14:14.573Z","2.0.0-master.20200325060840":"2020-03-25T06:13:11.921Z","2.0.0-master.20200325124454":"2020-03-25T12:49:25.541Z","2.0.0-master.20200325133634":"2020-03-25T13:41:39.968Z","2.0.0-master.20200325193634":"2020-03-25T19:41:30.052Z","2.0.0-ipcrm-config-change.20200327211110":"2020-03-27T21:16:16.715Z","2.0.0-master.20200330164246":"2020-03-30T16:48:02.359Z","2.0.0-master.20200416151221":"2020-04-16T15:16:51.699Z","2.0.0-master.20200416151527":"2020-04-16T15:19:56.874Z","2.0.0-master.20200513064903":"2020-05-13T06:53:09.929Z","2.0.0-master.20200513070623":"2020-05-13T07:10:32.129Z","2.0.0-master.20200518142233":"2020-05-18T14:27:08.384Z","2.0.0-master.20200609095842":"2020-06-09T10:02:58.276Z","2.0.0-master.20200609110411":"2020-06-09T11:08:34.152Z","2.0.0-master.20200618005140":"2020-06-18T00:57:29.499Z","2.0.0-master.20200618005455":"2020-06-18T00:59:36.690Z","2.0.0-master.20200622120411":"2020-06-22T12:08:47.197Z","2.0.0-master.20200622151907":"2020-06-22T15:23:52.361Z","2.0.0-client-core-index.20200622162901":"2020-06-22T16:33:35.148Z","2.0.0-client-core-index.20200623155820":"2020-06-23T16:03:09.651Z","2.0.0-client-core-index.20200623161140":"2020-06-23T16:16:26.065Z","2.0.0-master.20200624115947":"2020-06-24T12:04:33.285Z","2.0.0-client-core-index.20200624120353":"2020-06-24T12:08:44.340Z","2.0.0-client-core-index.20200624191441":"2020-06-24T19:17:29.528Z","2.0.0-client-core-index.20200625184448":"2020-06-25T18:49:23.759Z","2.0.0-master.20200630122714":"2020-06-30T12:33:35.916Z","2.0.0-master.20200630123015":"2020-06-30T12:35:54.886Z","2.0.0-remove-deprecations.20200630212333":"2020-06-30T21:28:17.821Z","2.0.0-remove-deprecations.20200701142824":"2020-07-01T14:33:41.114Z","2.0.0-master.20200703143732":"2020-07-03T14:42:51.779Z","2.0.0-master.20200703143927":"2020-07-03T14:44:23.132Z","2.0.0-add-node-pack.20200708163326":"2020-07-08T16:38:09.571Z","2.0.0-add-node-pack.20200708210024":"2020-07-08T21:05:57.950Z","2.0.0-add-node-pack.20200709125731":"2020-07-09T13:02:29.611Z","2.0.0-master.20200710134535":"2020-07-10T13:51:15.463Z","2.0.0-master.20200710134728":"2020-07-10T13:52:46.928Z","2.0.0-master.20200710165243":"2020-07-10T16:58:55.273Z","2.0.0-add-jvm-pack.20200710165350":"2020-07-10T16:59:49.751Z","2.0.0-master.20200713192703":"2020-07-13T19:32:04.132Z","2.0.0-add-jvm-pack.20200713193338":"2020-07-13T19:38:54.468Z","2.0.0-master.20200713202241":"2020-07-13T20:27:56.229Z","2.0.0-master.20200713202453":"2020-07-13T20:30:18.513Z","2.0.0-master.20200713214952":"2020-07-13T21:55:32.987Z","2.0.0-master.20200714004626":"2020-07-14T01:12:57.230Z","2.0.0-master.20200714011323":"2020-07-14T01:19:53.828Z","2.0.0-master.20200714021215":"2020-07-14T02:17:57.155Z","2.0.0-add-aws-pack.20200714023912":"2020-07-14T02:45:22.859Z","2.0.0-master.20200715111022":"2020-07-15T11:18:10.948Z","2.0.0-master.20200715111358":"2020-07-15T11:20:42.611Z","2.0.0-ipcrm-update-s3-pack.20200715113557":"2020-07-15T11:42:32.292Z","2.0.0-main.20200720151816":"2020-07-20T15:25:01.010Z","2.0.0-main.20200720152036":"2020-07-20T15:27:18.065Z","2.0.0-main.20200720184558":"2020-07-20T18:52:50.713Z","2.0.0-main.20200720184830":"2020-07-20T18:54:35.691Z","2.0.0-main.20200720194232":"2020-07-20T19:48:26.771Z","2.0.0-main.20200804173404":"2020-08-04T17:39:57.586Z","2.0.0-main.20200817164942":"2020-08-17T16:56:45.839Z","2.0.0-main.20200817165734":"2020-08-17T17:03:34.589Z","1.9.1-update-params-1.9.20200818141602":"2020-08-18T14:23:37.899Z","2.0.0-main.20200820104521":"2020-08-20T10:51:29.019Z","2.0.0-main.20200825161405":"2020-08-25T16:21:23.349Z","2.0.0-main.20200902213654":"2020-09-02T21:44:20.664Z","2.0.0-main.20200903122727":"2020-09-03T12:35:21.157Z","2.0.0-main.20200903175511":"2020-09-03T18:03:00.591Z","2.0.0-main.20200904015632":"2020-09-04T02:04:44.736Z","2.0.0-main.20200904144019":"2020-09-04T14:46:41.513Z","2.0.0":"2020-09-04T15:00:37.084Z","2.0.1-main.20200911130604":"2020-09-11T13:13:03.705Z","1.9.1-update-params-1.9.20200911133602":"2020-09-11T13:38:59.376Z","2.0.1-ipcrm-docker-changes.20201120154543":"2020-11-20T15:53:21.951Z","2.0.1-main.20201120171347":"2020-11-20T17:20:44.634Z","2.0.1-main.20210108042958":"2021-01-08T04:39:27.940Z","2.0.1-remove-skill-output.20210125164846":"2021-01-25T16:55:51.212Z","2.0.1-remove-skill-output.20210125165034":"2021-01-25T16:57:58.020Z","2.0.1-main.20210125170131":"2021-01-25T17:09:03.617Z","2.1.0-main.20210125172757":"2021-01-25T17:35:26.341Z","2.1.0-main.20210125174106":"2021-01-25T17:48:04.126Z","2.1.0":"2021-01-25T18:06:47.906Z","2.1.1-main.20210203223457":"2021-02-03T22:42:31.352Z","2.1.1-atomist.npm-update-main.20210203223639":"2021-02-03T22:44:58.130Z","2.1.1-main.20210203225026":"2021-02-03T22:58:06.484Z","2.1.1-atomist.npm-update-main.20210204150539":"2021-02-04T15:13:13.534Z","2.1.1-main.20210204151835":"2021-02-04T15:26:27.326Z","2.1.1-atomist.npm-update-main.20210205150724":"2021-02-05T15:16:28.984Z","2.1.1-main.20210205151101":"2021-02-05T15:19:34.508Z","2.1.1-atomist.npm-update-main.20210209150624":"2021-02-09T15:15:47.765Z","2.1.1-main.20210209152429":"2021-02-09T15:34:00.041Z","1.9.1-1.9-fixes.20210209224939":"2021-02-09T22:52:40.920Z","2.1.1-atomist.npm-update-main.20210210150604":"2021-02-10T15:14:01.990Z","2.1.1-main.20210210152001":"2021-02-10T15:28:13.887Z","1.9.1-1.9-fixes.20210210162852":"2021-02-10T16:31:44.488Z","1.9.1":"2021-02-10T16:36:04.276Z","1.9.2-1.9-fixes.20210210164117":"2021-02-10T16:44:11.295Z","2.1.1-main.20210210192104":"2021-02-10T19:29:29.345Z","2.1.1-main.20210210192540":"2021-02-10T19:33:22.824Z","2.1.1":"2021-02-10T19:45:37.370Z","2.1.2-main.20210211214218":"2021-02-11T21:52:54.765Z","2.1.2-main.20210212133024":"2021-02-12T13:40:42.411Z","2.1.2-atomist.npm-update-main.20210212133312":"2021-02-12T13:42:40.708Z","2.1.2":"2021-02-12T13:45:49.019Z","2.1.3-atomist.npm-audit-main.20210224150617":"2021-02-24T15:17:50.309Z","2.1.3-atomist.npm-audit-main.20210311130556":"2021-03-11T13:15:14.253Z","2.1.3-atomist.npm-audit-main.20210312130621":"2021-03-12T13:15:33.740Z","2.1.3-main.20210314130540":"2021-03-14T13:15:10.788Z","2.1.3-main.20210316141625":"2021-03-16T14:23:19.825Z","2.1.3":"2021-03-16T23:05:09.782Z","2.1.4-atomist.npm-update-main.20210316230825":"2021-03-16T23:16:05.673Z","2.1.4-atomist.npm-update-main.20210318130516":"2021-03-18T13:12:56.040Z","2.1.4-atomist.npm-update-main.20210319130416":"2021-03-19T13:11:46.415Z","2.1.4-atomist.npm-update-main.20210320130356":"2021-03-20T13:11:33.974Z","2.1.4-atomist.npm-update-main.20210322130348":"2021-03-22T13:11:47.428Z","2.1.4-atomist.npm-update-main.20210323130602":"2021-03-23T13:13:16.634Z","2.1.4-atomist.npm-update-main.20210324130516":"2021-03-24T13:12:42.679Z","2.1.4-atomist.npm-update-main.20210326130643":"2021-03-26T13:14:27.739Z","2.1.4-atomist.npm-update-main.20210327130533":"2021-03-27T13:13:06.943Z","2.1.4-atomist.npm-update-main.20210329130721":"2021-03-29T13:16:46.537Z","2.1.4-atomist.npm-update-main.20210330130415":"2021-03-30T13:12:12.229Z","2.1.4-atomist.npm-update-main.20210331130354":"2021-03-31T13:12:05.087Z","2.1.4-atomist.npm-update-main.20210401121241":"2021-04-01T12:20:04.755Z","2.1.4-main.20210401205512":"2021-04-01T21:02:53.479Z","2.1.4-atomist.npm-update-main.20210401205528":"2021-04-01T21:03:43.472Z","2.1.4-main.20210402085638":"2021-04-02T09:04:10.284Z","2.1.4-atomist.npm-update-main.20210402085721":"2021-04-02T09:04:54.050Z","2.1.4":"2021-04-02T09:05:15.074Z","2.1.5-atomist.npm-update-main.20210402090906":"2021-04-02T09:16:30.679Z","2.1.5-atomist.npm-update-main.20210403120522":"2021-04-03T12:12:42.729Z","2.1.5-atomist.npm-update-main.20210404120531":"2021-04-04T12:13:03.323Z","2.1.5-atomist.npm-update-main.20210406120659":"2021-04-06T12:14:45.509Z","2.1.5-atomist.npm-update-main.20210407120703":"2021-04-07T12:14:39.380Z","2.1.5-atomist.npm-update-main.20210408120517":"2021-04-08T12:12:56.113Z","2.1.5-atomist.npm-update-main.20210409120526":"2021-04-09T12:12:53.775Z","2.1.5-atomist.npm-update-main.20210410120519":"2021-04-10T12:12:46.594Z","2.1.5-atomist.npm-update-main.20210411120526":"2021-04-11T12:12:27.073Z","2.1.5-atomist.npm-update-main.20210412120854":"2021-04-12T12:16:08.635Z","2.1.5-atomist.npm-update-main.20210413120555":"2021-04-13T12:13:40.690Z","2.1.5-atomist.npm-update-main.20210414120503":"2021-04-14T12:12:19.328Z","2.1.5-atomist.npm-update-main.20210415120553":"2021-04-15T12:14:03.896Z","2.1.5-atomist.npm-update-main.20210416120811":"2021-04-16T12:16:18.509Z","2.1.5-branch-event-signing.0":"2021-05-05T09:01:00.237Z","2.1.5-branch-event-signing.1":"2021-05-05T09:49:00.485Z","2.1.5-branch-event-signing.2":"2021-05-05T09:51:35.594Z","2.1.5-branch-event-signing.3":"2021-05-05T10:13:29.487Z","2.1.5-main.0":"2021-05-07T12:12:00.259Z","2.1.5-branch-atomist-npm-audit-main.0":"2021-05-07T12:12:36.056Z","2.1.5-branch-atomist-npm-audit-main.1":"2021-05-08T12:13:49.346Z","2.1.5-main.1":"2021-05-08T12:15:24.188Z","2.1.5-branch-event-signing.4":"2021-05-10T07:41:43.320Z","2.2.0-branch-event-signing.0":"2021-05-10T08:26:22.100Z","2.2.0-main.0":"2021-05-10T08:40:21.680Z","2.2.0-branch-atomist-npm-audit-main.0":"2021-05-10T08:42:47.185Z","2.2.0-main.1":"2021-05-10T08:43:48.139Z","2.2.0-branch-atomist-npm-audit-main.1":"2021-06-02T10:10:41.310Z","2.2.0-main.2":"2021-06-02T20:28:00.833Z","2.2.0-main.3":"2021-06-02T20:29:24.353Z","2.1.5-main.2":"2021-06-02T20:37:34.757Z","2.1.5-branch-atomist-npm-audit-main.2":"2021-06-02T20:38:50.939Z","2.1.5-main.3":"2021-06-02T20:39:56.752Z","2.1.5-main.4":"2021-06-02T20:41:28.675Z","2.1.5":"2021-06-03T09:59:45.334Z","2.1.6-main.0":"2021-06-03T10:08:15.929Z","2.1.6-main.1":"2021-06-03T10:10:23.703Z","2.1.6-main.2":"2021-06-03T19:57:51.312Z","2.1.6-main.3":"2021-06-03T19:59:17.228Z","2.1.6-branch-atomist-npm-update-main.0":"2021-06-03T20:00:28.387Z","2.1.6":"2021-06-03T20:01:24.113Z","2.1.7-main.0":"2021-06-03T20:09:11.290Z","2.1.7-main.1":"2021-06-03T20:10:29.382Z","2.1.7-branch-atomist-npm-audit-main.0":"2021-06-09T10:11:14.423Z","2.1.7-main.3":"2021-06-09T10:13:08.391Z","2.1.7-branch-atomist-npm-audit-main.1":"2021-06-22T10:10:31.821Z","2.1.7-main.4":"2021-06-22T10:11:39.848Z","2.1.7-main.5":"2021-06-22T10:13:36.394Z","2.1.7-branch-atomist-npm-audit-main.2":"2021-08-04T08:11:08.958Z","2.1.7-main.6":"2021-08-04T08:12:01.597Z","2.1.7-main.7":"2021-08-04T08:13:24.088Z"},"maintainers":[{"email":"neil.prosser+npmjs@gmail.com","name":"neilprosser"},{"email":"npm@atomist.com","name":"atomist-bot"},{"email":"cd@atomist.com","name":"cdupuis"},{"email":"slimslenderslacks@gmail.com","name":"slimslenderslacks"}],"description":"Atomist Software Delivery Machine SDK","homepage":"https://github.com/atomist/sdm#readme","keywords":["atomist","automation","sdm","ci","cd"],"repository":{"type":"git","url":"git+https://github.com/atomist/sdm.git"},"author":{"name":"Atomist","email":"support@atomist.com","url":"https://atomist.com/"},"bugs":{"url":"https://github.com/atomist/sdm/issues"},"license":"Apache-2.0","readme":"","readmeFilename":""}